From c44c8f4e3b73c3fec8e5ef09c236aff2b56a1b92 Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Sat, 21 Sep 2024 21:28:56 +0800 Subject: [PATCH] Introduce minio sdk into project (#1892) ### What problem does this PR solve? Use minio sdk to connect MINIO/S3 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai Co-authored-by: Zhichang Yu --- scripts/Dockerfile_infinity_builder_centos7 | 24 +- .../download_deps_infinity_builder_centos7.sh | 1 + src/CMakeLists.txt | 226 +- src/storage/io/s3_client.cppm | 33 + src/storage/io/s3_client_minio.cpp | 98 + src/storage/io/s3_client_minio.cppm | 36 + src/storage/io/s3_file_system.cpp | 478 + src/storage/io/s3_file_system.cppm | 98 + third_party/CMakeLists.txt | 9 + .../curl/CMake/CMakeConfigurableFile.in | 24 + third_party/curl/CMake/CurlSymbolHiding.cmake | 85 + third_party/curl/CMake/CurlTests.c | 430 + third_party/curl/CMake/FindBearSSL.cmake | 32 + third_party/curl/CMake/FindBrotli.cmake | 43 + third_party/curl/CMake/FindCARES.cmake | 47 + third_party/curl/CMake/FindGSS.cmake | 312 + third_party/curl/CMake/FindLibPSL.cmake | 45 + third_party/curl/CMake/FindLibSSH2.cmake | 45 + third_party/curl/CMake/FindMSH3.cmake | 70 + third_party/curl/CMake/FindMbedTLS.cmake | 36 + third_party/curl/CMake/FindNGHTTP2.cmake | 41 + third_party/curl/CMake/FindNGHTTP3.cmake | 78 + third_party/curl/CMake/FindNGTCP2.cmake | 117 + third_party/curl/CMake/FindQUICHE.cmake | 70 + third_party/curl/CMake/FindWolfSSL.cmake | 60 + third_party/curl/CMake/FindZstd.cmake | 78 + third_party/curl/CMake/Macros.cmake | 80 + third_party/curl/CMake/OtherTests.cmake | 184 + third_party/curl/CMake/PickyWarnings.cmake | 246 + .../curl/CMake/Platforms/WindowsCache.cmake | 192 + third_party/curl/CMake/Utilities.cmake | 35 + .../curl/CMake/cmake_uninstall.cmake.in | 49 + third_party/curl/CMake/curl-config.cmake.in | 44 + third_party/curl/CMakeLists.txt | 2180 +++ third_party/curl/curl-config.in | 195 + third_party/curl/include/Makefile.am | 28 + third_party/curl/include/Makefile.in | 775 + third_party/curl/include/README.md | 20 + third_party/curl/include/curl/Makefile.am | 41 + third_party/curl/include/curl/Makefile.in | 726 + third_party/curl/include/curl/curl.h | 3258 ++++ third_party/curl/include/curl/curlver.h | 79 + third_party/curl/include/curl/easy.h | 125 + third_party/curl/include/curl/header.h | 74 + third_party/curl/include/curl/mprintf.h | 79 + third_party/curl/include/curl/multi.h | 485 + third_party/curl/include/curl/options.h | 70 + third_party/curl/include/curl/stdcheaders.h | 35 + third_party/curl/include/curl/system.h | 496 + third_party/curl/include/curl/typecheck-gcc.h | 718 + third_party/curl/include/curl/urlapi.h | 155 + third_party/curl/include/curl/websockets.h | 84 + third_party/curl/lib/.checksrc | 1 + third_party/curl/lib/CMakeLists.txt | 250 + third_party/curl/lib/Makefile.am | 148 + third_party/curl/lib/Makefile.in | 5524 +++++++ third_party/curl/lib/Makefile.inc | 381 + third_party/curl/lib/Makefile.mk | 334 + third_party/curl/lib/Makefile.soname | 42 + third_party/curl/lib/altsvc.c | 708 + third_party/curl/lib/altsvc.h | 81 + third_party/curl/lib/amigaos.c | 247 + third_party/curl/lib/amigaos.h | 41 + third_party/curl/lib/arpa_telnet.h | 117 + third_party/curl/lib/asyn-ares.c | 958 ++ third_party/curl/lib/asyn-thread.c | 1031 ++ third_party/curl/lib/asyn.h | 184 + third_party/curl/lib/base64.c | 293 + third_party/curl/lib/bufq.c | 677 + third_party/curl/lib/bufq.h | 272 + third_party/curl/lib/bufref.c | 127 + third_party/curl/lib/bufref.h | 48 + third_party/curl/lib/c-hyper.c | 1229 ++ third_party/curl/lib/c-hyper.h | 63 + third_party/curl/lib/cf-h1-proxy.c | 1104 ++ third_party/curl/lib/cf-h1-proxy.h | 39 + third_party/curl/lib/cf-h2-proxy.c | 1636 ++ third_party/curl/lib/cf-h2-proxy.h | 39 + third_party/curl/lib/cf-haproxy.c | 251 + third_party/curl/lib/cf-haproxy.h | 39 + third_party/curl/lib/cf-https-connect.c | 576 + third_party/curl/lib/cf-https-connect.h | 58 + third_party/curl/lib/cf-socket.c | 2143 +++ third_party/curl/lib/cf-socket.h | 176 + third_party/curl/lib/cfilters.c | 966 ++ third_party/curl/lib/cfilters.h | 675 + third_party/curl/lib/config-amigaos.h | 129 + third_party/curl/lib/config-dos.h | 138 + third_party/curl/lib/config-mac.h | 103 + third_party/curl/lib/config-os400.h | 331 + third_party/curl/lib/config-plan9.h | 147 + third_party/curl/lib/config-riscos.h | 274 + third_party/curl/lib/config-win32.h | 512 + third_party/curl/lib/config-win32ce.h | 303 + third_party/curl/lib/conncache.c | 1189 ++ third_party/curl/lib/conncache.h | 164 + third_party/curl/lib/connect.c | 1555 ++ third_party/curl/lib/connect.h | 153 + third_party/curl/lib/content_encoding.c | 1086 ++ third_party/curl/lib/content_encoding.h | 34 + third_party/curl/lib/cookie.c | 1772 +++ third_party/curl/lib/cookie.h | 138 + third_party/curl/lib/curl_addrinfo.c | 600 + third_party/curl/lib/curl_addrinfo.h | 108 + third_party/curl/lib/curl_base64.h | 41 + third_party/curl/lib/curl_config.h.cmake | 819 + third_party/curl/lib/curl_config.h.in | 1007 ++ third_party/curl/lib/curl_ctype.h | 51 + third_party/curl/lib/curl_des.c | 69 + third_party/curl/lib/curl_des.h | 40 + third_party/curl/lib/curl_endian.c | 84 + third_party/curl/lib/curl_endian.h | 36 + third_party/curl/lib/curl_fnmatch.c | 390 + third_party/curl/lib/curl_fnmatch.h | 46 + third_party/curl/lib/curl_get_line.c | 77 + third_party/curl/lib/curl_get_line.h | 32 + third_party/curl/lib/curl_gethostname.c | 102 + third_party/curl/lib/curl_gethostname.h | 33 + third_party/curl/lib/curl_gssapi.c | 152 + third_party/curl/lib/curl_gssapi.h | 63 + third_party/curl/lib/curl_hmac.h | 78 + third_party/curl/lib/curl_krb5.h | 52 + third_party/curl/lib/curl_ldap.h | 36 + third_party/curl/lib/curl_md4.h | 39 + third_party/curl/lib/curl_md5.h | 67 + third_party/curl/lib/curl_memory.h | 178 + third_party/curl/lib/curl_memrchr.c | 64 + third_party/curl/lib/curl_memrchr.h | 44 + third_party/curl/lib/curl_multibyte.c | 162 + third_party/curl/lib/curl_multibyte.h | 92 + third_party/curl/lib/curl_ntlm_core.c | 669 + third_party/curl/lib/curl_ntlm_core.h | 72 + third_party/curl/lib/curl_path.c | 203 + third_party/curl/lib/curl_path.h | 49 + third_party/curl/lib/curl_printf.h | 55 + third_party/curl/lib/curl_range.c | 96 + third_party/curl/lib/curl_range.h | 31 + third_party/curl/lib/curl_rtmp.c | 362 + third_party/curl/lib/curl_rtmp.h | 37 + third_party/curl/lib/curl_sasl.c | 760 + third_party/curl/lib/curl_sasl.h | 165 + third_party/curl/lib/curl_setup.h | 972 ++ third_party/curl/lib/curl_setup_once.h | 414 + third_party/curl/lib/curl_sha256.h | 50 + third_party/curl/lib/curl_sha512_256.c | 850 + third_party/curl/lib/curl_sha512_256.h | 44 + third_party/curl/lib/curl_sspi.c | 239 + third_party/curl/lib/curl_sspi.h | 123 + third_party/curl/lib/curl_threads.c | 159 + third_party/curl/lib/curl_threads.h | 70 + third_party/curl/lib/curl_trc.c | 331 + third_party/curl/lib/curl_trc.h | 200 + third_party/curl/lib/curlx.h | 117 + third_party/curl/lib/cw-out.c | 474 + third_party/curl/lib/cw-out.h | 53 + third_party/curl/lib/dict.c | 321 + third_party/curl/lib/dict.h | 31 + third_party/curl/lib/dllmain.c | 81 + third_party/curl/lib/doh.c | 1436 ++ third_party/curl/lib/doh.h | 168 + third_party/curl/lib/dynbuf.c | 282 + third_party/curl/lib/dynbuf.h | 93 + third_party/curl/lib/dynhds.c | 396 + third_party/curl/lib/dynhds.h | 187 + third_party/curl/lib/easy.c | 1350 ++ third_party/curl/lib/easy_lock.h | 111 + third_party/curl/lib/easygetopt.c | 98 + third_party/curl/lib/easyif.h | 41 + third_party/curl/lib/easyoptions.c | 382 + third_party/curl/lib/easyoptions.h | 37 + third_party/curl/lib/escape.c | 235 + third_party/curl/lib/escape.h | 44 + third_party/curl/lib/file.c | 643 + third_party/curl/lib/file.h | 42 + third_party/curl/lib/fileinfo.c | 46 + third_party/curl/lib/fileinfo.h | 40 + third_party/curl/lib/fopen.c | 158 + third_party/curl/lib/fopen.h | 30 + third_party/curl/lib/formdata.c | 958 ++ third_party/curl/lib/formdata.h | 59 + third_party/curl/lib/ftp.c | 4590 ++++++ third_party/curl/lib/ftp.h | 167 + third_party/curl/lib/ftplistparser.c | 1041 ++ third_party/curl/lib/ftplistparser.h | 77 + third_party/curl/lib/functypes.h | 115 + third_party/curl/lib/getenv.c | 80 + third_party/curl/lib/getinfo.c | 640 + third_party/curl/lib/getinfo.h | 29 + third_party/curl/lib/gopher.c | 244 + third_party/curl/lib/gopher.h | 34 + third_party/curl/lib/hash.c | 385 + third_party/curl/lib/hash.h | 113 + third_party/curl/lib/headers.c | 449 + third_party/curl/lib/headers.h | 62 + third_party/curl/lib/hmac.c | 173 + third_party/curl/lib/hostasyn.c | 123 + third_party/curl/lib/hostip.c | 1481 ++ third_party/curl/lib/hostip.h | 266 + third_party/curl/lib/hostip4.c | 301 + third_party/curl/lib/hostip6.c | 157 + third_party/curl/lib/hostsyn.c | 104 + third_party/curl/lib/hsts.c | 576 + third_party/curl/lib/hsts.h | 69 + third_party/curl/lib/http.c | 4543 ++++++ third_party/curl/lib/http.h | 296 + third_party/curl/lib/http1.c | 346 + third_party/curl/lib/http1.h | 63 + third_party/curl/lib/http2.c | 2919 ++++ third_party/curl/lib/http2.h | 77 + third_party/curl/lib/http_aws_sigv4.c | 849 + third_party/curl/lib/http_aws_sigv4.h | 31 + third_party/curl/lib/http_chunks.c | 682 + third_party/curl/lib/http_chunks.h | 145 + third_party/curl/lib/http_digest.c | 185 + third_party/curl/lib/http_digest.h | 44 + third_party/curl/lib/http_negotiate.c | 239 + third_party/curl/lib/http_negotiate.h | 43 + third_party/curl/lib/http_ntlm.c | 270 + third_party/curl/lib/http_ntlm.h | 44 + third_party/curl/lib/http_proxy.c | 337 + third_party/curl/lib/http_proxy.h | 61 + third_party/curl/lib/idn.c | 337 + third_party/curl/lib/idn.h | 39 + third_party/curl/lib/if2ip.c | 260 + third_party/curl/lib/if2ip.h | 92 + third_party/curl/lib/imap.c | 2117 +++ third_party/curl/lib/imap.h | 101 + third_party/curl/lib/inet_ntop.c | 204 + third_party/curl/lib/inet_ntop.h | 44 + third_party/curl/lib/inet_pton.c | 243 + third_party/curl/lib/inet_pton.h | 38 + third_party/curl/lib/krb5.c | 922 ++ third_party/curl/lib/ldap.c | 1116 ++ third_party/curl/lib/libcurl.rc | 65 + third_party/curl/lib/libcurl.vers.in | 13 + third_party/curl/lib/llist.c | 162 + third_party/curl/lib/llist.h | 54 + third_party/curl/lib/macos.c | 55 + third_party/curl/lib/macos.h | 39 + third_party/curl/lib/md4.c | 530 + third_party/curl/lib/md5.c | 657 + third_party/curl/lib/memdebug.c | 463 + third_party/curl/lib/memdebug.h | 203 + third_party/curl/lib/mime.c | 2248 +++ third_party/curl/lib/mime.h | 176 + third_party/curl/lib/mprintf.c | 1204 ++ third_party/curl/lib/mqtt.c | 842 + third_party/curl/lib/mqtt.h | 63 + third_party/curl/lib/multi.c | 3947 +++++ third_party/curl/lib/multihandle.h | 191 + third_party/curl/lib/multiif.h | 156 + third_party/curl/lib/netrc.c | 353 + third_party/curl/lib/netrc.h | 43 + third_party/curl/lib/nonblock.c | 93 + third_party/curl/lib/nonblock.h | 32 + third_party/curl/lib/noproxy.c | 264 + third_party/curl/lib/noproxy.h | 43 + third_party/curl/lib/openldap.c | 1224 ++ third_party/curl/lib/parsedate.c | 644 + third_party/curl/lib/parsedate.h | 38 + third_party/curl/lib/pingpong.c | 438 + third_party/curl/lib/pingpong.h | 160 + third_party/curl/lib/pop3.c | 1584 ++ third_party/curl/lib/pop3.h | 98 + third_party/curl/lib/progress.c | 647 + third_party/curl/lib/progress.h | 79 + third_party/curl/lib/psl.c | 113 + third_party/curl/lib/psl.h | 49 + third_party/curl/lib/rand.c | 292 + third_party/curl/lib/rand.h | 50 + third_party/curl/lib/rename.c | 73 + third_party/curl/lib/rename.h | 29 + third_party/curl/lib/request.c | 411 + third_party/curl/lib/request.h | 229 + third_party/curl/lib/rtsp.c | 1042 ++ third_party/curl/lib/rtsp.h | 70 + third_party/curl/lib/select.c | 551 + third_party/curl/lib/select.h | 147 + third_party/curl/lib/sendf.c | 1438 ++ third_party/curl/lib/sendf.h | 415 + third_party/curl/lib/setopt.c | 3264 ++++ third_party/curl/lib/setopt.h | 33 + third_party/curl/lib/setup-os400.h | 157 + third_party/curl/lib/setup-vms.h | 444 + third_party/curl/lib/setup-win32.h | 138 + third_party/curl/lib/sha256.c | 545 + third_party/curl/lib/share.c | 291 + third_party/curl/lib/share.h | 68 + third_party/curl/lib/sigpipe.h | 96 + third_party/curl/lib/slist.c | 146 + third_party/curl/lib/slist.h | 41 + third_party/curl/lib/smb.c | 1206 ++ third_party/curl/lib/smb.h | 61 + third_party/curl/lib/smtp.c | 1953 +++ third_party/curl/lib/smtp.h | 87 + third_party/curl/lib/sockaddr.h | 44 + third_party/curl/lib/socketpair.c | 264 + third_party/curl/lib/socketpair.h | 98 + third_party/curl/lib/socks.c | 1277 ++ third_party/curl/lib/socks.h | 61 + third_party/curl/lib/socks_gssapi.c | 535 + third_party/curl/lib/socks_sspi.c | 620 + third_party/curl/lib/speedcheck.c | 79 + third_party/curl/lib/speedcheck.h | 35 + third_party/curl/lib/splay.c | 278 + third_party/curl/lib/splay.h | 58 + third_party/curl/lib/strcase.c | 204 + third_party/curl/lib/strcase.h | 54 + third_party/curl/lib/strdup.c | 143 + third_party/curl/lib/strdup.h | 38 + third_party/curl/lib/strerror.c | 1116 ++ third_party/curl/lib/strerror.h | 39 + third_party/curl/lib/strtok.c | 68 + third_party/curl/lib/strtok.h | 36 + third_party/curl/lib/strtoofft.c | 240 + third_party/curl/lib/strtoofft.h | 54 + third_party/curl/lib/system_win32.c | 286 + third_party/curl/lib/system_win32.h | 81 + third_party/curl/lib/telnet.c | 1652 ++ third_party/curl/lib/telnet.h | 30 + third_party/curl/lib/tftp.c | 1407 ++ third_party/curl/lib/tftp.h | 33 + third_party/curl/lib/timediff.c | 88 + third_party/curl/lib/timediff.h | 52 + third_party/curl/lib/timeval.c | 237 + third_party/curl/lib/timeval.h | 62 + third_party/curl/lib/transfer.c | 1315 ++ third_party/curl/lib/transfer.h | 144 + third_party/curl/lib/url.c | 3949 +++++ third_party/curl/lib/url.h | 82 + third_party/curl/lib/urlapi-int.h | 38 + third_party/curl/lib/urlapi.c | 2005 +++ third_party/curl/lib/urldata.h | 1974 +++ third_party/curl/lib/vauth/cleartext.c | 137 + third_party/curl/lib/vauth/cram.c | 97 + third_party/curl/lib/vauth/digest.c | 1031 ++ third_party/curl/lib/vauth/digest.h | 40 + third_party/curl/lib/vauth/digest_sspi.c | 672 + third_party/curl/lib/vauth/gsasl.c | 127 + third_party/curl/lib/vauth/krb5_gssapi.c | 324 + third_party/curl/lib/vauth/krb5_sspi.c | 475 + third_party/curl/lib/vauth/ntlm.c | 780 + third_party/curl/lib/vauth/ntlm.h | 143 + third_party/curl/lib/vauth/ntlm_sspi.c | 372 + third_party/curl/lib/vauth/oauth2.c | 108 + third_party/curl/lib/vauth/spnego_gssapi.c | 281 + third_party/curl/lib/vauth/spnego_sspi.c | 365 + third_party/curl/lib/vauth/vauth.c | 163 + third_party/curl/lib/vauth/vauth.h | 236 + third_party/curl/lib/version.c | 691 + third_party/curl/lib/version_win32.c | 321 + third_party/curl/lib/version_win32.h | 56 + third_party/curl/lib/vquic/curl_msh3.c | 1116 ++ third_party/curl/lib/vquic/curl_msh3.h | 46 + third_party/curl/lib/vquic/curl_ngtcp2.c | 2555 +++ third_party/curl/lib/vquic/curl_ngtcp2.h | 61 + third_party/curl/lib/vquic/curl_osslq.c | 2379 +++ third_party/curl/lib/vquic/curl_osslq.h | 51 + third_party/curl/lib/vquic/curl_quiche.c | 1694 ++ third_party/curl/lib/vquic/curl_quiche.h | 50 + third_party/curl/lib/vquic/vquic-tls.c | 347 + third_party/curl/lib/vquic/vquic-tls.h | 100 + third_party/curl/lib/vquic/vquic.c | 755 + third_party/curl/lib/vquic/vquic.h | 64 + third_party/curl/lib/vquic/vquic_int.h | 93 + third_party/curl/lib/vssh/libssh.c | 2953 ++++ third_party/curl/lib/vssh/libssh2.c | 3837 +++++ third_party/curl/lib/vssh/ssh.h | 273 + third_party/curl/lib/vssh/wolfssh.c | 1169 ++ third_party/curl/lib/vtls/bearssl.c | 1162 ++ third_party/curl/lib/vtls/bearssl.h | 34 + third_party/curl/lib/vtls/cipher_suite.c | 884 ++ third_party/curl/lib/vtls/cipher_suite.h | 47 + third_party/curl/lib/vtls/gtls.c | 2024 +++ third_party/curl/lib/vtls/gtls.h | 99 + third_party/curl/lib/vtls/hostcheck.c | 135 + third_party/curl/lib/vtls/hostcheck.h | 33 + third_party/curl/lib/vtls/keylog.c | 167 + third_party/curl/lib/vtls/keylog.h | 58 + third_party/curl/lib/vtls/mbedtls.c | 1700 ++ third_party/curl/lib/vtls/mbedtls.h | 34 + .../curl/lib/vtls/mbedtls_threadlock.c | 134 + .../curl/lib/vtls/mbedtls_threadlock.h | 50 + third_party/curl/lib/vtls/openssl.c | 5225 ++++++ third_party/curl/lib/vtls/openssl.h | 122 + third_party/curl/lib/vtls/rustls.c | 856 + third_party/curl/lib/vtls/rustls.h | 35 + third_party/curl/lib/vtls/schannel.c | 3001 ++++ third_party/curl/lib/vtls/schannel.h | 86 + third_party/curl/lib/vtls/schannel_int.h | 185 + third_party/curl/lib/vtls/schannel_verify.c | 794 + third_party/curl/lib/vtls/sectransp.c | 2927 ++++ third_party/curl/lib/vtls/sectransp.h | 34 + third_party/curl/lib/vtls/vtls.c | 2254 +++ third_party/curl/lib/vtls/vtls.h | 258 + third_party/curl/lib/vtls/vtls_int.h | 229 + third_party/curl/lib/vtls/wolfssl.c | 1803 +++ third_party/curl/lib/vtls/wolfssl.h | 50 + third_party/curl/lib/vtls/x509asn1.c | 1263 ++ third_party/curl/lib/vtls/x509asn1.h | 92 + third_party/curl/lib/warnless.c | 386 + third_party/curl/lib/warnless.h | 92 + third_party/curl/lib/ws.c | 1271 ++ third_party/curl/lib/ws.h | 90 + third_party/curl/libcurl.def | 95 + third_party/curl/libcurl.pc.in | 40 + third_party/curl/src/.checksrc | 1 + third_party/curl/src/CMakeLists.txt | 144 + third_party/curl/src/Makefile.am | 163 + third_party/curl/src/Makefile.in | 2001 +++ third_party/curl/src/Makefile.inc | 157 + third_party/curl/src/Makefile.mk | 90 + third_party/curl/src/curl.rc | 113 + third_party/curl/src/mkhelp.pl | 201 + third_party/curl/src/slist_wc.c | 74 + third_party/curl/src/slist_wc.h | 57 + third_party/curl/src/terminal.c | 91 + third_party/curl/src/terminal.h | 30 + third_party/curl/src/tool_binmode.c | 53 + third_party/curl/src/tool_binmode.h | 38 + third_party/curl/src/tool_bname.c | 51 + third_party/curl/src/tool_bname.h | 36 + third_party/curl/src/tool_cb_dbg.c | 300 + third_party/curl/src/tool_cb_dbg.h | 36 + third_party/curl/src/tool_cb_hdr.c | 467 + third_party/curl/src/tool_cb_hdr.h | 58 + third_party/curl/src/tool_cb_prg.c | 243 + third_party/curl/src/tool_cb_prg.h | 56 + third_party/curl/src/tool_cb_rea.c | 158 + third_party/curl/src/tool_cb_rea.h | 42 + third_party/curl/src/tool_cb_see.c | 92 + third_party/curl/src/tool_cb_see.h | 34 + third_party/curl/src/tool_cb_soc.c | 58 + third_party/curl/src/tool_cb_soc.h | 36 + third_party/curl/src/tool_cb_wrt.c | 372 + third_party/curl/src/tool_cb_wrt.h | 38 + third_party/curl/src/tool_cfgable.c | 204 + third_party/curl/src/tool_cfgable.h | 346 + third_party/curl/src/tool_dirhie.c | 167 + third_party/curl/src/tool_dirhie.h | 32 + third_party/curl/src/tool_doswin.c | 807 + third_party/curl/src/tool_doswin.h | 72 + third_party/curl/src/tool_easysrc.c | 238 + third_party/curl/src/tool_easysrc.h | 58 + third_party/curl/src/tool_filetime.c | 156 + third_party/curl/src/tool_filetime.h | 42 + third_party/curl/src/tool_findfile.c | 157 + third_party/curl/src/tool_findfile.h | 36 + third_party/curl/src/tool_formparse.c | 907 ++ third_party/curl/src/tool_formparse.h | 73 + third_party/curl/src/tool_getparam.c | 2988 ++++ third_party/curl/src/tool_getparam.h | 74 + third_party/curl/src/tool_getpass.c | 207 + third_party/curl/src/tool_getpass.h | 38 + third_party/curl/src/tool_help.c | 278 + third_party/curl/src/tool_help.h | 75 + third_party/curl/src/tool_helpers.c | 135 + third_party/curl/src/tool_helpers.h | 36 + third_party/curl/src/tool_hugehelp.c | 10871 +++++++++++++ third_party/curl/src/tool_hugehelp.h | 35 + third_party/curl/src/tool_ipfs.c | 291 + third_party/curl/src/tool_ipfs.h | 33 + third_party/curl/src/tool_libinfo.c | 208 + third_party/curl/src/tool_libinfo.h | 67 + third_party/curl/src/tool_listhelp.c | 833 + third_party/curl/src/tool_main.c | 297 + third_party/curl/src/tool_main.h | 48 + third_party/curl/src/tool_msgs.c | 152 + third_party/curl/src/tool_msgs.h | 38 + third_party/curl/src/tool_operate.c | 2911 ++++ third_party/curl/src/tool_operate.h | 87 + third_party/curl/src/tool_operhlp.c | 254 + third_party/curl/src/tool_operhlp.h | 42 + third_party/curl/src/tool_paramhlp.c | 746 + third_party/curl/src/tool_paramhlp.h | 63 + third_party/curl/src/tool_parsecfg.c | 353 + third_party/curl/src/tool_parsecfg.h | 30 + third_party/curl/src/tool_progress.c | 321 + third_party/curl/src/tool_progress.h | 41 + third_party/curl/src/tool_sdecls.h | 133 + third_party/curl/src/tool_setopt.c | 722 + third_party/curl/src/tool_setopt.h | 160 + third_party/curl/src/tool_setup.h | 90 + third_party/curl/src/tool_sleep.c | 61 + third_party/curl/src/tool_sleep.h | 30 + third_party/curl/src/tool_stderr.c | 71 + third_party/curl/src/tool_stderr.h | 32 + third_party/curl/src/tool_strdup.c | 44 + third_party/curl/src/tool_strdup.h | 32 + third_party/curl/src/tool_urlglob.c | 721 + third_party/curl/src/tool_urlglob.h | 78 + third_party/curl/src/tool_util.c | 190 + third_party/curl/src/tool_util.h | 42 + third_party/curl/src/tool_version.h | 36 + third_party/curl/src/tool_vms.c | 220 + third_party/curl/src/tool_vms.h | 48 + third_party/curl/src/tool_writeout.c | 704 + third_party/curl/src/tool_writeout.h | 115 + third_party/curl/src/tool_writeout_json.c | 174 + third_party/curl/src/tool_writeout_json.h | 38 + third_party/curl/src/tool_xattr.c | 136 + third_party/curl/src/tool_xattr.h | 45 + third_party/curl/src/var.c | 467 + third_party/curl/src/var.h | 47 + third_party/curlpp/CMakeLists.txt | 111 + third_party/curlpp/Readme.md | 34 + third_party/curlpp/include/curlpp/Easy.hpp | 170 + third_party/curlpp/include/curlpp/Easy.inl | 48 + .../curlpp/include/curlpp/Exception.hpp | 306 + third_party/curlpp/include/curlpp/Form.hpp | 272 + third_party/curlpp/include/curlpp/Info.hpp | 121 + third_party/curlpp/include/curlpp/Info.inl | 79 + third_party/curlpp/include/curlpp/Infos.hpp | 99 + third_party/curlpp/include/curlpp/Multi.hpp | 82 + third_party/curlpp/include/curlpp/Option.hpp | 253 + third_party/curlpp/include/curlpp/Option.inl | 206 + .../curlpp/include/curlpp/OptionBase.hpp | 107 + third_party/curlpp/include/curlpp/Options.hpp | 336 + third_party/curlpp/include/curlpp/Types.hpp | 56 + third_party/curlpp/include/curlpp/cURLpp.hpp | 223 + .../include/curlpp/internal/CurlHandle.hpp | 203 + .../include/curlpp/internal/CurlHandle.inl | 76 + .../curlpp/internal/OptionContainer.hpp | 106 + .../curlpp/internal/OptionContainer.inl | 73 + .../curlpp/internal/OptionContainerType.hpp | 172 + .../include/curlpp/internal/OptionList.hpp | 122 + .../include/curlpp/internal/OptionSetter.hpp | 504 + .../include/curlpp/internal/OptionSetter.inl | 56 + .../curlpp/include/curlpp/internal/SList.hpp | 92 + .../curlpp/include/utilspp/EmptyType.hpp | 32 + .../curlpp/include/utilspp/NonCopyable.hpp | 41 + .../curlpp/include/utilspp/NullType.hpp | 32 + .../curlpp/include/utilspp/SmartPtr.hpp | 186 + .../include/utilspp/ThreadingFactoryMutex.hpp | 45 + .../include/utilspp/ThreadingFactoryMutex.inl | 37 + .../include/utilspp/ThreadingSingle.hpp | 52 + .../include/utilspp/ThreadingSingle.inl | 50 + .../curlpp/include/utilspp/TypeList.hpp | 216 + .../curlpp/include/utilspp/TypeTrait.hpp | 869 + .../curlpp/include/utilspp/clone_ptr.hpp | 96 + third_party/curlpp/src/curlpp/Easy.cpp | 122 + third_party/curlpp/src/curlpp/Exception.cpp | 146 + third_party/curlpp/src/curlpp/Form.cpp | 232 + third_party/curlpp/src/curlpp/Info.cpp | 60 + third_party/curlpp/src/curlpp/Multi.cpp | 115 + third_party/curlpp/src/curlpp/OptionBase.cpp | 44 + third_party/curlpp/src/curlpp/Options.cpp | 36 + third_party/curlpp/src/curlpp/cURLpp.cpp | 121 + .../curlpp/src/curlpp/internal/CurlHandle.cpp | 345 + .../curlpp/src/curlpp/internal/OptionList.cpp | 112 + .../src/curlpp/internal/OptionSetter.cpp | 296 + .../curlpp/src/curlpp/internal/SList.cpp | 148 + third_party/inih/LICENSE.txt | 27 + third_party/inih/README.md | 175 + third_party/inih/cpp/INIReader.cpp | 148 + third_party/inih/cpp/INIReader.h | 107 + third_party/inih/ini.c | 304 + third_party/inih/ini.h | 178 + third_party/minio-cpp/CMakeLists.txt | 268 + third_party/minio-cpp/README.md | 168 + third_party/minio-cpp/include/miniocpp/args.h | 606 + .../minio-cpp/include/miniocpp/baseclient.h | 186 + .../minio-cpp/include/miniocpp/client.h | 129 + .../minio-cpp/include/miniocpp/config.h | 77 + .../minio-cpp/include/miniocpp/credentials.h | 80 + .../minio-cpp/include/miniocpp/error.h | 61 + third_party/minio-cpp/include/miniocpp/http.h | 167 + .../minio-cpp/include/miniocpp/providers.h | 259 + .../minio-cpp/include/miniocpp/request.h | 115 + .../minio-cpp/include/miniocpp/response.h | 575 + .../minio-cpp/include/miniocpp/select.h | 72 + .../minio-cpp/include/miniocpp/signer.h | 83 + third_party/minio-cpp/include/miniocpp/sse.h | 68 + .../minio-cpp/include/miniocpp/types.h | 771 + .../minio-cpp/include/miniocpp/utils.h | 234 + third_party/minio-cpp/src/args.cc | 735 + third_party/minio-cpp/src/baseclient.cc | 1965 +++ third_party/minio-cpp/src/client.cc | 814 + third_party/minio-cpp/src/credentials.cc | 62 + third_party/minio-cpp/src/error.cc | 24 + third_party/minio-cpp/src/http.cc | 475 + third_party/minio-cpp/src/providers.cc | 541 + third_party/minio-cpp/src/request.cc | 371 + third_party/minio-cpp/src/response.cc | 833 + third_party/minio-cpp/src/select.cc | 246 + third_party/minio-cpp/src/signer.cc | 208 + third_party/minio-cpp/src/sse.cc | 73 + third_party/minio-cpp/src/types.cc | 637 + third_party/minio-cpp/src/utils.cc | 675 + third_party/nlohmann/json_fwd.hpp | 176 + third_party/pugixml/CMakeLists.txt | 225 + third_party/pugixml/LICENSE.md | 24 + third_party/pugixml/readme.txt | 50 + .../pugixml/scripts/pugixml-config.cmake.in | 0 third_party/pugixml/scripts/pugixml.pc.in | 0 third_party/pugixml/src/pugiconfig.hpp | 77 + third_party/pugixml/src/pugixml.cpp | 13226 ++++++++++++++++ third_party/pugixml/src/pugixml.hpp | 1516 ++ third_party/versions | 7 +- 599 files changed, 258297 insertions(+), 112 deletions(-) create mode 100644 src/storage/io/s3_client.cppm create mode 100644 src/storage/io/s3_client_minio.cpp create mode 100644 src/storage/io/s3_client_minio.cppm create mode 100644 src/storage/io/s3_file_system.cpp create mode 100644 src/storage/io/s3_file_system.cppm create mode 100644 third_party/curl/CMake/CMakeConfigurableFile.in create mode 100644 third_party/curl/CMake/CurlSymbolHiding.cmake create mode 100644 third_party/curl/CMake/CurlTests.c create mode 100644 third_party/curl/CMake/FindBearSSL.cmake create mode 100644 third_party/curl/CMake/FindBrotli.cmake create mode 100644 third_party/curl/CMake/FindCARES.cmake create mode 100644 third_party/curl/CMake/FindGSS.cmake create mode 100644 third_party/curl/CMake/FindLibPSL.cmake create mode 100644 third_party/curl/CMake/FindLibSSH2.cmake create mode 100644 third_party/curl/CMake/FindMSH3.cmake create mode 100644 third_party/curl/CMake/FindMbedTLS.cmake create mode 100644 third_party/curl/CMake/FindNGHTTP2.cmake create mode 100644 third_party/curl/CMake/FindNGHTTP3.cmake create mode 100644 third_party/curl/CMake/FindNGTCP2.cmake create mode 100644 third_party/curl/CMake/FindQUICHE.cmake create mode 100644 third_party/curl/CMake/FindWolfSSL.cmake create mode 100644 third_party/curl/CMake/FindZstd.cmake create mode 100644 third_party/curl/CMake/Macros.cmake create mode 100644 third_party/curl/CMake/OtherTests.cmake create mode 100644 third_party/curl/CMake/PickyWarnings.cmake create mode 100644 third_party/curl/CMake/Platforms/WindowsCache.cmake create mode 100644 third_party/curl/CMake/Utilities.cmake create mode 100644 third_party/curl/CMake/cmake_uninstall.cmake.in create mode 100644 third_party/curl/CMake/curl-config.cmake.in create mode 100644 third_party/curl/CMakeLists.txt create mode 100644 third_party/curl/curl-config.in create mode 100644 third_party/curl/include/Makefile.am create mode 100644 third_party/curl/include/Makefile.in create mode 100644 third_party/curl/include/README.md create mode 100644 third_party/curl/include/curl/Makefile.am create mode 100644 third_party/curl/include/curl/Makefile.in create mode 100644 third_party/curl/include/curl/curl.h create mode 100644 third_party/curl/include/curl/curlver.h create mode 100644 third_party/curl/include/curl/easy.h create mode 100644 third_party/curl/include/curl/header.h create mode 100644 third_party/curl/include/curl/mprintf.h create mode 100644 third_party/curl/include/curl/multi.h create mode 100644 third_party/curl/include/curl/options.h create mode 100644 third_party/curl/include/curl/stdcheaders.h create mode 100644 third_party/curl/include/curl/system.h create mode 100644 third_party/curl/include/curl/typecheck-gcc.h create mode 100644 third_party/curl/include/curl/urlapi.h create mode 100644 third_party/curl/include/curl/websockets.h create mode 100644 third_party/curl/lib/.checksrc create mode 100644 third_party/curl/lib/CMakeLists.txt create mode 100644 third_party/curl/lib/Makefile.am create mode 100644 third_party/curl/lib/Makefile.in create mode 100644 third_party/curl/lib/Makefile.inc create mode 100644 third_party/curl/lib/Makefile.mk create mode 100644 third_party/curl/lib/Makefile.soname create mode 100644 third_party/curl/lib/altsvc.c create mode 100644 third_party/curl/lib/altsvc.h create mode 100644 third_party/curl/lib/amigaos.c create mode 100644 third_party/curl/lib/amigaos.h create mode 100644 third_party/curl/lib/arpa_telnet.h create mode 100644 third_party/curl/lib/asyn-ares.c create mode 100644 third_party/curl/lib/asyn-thread.c create mode 100644 third_party/curl/lib/asyn.h create mode 100644 third_party/curl/lib/base64.c create mode 100644 third_party/curl/lib/bufq.c create mode 100644 third_party/curl/lib/bufq.h create mode 100644 third_party/curl/lib/bufref.c create mode 100644 third_party/curl/lib/bufref.h create mode 100644 third_party/curl/lib/c-hyper.c create mode 100644 third_party/curl/lib/c-hyper.h create mode 100644 third_party/curl/lib/cf-h1-proxy.c create mode 100644 third_party/curl/lib/cf-h1-proxy.h create mode 100644 third_party/curl/lib/cf-h2-proxy.c create mode 100644 third_party/curl/lib/cf-h2-proxy.h create mode 100644 third_party/curl/lib/cf-haproxy.c create mode 100644 third_party/curl/lib/cf-haproxy.h create mode 100644 third_party/curl/lib/cf-https-connect.c create mode 100644 third_party/curl/lib/cf-https-connect.h create mode 100644 third_party/curl/lib/cf-socket.c create mode 100644 third_party/curl/lib/cf-socket.h create mode 100644 third_party/curl/lib/cfilters.c create mode 100644 third_party/curl/lib/cfilters.h create mode 100644 third_party/curl/lib/config-amigaos.h create mode 100644 third_party/curl/lib/config-dos.h create mode 100644 third_party/curl/lib/config-mac.h create mode 100644 third_party/curl/lib/config-os400.h create mode 100644 third_party/curl/lib/config-plan9.h create mode 100644 third_party/curl/lib/config-riscos.h create mode 100644 third_party/curl/lib/config-win32.h create mode 100644 third_party/curl/lib/config-win32ce.h create mode 100644 third_party/curl/lib/conncache.c create mode 100644 third_party/curl/lib/conncache.h create mode 100644 third_party/curl/lib/connect.c create mode 100644 third_party/curl/lib/connect.h create mode 100644 third_party/curl/lib/content_encoding.c create mode 100644 third_party/curl/lib/content_encoding.h create mode 100644 third_party/curl/lib/cookie.c create mode 100644 third_party/curl/lib/cookie.h create mode 100644 third_party/curl/lib/curl_addrinfo.c create mode 100644 third_party/curl/lib/curl_addrinfo.h create mode 100644 third_party/curl/lib/curl_base64.h create mode 100644 third_party/curl/lib/curl_config.h.cmake create mode 100644 third_party/curl/lib/curl_config.h.in create mode 100644 third_party/curl/lib/curl_ctype.h create mode 100644 third_party/curl/lib/curl_des.c create mode 100644 third_party/curl/lib/curl_des.h create mode 100644 third_party/curl/lib/curl_endian.c create mode 100644 third_party/curl/lib/curl_endian.h create mode 100644 third_party/curl/lib/curl_fnmatch.c create mode 100644 third_party/curl/lib/curl_fnmatch.h create mode 100644 third_party/curl/lib/curl_get_line.c create mode 100644 third_party/curl/lib/curl_get_line.h create mode 100644 third_party/curl/lib/curl_gethostname.c create mode 100644 third_party/curl/lib/curl_gethostname.h create mode 100644 third_party/curl/lib/curl_gssapi.c create mode 100644 third_party/curl/lib/curl_gssapi.h create mode 100644 third_party/curl/lib/curl_hmac.h create mode 100644 third_party/curl/lib/curl_krb5.h create mode 100644 third_party/curl/lib/curl_ldap.h create mode 100644 third_party/curl/lib/curl_md4.h create mode 100644 third_party/curl/lib/curl_md5.h create mode 100644 third_party/curl/lib/curl_memory.h create mode 100644 third_party/curl/lib/curl_memrchr.c create mode 100644 third_party/curl/lib/curl_memrchr.h create mode 100644 third_party/curl/lib/curl_multibyte.c create mode 100644 third_party/curl/lib/curl_multibyte.h create mode 100644 third_party/curl/lib/curl_ntlm_core.c create mode 100644 third_party/curl/lib/curl_ntlm_core.h create mode 100644 third_party/curl/lib/curl_path.c create mode 100644 third_party/curl/lib/curl_path.h create mode 100644 third_party/curl/lib/curl_printf.h create mode 100644 third_party/curl/lib/curl_range.c create mode 100644 third_party/curl/lib/curl_range.h create mode 100644 third_party/curl/lib/curl_rtmp.c create mode 100644 third_party/curl/lib/curl_rtmp.h create mode 100644 third_party/curl/lib/curl_sasl.c create mode 100644 third_party/curl/lib/curl_sasl.h create mode 100644 third_party/curl/lib/curl_setup.h create mode 100644 third_party/curl/lib/curl_setup_once.h create mode 100644 third_party/curl/lib/curl_sha256.h create mode 100644 third_party/curl/lib/curl_sha512_256.c create mode 100644 third_party/curl/lib/curl_sha512_256.h create mode 100644 third_party/curl/lib/curl_sspi.c create mode 100644 third_party/curl/lib/curl_sspi.h create mode 100644 third_party/curl/lib/curl_threads.c create mode 100644 third_party/curl/lib/curl_threads.h create mode 100644 third_party/curl/lib/curl_trc.c create mode 100644 third_party/curl/lib/curl_trc.h create mode 100644 third_party/curl/lib/curlx.h create mode 100644 third_party/curl/lib/cw-out.c create mode 100644 third_party/curl/lib/cw-out.h create mode 100644 third_party/curl/lib/dict.c create mode 100644 third_party/curl/lib/dict.h create mode 100644 third_party/curl/lib/dllmain.c create mode 100644 third_party/curl/lib/doh.c create mode 100644 third_party/curl/lib/doh.h create mode 100644 third_party/curl/lib/dynbuf.c create mode 100644 third_party/curl/lib/dynbuf.h create mode 100644 third_party/curl/lib/dynhds.c create mode 100644 third_party/curl/lib/dynhds.h create mode 100644 third_party/curl/lib/easy.c create mode 100644 third_party/curl/lib/easy_lock.h create mode 100644 third_party/curl/lib/easygetopt.c create mode 100644 third_party/curl/lib/easyif.h create mode 100644 third_party/curl/lib/easyoptions.c create mode 100644 third_party/curl/lib/easyoptions.h create mode 100644 third_party/curl/lib/escape.c create mode 100644 third_party/curl/lib/escape.h create mode 100644 third_party/curl/lib/file.c create mode 100644 third_party/curl/lib/file.h create mode 100644 third_party/curl/lib/fileinfo.c create mode 100644 third_party/curl/lib/fileinfo.h create mode 100644 third_party/curl/lib/fopen.c create mode 100644 third_party/curl/lib/fopen.h create mode 100644 third_party/curl/lib/formdata.c create mode 100644 third_party/curl/lib/formdata.h create mode 100644 third_party/curl/lib/ftp.c create mode 100644 third_party/curl/lib/ftp.h create mode 100644 third_party/curl/lib/ftplistparser.c create mode 100644 third_party/curl/lib/ftplistparser.h create mode 100644 third_party/curl/lib/functypes.h create mode 100644 third_party/curl/lib/getenv.c create mode 100644 third_party/curl/lib/getinfo.c create mode 100644 third_party/curl/lib/getinfo.h create mode 100644 third_party/curl/lib/gopher.c create mode 100644 third_party/curl/lib/gopher.h create mode 100644 third_party/curl/lib/hash.c create mode 100644 third_party/curl/lib/hash.h create mode 100644 third_party/curl/lib/headers.c create mode 100644 third_party/curl/lib/headers.h create mode 100644 third_party/curl/lib/hmac.c create mode 100644 third_party/curl/lib/hostasyn.c create mode 100644 third_party/curl/lib/hostip.c create mode 100644 third_party/curl/lib/hostip.h create mode 100644 third_party/curl/lib/hostip4.c create mode 100644 third_party/curl/lib/hostip6.c create mode 100644 third_party/curl/lib/hostsyn.c create mode 100644 third_party/curl/lib/hsts.c create mode 100644 third_party/curl/lib/hsts.h create mode 100644 third_party/curl/lib/http.c create mode 100644 third_party/curl/lib/http.h create mode 100644 third_party/curl/lib/http1.c create mode 100644 third_party/curl/lib/http1.h create mode 100644 third_party/curl/lib/http2.c create mode 100644 third_party/curl/lib/http2.h create mode 100644 third_party/curl/lib/http_aws_sigv4.c create mode 100644 third_party/curl/lib/http_aws_sigv4.h create mode 100644 third_party/curl/lib/http_chunks.c create mode 100644 third_party/curl/lib/http_chunks.h create mode 100644 third_party/curl/lib/http_digest.c create mode 100644 third_party/curl/lib/http_digest.h create mode 100644 third_party/curl/lib/http_negotiate.c create mode 100644 third_party/curl/lib/http_negotiate.h create mode 100644 third_party/curl/lib/http_ntlm.c create mode 100644 third_party/curl/lib/http_ntlm.h create mode 100644 third_party/curl/lib/http_proxy.c create mode 100644 third_party/curl/lib/http_proxy.h create mode 100644 third_party/curl/lib/idn.c create mode 100644 third_party/curl/lib/idn.h create mode 100644 third_party/curl/lib/if2ip.c create mode 100644 third_party/curl/lib/if2ip.h create mode 100644 third_party/curl/lib/imap.c create mode 100644 third_party/curl/lib/imap.h create mode 100644 third_party/curl/lib/inet_ntop.c create mode 100644 third_party/curl/lib/inet_ntop.h create mode 100644 third_party/curl/lib/inet_pton.c create mode 100644 third_party/curl/lib/inet_pton.h create mode 100644 third_party/curl/lib/krb5.c create mode 100644 third_party/curl/lib/ldap.c create mode 100644 third_party/curl/lib/libcurl.rc create mode 100644 third_party/curl/lib/libcurl.vers.in create mode 100644 third_party/curl/lib/llist.c create mode 100644 third_party/curl/lib/llist.h create mode 100644 third_party/curl/lib/macos.c create mode 100644 third_party/curl/lib/macos.h create mode 100644 third_party/curl/lib/md4.c create mode 100644 third_party/curl/lib/md5.c create mode 100644 third_party/curl/lib/memdebug.c create mode 100644 third_party/curl/lib/memdebug.h create mode 100644 third_party/curl/lib/mime.c create mode 100644 third_party/curl/lib/mime.h create mode 100644 third_party/curl/lib/mprintf.c create mode 100644 third_party/curl/lib/mqtt.c create mode 100644 third_party/curl/lib/mqtt.h create mode 100644 third_party/curl/lib/multi.c create mode 100644 third_party/curl/lib/multihandle.h create mode 100644 third_party/curl/lib/multiif.h create mode 100644 third_party/curl/lib/netrc.c create mode 100644 third_party/curl/lib/netrc.h create mode 100644 third_party/curl/lib/nonblock.c create mode 100644 third_party/curl/lib/nonblock.h create mode 100644 third_party/curl/lib/noproxy.c create mode 100644 third_party/curl/lib/noproxy.h create mode 100644 third_party/curl/lib/openldap.c create mode 100644 third_party/curl/lib/parsedate.c create mode 100644 third_party/curl/lib/parsedate.h create mode 100644 third_party/curl/lib/pingpong.c create mode 100644 third_party/curl/lib/pingpong.h create mode 100644 third_party/curl/lib/pop3.c create mode 100644 third_party/curl/lib/pop3.h create mode 100644 third_party/curl/lib/progress.c create mode 100644 third_party/curl/lib/progress.h create mode 100644 third_party/curl/lib/psl.c create mode 100644 third_party/curl/lib/psl.h create mode 100644 third_party/curl/lib/rand.c create mode 100644 third_party/curl/lib/rand.h create mode 100644 third_party/curl/lib/rename.c create mode 100644 third_party/curl/lib/rename.h create mode 100644 third_party/curl/lib/request.c create mode 100644 third_party/curl/lib/request.h create mode 100644 third_party/curl/lib/rtsp.c create mode 100644 third_party/curl/lib/rtsp.h create mode 100644 third_party/curl/lib/select.c create mode 100644 third_party/curl/lib/select.h create mode 100644 third_party/curl/lib/sendf.c create mode 100644 third_party/curl/lib/sendf.h create mode 100644 third_party/curl/lib/setopt.c create mode 100644 third_party/curl/lib/setopt.h create mode 100644 third_party/curl/lib/setup-os400.h create mode 100644 third_party/curl/lib/setup-vms.h create mode 100644 third_party/curl/lib/setup-win32.h create mode 100644 third_party/curl/lib/sha256.c create mode 100644 third_party/curl/lib/share.c create mode 100644 third_party/curl/lib/share.h create mode 100644 third_party/curl/lib/sigpipe.h create mode 100644 third_party/curl/lib/slist.c create mode 100644 third_party/curl/lib/slist.h create mode 100644 third_party/curl/lib/smb.c create mode 100644 third_party/curl/lib/smb.h create mode 100644 third_party/curl/lib/smtp.c create mode 100644 third_party/curl/lib/smtp.h create mode 100644 third_party/curl/lib/sockaddr.h create mode 100644 third_party/curl/lib/socketpair.c create mode 100644 third_party/curl/lib/socketpair.h create mode 100644 third_party/curl/lib/socks.c create mode 100644 third_party/curl/lib/socks.h create mode 100644 third_party/curl/lib/socks_gssapi.c create mode 100644 third_party/curl/lib/socks_sspi.c create mode 100644 third_party/curl/lib/speedcheck.c create mode 100644 third_party/curl/lib/speedcheck.h create mode 100644 third_party/curl/lib/splay.c create mode 100644 third_party/curl/lib/splay.h create mode 100644 third_party/curl/lib/strcase.c create mode 100644 third_party/curl/lib/strcase.h create mode 100644 third_party/curl/lib/strdup.c create mode 100644 third_party/curl/lib/strdup.h create mode 100644 third_party/curl/lib/strerror.c create mode 100644 third_party/curl/lib/strerror.h create mode 100644 third_party/curl/lib/strtok.c create mode 100644 third_party/curl/lib/strtok.h create mode 100644 third_party/curl/lib/strtoofft.c create mode 100644 third_party/curl/lib/strtoofft.h create mode 100644 third_party/curl/lib/system_win32.c create mode 100644 third_party/curl/lib/system_win32.h create mode 100644 third_party/curl/lib/telnet.c create mode 100644 third_party/curl/lib/telnet.h create mode 100644 third_party/curl/lib/tftp.c create mode 100644 third_party/curl/lib/tftp.h create mode 100644 third_party/curl/lib/timediff.c create mode 100644 third_party/curl/lib/timediff.h create mode 100644 third_party/curl/lib/timeval.c create mode 100644 third_party/curl/lib/timeval.h create mode 100644 third_party/curl/lib/transfer.c create mode 100644 third_party/curl/lib/transfer.h create mode 100644 third_party/curl/lib/url.c create mode 100644 third_party/curl/lib/url.h create mode 100644 third_party/curl/lib/urlapi-int.h create mode 100644 third_party/curl/lib/urlapi.c create mode 100644 third_party/curl/lib/urldata.h create mode 100644 third_party/curl/lib/vauth/cleartext.c create mode 100644 third_party/curl/lib/vauth/cram.c create mode 100644 third_party/curl/lib/vauth/digest.c create mode 100644 third_party/curl/lib/vauth/digest.h create mode 100644 third_party/curl/lib/vauth/digest_sspi.c create mode 100644 third_party/curl/lib/vauth/gsasl.c create mode 100644 third_party/curl/lib/vauth/krb5_gssapi.c create mode 100644 third_party/curl/lib/vauth/krb5_sspi.c create mode 100644 third_party/curl/lib/vauth/ntlm.c create mode 100644 third_party/curl/lib/vauth/ntlm.h create mode 100644 third_party/curl/lib/vauth/ntlm_sspi.c create mode 100644 third_party/curl/lib/vauth/oauth2.c create mode 100644 third_party/curl/lib/vauth/spnego_gssapi.c create mode 100644 third_party/curl/lib/vauth/spnego_sspi.c create mode 100644 third_party/curl/lib/vauth/vauth.c create mode 100644 third_party/curl/lib/vauth/vauth.h create mode 100644 third_party/curl/lib/version.c create mode 100644 third_party/curl/lib/version_win32.c create mode 100644 third_party/curl/lib/version_win32.h create mode 100644 third_party/curl/lib/vquic/curl_msh3.c create mode 100644 third_party/curl/lib/vquic/curl_msh3.h create mode 100644 third_party/curl/lib/vquic/curl_ngtcp2.c create mode 100644 third_party/curl/lib/vquic/curl_ngtcp2.h create mode 100644 third_party/curl/lib/vquic/curl_osslq.c create mode 100644 third_party/curl/lib/vquic/curl_osslq.h create mode 100644 third_party/curl/lib/vquic/curl_quiche.c create mode 100644 third_party/curl/lib/vquic/curl_quiche.h create mode 100644 third_party/curl/lib/vquic/vquic-tls.c create mode 100644 third_party/curl/lib/vquic/vquic-tls.h create mode 100644 third_party/curl/lib/vquic/vquic.c create mode 100644 third_party/curl/lib/vquic/vquic.h create mode 100644 third_party/curl/lib/vquic/vquic_int.h create mode 100644 third_party/curl/lib/vssh/libssh.c create mode 100644 third_party/curl/lib/vssh/libssh2.c create mode 100644 third_party/curl/lib/vssh/ssh.h create mode 100644 third_party/curl/lib/vssh/wolfssh.c create mode 100644 third_party/curl/lib/vtls/bearssl.c create mode 100644 third_party/curl/lib/vtls/bearssl.h create mode 100644 third_party/curl/lib/vtls/cipher_suite.c create mode 100644 third_party/curl/lib/vtls/cipher_suite.h create mode 100644 third_party/curl/lib/vtls/gtls.c create mode 100644 third_party/curl/lib/vtls/gtls.h create mode 100644 third_party/curl/lib/vtls/hostcheck.c create mode 100644 third_party/curl/lib/vtls/hostcheck.h create mode 100644 third_party/curl/lib/vtls/keylog.c create mode 100644 third_party/curl/lib/vtls/keylog.h create mode 100644 third_party/curl/lib/vtls/mbedtls.c create mode 100644 third_party/curl/lib/vtls/mbedtls.h create mode 100644 third_party/curl/lib/vtls/mbedtls_threadlock.c create mode 100644 third_party/curl/lib/vtls/mbedtls_threadlock.h create mode 100644 third_party/curl/lib/vtls/openssl.c create mode 100644 third_party/curl/lib/vtls/openssl.h create mode 100644 third_party/curl/lib/vtls/rustls.c create mode 100644 third_party/curl/lib/vtls/rustls.h create mode 100644 third_party/curl/lib/vtls/schannel.c create mode 100644 third_party/curl/lib/vtls/schannel.h create mode 100644 third_party/curl/lib/vtls/schannel_int.h create mode 100644 third_party/curl/lib/vtls/schannel_verify.c create mode 100644 third_party/curl/lib/vtls/sectransp.c create mode 100644 third_party/curl/lib/vtls/sectransp.h create mode 100644 third_party/curl/lib/vtls/vtls.c create mode 100644 third_party/curl/lib/vtls/vtls.h create mode 100644 third_party/curl/lib/vtls/vtls_int.h create mode 100644 third_party/curl/lib/vtls/wolfssl.c create mode 100644 third_party/curl/lib/vtls/wolfssl.h create mode 100644 third_party/curl/lib/vtls/x509asn1.c create mode 100644 third_party/curl/lib/vtls/x509asn1.h create mode 100644 third_party/curl/lib/warnless.c create mode 100644 third_party/curl/lib/warnless.h create mode 100644 third_party/curl/lib/ws.c create mode 100644 third_party/curl/lib/ws.h create mode 100644 third_party/curl/libcurl.def create mode 100644 third_party/curl/libcurl.pc.in create mode 100644 third_party/curl/src/.checksrc create mode 100644 third_party/curl/src/CMakeLists.txt create mode 100644 third_party/curl/src/Makefile.am create mode 100644 third_party/curl/src/Makefile.in create mode 100644 third_party/curl/src/Makefile.inc create mode 100644 third_party/curl/src/Makefile.mk create mode 100644 third_party/curl/src/curl.rc create mode 100755 third_party/curl/src/mkhelp.pl create mode 100644 third_party/curl/src/slist_wc.c create mode 100644 third_party/curl/src/slist_wc.h create mode 100644 third_party/curl/src/terminal.c create mode 100644 third_party/curl/src/terminal.h create mode 100644 third_party/curl/src/tool_binmode.c create mode 100644 third_party/curl/src/tool_binmode.h create mode 100644 third_party/curl/src/tool_bname.c create mode 100644 third_party/curl/src/tool_bname.h create mode 100644 third_party/curl/src/tool_cb_dbg.c create mode 100644 third_party/curl/src/tool_cb_dbg.h create mode 100644 third_party/curl/src/tool_cb_hdr.c create mode 100644 third_party/curl/src/tool_cb_hdr.h create mode 100644 third_party/curl/src/tool_cb_prg.c create mode 100644 third_party/curl/src/tool_cb_prg.h create mode 100644 third_party/curl/src/tool_cb_rea.c create mode 100644 third_party/curl/src/tool_cb_rea.h create mode 100644 third_party/curl/src/tool_cb_see.c create mode 100644 third_party/curl/src/tool_cb_see.h create mode 100644 third_party/curl/src/tool_cb_soc.c create mode 100644 third_party/curl/src/tool_cb_soc.h create mode 100644 third_party/curl/src/tool_cb_wrt.c create mode 100644 third_party/curl/src/tool_cb_wrt.h create mode 100644 third_party/curl/src/tool_cfgable.c create mode 100644 third_party/curl/src/tool_cfgable.h create mode 100644 third_party/curl/src/tool_dirhie.c create mode 100644 third_party/curl/src/tool_dirhie.h create mode 100644 third_party/curl/src/tool_doswin.c create mode 100644 third_party/curl/src/tool_doswin.h create mode 100644 third_party/curl/src/tool_easysrc.c create mode 100644 third_party/curl/src/tool_easysrc.h create mode 100644 third_party/curl/src/tool_filetime.c create mode 100644 third_party/curl/src/tool_filetime.h create mode 100644 third_party/curl/src/tool_findfile.c create mode 100644 third_party/curl/src/tool_findfile.h create mode 100644 third_party/curl/src/tool_formparse.c create mode 100644 third_party/curl/src/tool_formparse.h create mode 100644 third_party/curl/src/tool_getparam.c create mode 100644 third_party/curl/src/tool_getparam.h create mode 100644 third_party/curl/src/tool_getpass.c create mode 100644 third_party/curl/src/tool_getpass.h create mode 100644 third_party/curl/src/tool_help.c create mode 100644 third_party/curl/src/tool_help.h create mode 100644 third_party/curl/src/tool_helpers.c create mode 100644 third_party/curl/src/tool_helpers.h create mode 100644 third_party/curl/src/tool_hugehelp.c create mode 100644 third_party/curl/src/tool_hugehelp.h create mode 100644 third_party/curl/src/tool_ipfs.c create mode 100644 third_party/curl/src/tool_ipfs.h create mode 100644 third_party/curl/src/tool_libinfo.c create mode 100644 third_party/curl/src/tool_libinfo.h create mode 100644 third_party/curl/src/tool_listhelp.c create mode 100644 third_party/curl/src/tool_main.c create mode 100644 third_party/curl/src/tool_main.h create mode 100644 third_party/curl/src/tool_msgs.c create mode 100644 third_party/curl/src/tool_msgs.h create mode 100644 third_party/curl/src/tool_operate.c create mode 100644 third_party/curl/src/tool_operate.h create mode 100644 third_party/curl/src/tool_operhlp.c create mode 100644 third_party/curl/src/tool_operhlp.h create mode 100644 third_party/curl/src/tool_paramhlp.c create mode 100644 third_party/curl/src/tool_paramhlp.h create mode 100644 third_party/curl/src/tool_parsecfg.c create mode 100644 third_party/curl/src/tool_parsecfg.h create mode 100644 third_party/curl/src/tool_progress.c create mode 100644 third_party/curl/src/tool_progress.h create mode 100644 third_party/curl/src/tool_sdecls.h create mode 100644 third_party/curl/src/tool_setopt.c create mode 100644 third_party/curl/src/tool_setopt.h create mode 100644 third_party/curl/src/tool_setup.h create mode 100644 third_party/curl/src/tool_sleep.c create mode 100644 third_party/curl/src/tool_sleep.h create mode 100644 third_party/curl/src/tool_stderr.c create mode 100644 third_party/curl/src/tool_stderr.h create mode 100644 third_party/curl/src/tool_strdup.c create mode 100644 third_party/curl/src/tool_strdup.h create mode 100644 third_party/curl/src/tool_urlglob.c create mode 100644 third_party/curl/src/tool_urlglob.h create mode 100644 third_party/curl/src/tool_util.c create mode 100644 third_party/curl/src/tool_util.h create mode 100644 third_party/curl/src/tool_version.h create mode 100644 third_party/curl/src/tool_vms.c create mode 100644 third_party/curl/src/tool_vms.h create mode 100644 third_party/curl/src/tool_writeout.c create mode 100644 third_party/curl/src/tool_writeout.h create mode 100644 third_party/curl/src/tool_writeout_json.c create mode 100644 third_party/curl/src/tool_writeout_json.h create mode 100644 third_party/curl/src/tool_xattr.c create mode 100644 third_party/curl/src/tool_xattr.h create mode 100644 third_party/curl/src/var.c create mode 100644 third_party/curl/src/var.h create mode 100644 third_party/curlpp/CMakeLists.txt create mode 100644 third_party/curlpp/Readme.md create mode 100644 third_party/curlpp/include/curlpp/Easy.hpp create mode 100644 third_party/curlpp/include/curlpp/Easy.inl create mode 100644 third_party/curlpp/include/curlpp/Exception.hpp create mode 100644 third_party/curlpp/include/curlpp/Form.hpp create mode 100644 third_party/curlpp/include/curlpp/Info.hpp create mode 100644 third_party/curlpp/include/curlpp/Info.inl create mode 100644 third_party/curlpp/include/curlpp/Infos.hpp create mode 100644 third_party/curlpp/include/curlpp/Multi.hpp create mode 100644 third_party/curlpp/include/curlpp/Option.hpp create mode 100644 third_party/curlpp/include/curlpp/Option.inl create mode 100644 third_party/curlpp/include/curlpp/OptionBase.hpp create mode 100644 third_party/curlpp/include/curlpp/Options.hpp create mode 100644 third_party/curlpp/include/curlpp/Types.hpp create mode 100644 third_party/curlpp/include/curlpp/cURLpp.hpp create mode 100644 third_party/curlpp/include/curlpp/internal/CurlHandle.hpp create mode 100644 third_party/curlpp/include/curlpp/internal/CurlHandle.inl create mode 100644 third_party/curlpp/include/curlpp/internal/OptionContainer.hpp create mode 100644 third_party/curlpp/include/curlpp/internal/OptionContainer.inl create mode 100644 third_party/curlpp/include/curlpp/internal/OptionContainerType.hpp create mode 100644 third_party/curlpp/include/curlpp/internal/OptionList.hpp create mode 100644 third_party/curlpp/include/curlpp/internal/OptionSetter.hpp create mode 100644 third_party/curlpp/include/curlpp/internal/OptionSetter.inl create mode 100644 third_party/curlpp/include/curlpp/internal/SList.hpp create mode 100644 third_party/curlpp/include/utilspp/EmptyType.hpp create mode 100644 third_party/curlpp/include/utilspp/NonCopyable.hpp create mode 100644 third_party/curlpp/include/utilspp/NullType.hpp create mode 100644 third_party/curlpp/include/utilspp/SmartPtr.hpp create mode 100644 third_party/curlpp/include/utilspp/ThreadingFactoryMutex.hpp create mode 100644 third_party/curlpp/include/utilspp/ThreadingFactoryMutex.inl create mode 100644 third_party/curlpp/include/utilspp/ThreadingSingle.hpp create mode 100644 third_party/curlpp/include/utilspp/ThreadingSingle.inl create mode 100644 third_party/curlpp/include/utilspp/TypeList.hpp create mode 100644 third_party/curlpp/include/utilspp/TypeTrait.hpp create mode 100644 third_party/curlpp/include/utilspp/clone_ptr.hpp create mode 100644 third_party/curlpp/src/curlpp/Easy.cpp create mode 100644 third_party/curlpp/src/curlpp/Exception.cpp create mode 100644 third_party/curlpp/src/curlpp/Form.cpp create mode 100644 third_party/curlpp/src/curlpp/Info.cpp create mode 100644 third_party/curlpp/src/curlpp/Multi.cpp create mode 100644 third_party/curlpp/src/curlpp/OptionBase.cpp create mode 100644 third_party/curlpp/src/curlpp/Options.cpp create mode 100644 third_party/curlpp/src/curlpp/cURLpp.cpp create mode 100644 third_party/curlpp/src/curlpp/internal/CurlHandle.cpp create mode 100644 third_party/curlpp/src/curlpp/internal/OptionList.cpp create mode 100644 third_party/curlpp/src/curlpp/internal/OptionSetter.cpp create mode 100644 third_party/curlpp/src/curlpp/internal/SList.cpp create mode 100644 third_party/inih/LICENSE.txt create mode 100644 third_party/inih/README.md create mode 100644 third_party/inih/cpp/INIReader.cpp create mode 100644 third_party/inih/cpp/INIReader.h create mode 100644 third_party/inih/ini.c create mode 100644 third_party/inih/ini.h create mode 100644 third_party/minio-cpp/CMakeLists.txt create mode 100644 third_party/minio-cpp/README.md create mode 100644 third_party/minio-cpp/include/miniocpp/args.h create mode 100644 third_party/minio-cpp/include/miniocpp/baseclient.h create mode 100644 third_party/minio-cpp/include/miniocpp/client.h create mode 100644 third_party/minio-cpp/include/miniocpp/config.h create mode 100644 third_party/minio-cpp/include/miniocpp/credentials.h create mode 100644 third_party/minio-cpp/include/miniocpp/error.h create mode 100644 third_party/minio-cpp/include/miniocpp/http.h create mode 100644 third_party/minio-cpp/include/miniocpp/providers.h create mode 100644 third_party/minio-cpp/include/miniocpp/request.h create mode 100644 third_party/minio-cpp/include/miniocpp/response.h create mode 100644 third_party/minio-cpp/include/miniocpp/select.h create mode 100644 third_party/minio-cpp/include/miniocpp/signer.h create mode 100644 third_party/minio-cpp/include/miniocpp/sse.h create mode 100644 third_party/minio-cpp/include/miniocpp/types.h create mode 100644 third_party/minio-cpp/include/miniocpp/utils.h create mode 100644 third_party/minio-cpp/src/args.cc create mode 100644 third_party/minio-cpp/src/baseclient.cc create mode 100644 third_party/minio-cpp/src/client.cc create mode 100644 third_party/minio-cpp/src/credentials.cc create mode 100644 third_party/minio-cpp/src/error.cc create mode 100644 third_party/minio-cpp/src/http.cc create mode 100644 third_party/minio-cpp/src/providers.cc create mode 100644 third_party/minio-cpp/src/request.cc create mode 100644 third_party/minio-cpp/src/response.cc create mode 100644 third_party/minio-cpp/src/select.cc create mode 100644 third_party/minio-cpp/src/signer.cc create mode 100644 third_party/minio-cpp/src/sse.cc create mode 100644 third_party/minio-cpp/src/types.cc create mode 100644 third_party/minio-cpp/src/utils.cc create mode 100644 third_party/nlohmann/json_fwd.hpp create mode 100644 third_party/pugixml/CMakeLists.txt create mode 100644 third_party/pugixml/LICENSE.md create mode 100644 third_party/pugixml/readme.txt create mode 100644 third_party/pugixml/scripts/pugixml-config.cmake.in create mode 100644 third_party/pugixml/scripts/pugixml.pc.in create mode 100644 third_party/pugixml/src/pugiconfig.hpp create mode 100644 third_party/pugixml/src/pugixml.cpp create mode 100644 third_party/pugixml/src/pugixml.hpp diff --git a/scripts/Dockerfile_infinity_builder_centos7 b/scripts/Dockerfile_infinity_builder_centos7 index 3f536cd9c7..837bdcd1be 100644 --- a/scripts/Dockerfile_infinity_builder_centos7 +++ b/scripts/Dockerfile_infinity_builder_centos7 @@ -134,7 +134,7 @@ RUN --mount=type=bind,source=jemalloc-5.3.0.tar.bz2,target=/root/jemalloc-5.3.0. # Install gperftools-2.15 RUN --mount=type=bind,source=gperftools-2.15.tar.gz,target=/root/gperftools-2.15.tar.gz \ cd /root && tar xzf gperftools-2.15.tar.gz \ - && cd gperftools-2.15 && ./configure && make -j 8 && make install \ + && cd gperftools-2.15 && ./configure && make -j && make install \ && ldconfig && cd /root && rm -rf gperftools-2.15 # download https://github.com/risinglightdb/sqllogictest-rs/releases/download/v0.20.2/sqllogictest-bin-v0.20.2-x86_64-unknown-linux-musl.tar.gz @@ -147,8 +147,8 @@ RUN --mount=type=bind,source=sqllogictest-bin-v0.20.2-x86_64-unknown-linux-musl. # This means installing to /usr/local/lib may break app compilation. RUN --mount=type=bind,source=openssl-1.1.1w.tar.gz,target=/root/openssl-1.1.1w.tar.gz \ cd /root && tar xzf openssl-1.1.1w.tar.gz \ - && cd openssl-1.1.1w && ./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl shared \ - && make -j 8 && make install \ + && cd openssl-1.1.1w && ./config --prefix=/usr/local/openssl11 --openssldir=/usr/local/openssl11 shared \ + && make -j && make install \ && ldconfig && cd /root && rm -rf openssl-1.1.1w # packages need by python optional modules: _dbm _gdbm _lzma _sqlite3 _tkinter _uuid @@ -161,27 +161,25 @@ RUN yum install -y gdbm-devel sqlite-devel xz-devel libffi-devel libuuid-devel b # Install Python 3.10 RUN --mount=type=bind,source=Python-3.10.14.tar.xz,target=/root/Python-3.10.14.tar.xz \ cd /root && tar xJf Python-3.10.14.tar.xz \ - && cd Python-3.10.14 && ./configure --with-openssl=/usr/local/openssl --with-openssl-rpath=auto --enable-shared && make -j 8 && make install \ + && cd Python-3.10.14 && ./configure --with-openssl=/usr/local/openssl11 --with-openssl-rpath=auto --enable-shared && make -j && make altinstall \ && ldconfig && cd /root && rm -rf Python-3.10.14 # Install Python 3.11 RUN --mount=type=bind,source=Python-3.11.9.tar.xz,target=/root/Python-3.11.9.tar.xz \ cd /root && tar xJf Python-3.11.9.tar.xz \ -&& cd Python-3.11.9 && ./configure --with-openssl=/usr/local/openssl --with-openssl-rpath=auto --enable-shared && make -j 8 && make install \ +&& cd Python-3.11.9 && ./configure --with-openssl=/usr/local/openssl11 --with-openssl-rpath=auto --enable-shared && make -j && make altinstall \ && ldconfig && cd /root && rm -rf Python-3.11.9 # Install Python 3.12 RUN --mount=type=bind,source=Python-3.12.4.tar.xz,target=/root/Python-3.12.4.tar.xz \ cd /root && tar xJf Python-3.12.4.tar.xz \ -&& cd Python-3.12.4 && ./configure --with-openssl=/usr/local/openssl --with-openssl-rpath=auto --enable-shared && make -j 8 && make install \ +&& cd Python-3.12.4 && ./configure --with-openssl=/usr/local/openssl11 --with-openssl-rpath=auto --enable-shared && make -j && make altinstall \ && ldconfig && cd /root && rm -rf Python-3.12.4 -RUN pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && pip3 config set global.trusted-host "pypi.tuna.tsinghua.edu.cn mirrors.pku.edu.cn" && pip3 config set global.extra-index-url "https://mirrors.pku.edu.cn/pypi/web/simple" +RUN /usr/local/bin/pip3.10 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && /usr/local/bin/pip3.10 config set global.trusted-host "pypi.tuna.tsinghua.edu.cn mirrors.pku.edu.cn" && /usr/local/bin/pip3.10 config set global.extra-index-url "https://mirrors.pku.edu.cn/pypi/web/simple" # Install tools for building and uploading a python wheel. RUN /usr/local/bin/pip3.10 install wheel auditwheel patchelf twine -RUN /usr/local/bin/pip3.11 install wheel auditwheel patchelf twine -RUN /usr/local/bin/pip3.12 install wheel auditwheel patchelf twine # Create a python virtual environment for each python version. Set PATH so that the shell activate a virtual environment automatically when entering a container from this image. RUN --mount=type=bind,source=python/requirements.txt,target=/root/requirements.txt /usr/local/bin/python3.10 -m venv /usr/local/venv310 && source /usr/local/venv310/bin/activate && pip3 install --no-input wheel && pip3 install --no-input -r /root/requirements.txt @@ -201,9 +199,15 @@ RUN --mount=type=bind,source=thrift-0.20.0.tar.gz,target=/root/thrift-0.20.0.tar && cp compiler/cpp/bin/thrift /usr/local/bin/thrift \ && cd /root && rm -rf thrift-0.20.0 -RUN yum -y install expat-static\ +RUN yum -y install expat-static perl-core \ && yum clean all +RUN --mount=type=bind,source=openssl-3.0.15.tar.gz,target=/root/openssl-3.0.15.tar.gz \ + cd /root && tar xzf openssl-3.0.15.tar.gz \ + && cd openssl-3.0.15 && ./config --prefix=/usr/local/openssl30 --openssldir=/usr/local/openssl30 shared zlib \ + && make -j && make install \ + && ldconfig && cd /root && rm -rf openssl-3.0.15 + ENV PATH="/usr/local/venv310/bin:$PATH" ENV CC=clang CXX=clang++ LZ4_ROOT=/usr/local diff --git a/scripts/download_deps_infinity_builder_centos7.sh b/scripts/download_deps_infinity_builder_centos7.sh index 48857a956e..853b6150d5 100644 --- a/scripts/download_deps_infinity_builder_centos7.sh +++ b/scripts/download_deps_infinity_builder_centos7.sh @@ -28,6 +28,7 @@ https://github.com/google/brotli/archive/refs/tags/v1.1.0.tar.gz https://github.com/jemalloc/jemalloc/releases/download/5.3.0/jemalloc-5.3.0.tar.bz2 https://github.com/gperftools/gperftools/releases/download/gperftools-2.15/gperftools-2.15.tar.gz https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz +https://github.com/openssl/openssl/releases/download/openssl-3.0.15/openssl-3.0.15.tar.gz https://dlcdn.apache.org/thrift/0.20.0/thrift-0.20.0.tar.gz https://www.python.org/ftp/python/3.10.14/Python-3.10.14.tar.xz https://www.python.org/ftp/python/3.11.9/Python-3.11.9.tar.xz diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d6cb94a431..e385e0bf08 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,46 +19,46 @@ add_subdirectory(parser) # add_definitions(-mavx2 -mf16c -mpopcnt) if(APPLE) - execute_process(COMMAND sh -c "sysctl -a machdep.cpu.features | grep FMA" - RESULT_VARIABLE SUPPORT_FMA - OUTPUT_QUIET - ERROR_QUIET) - - execute_process(COMMAND sh -c "sysctl -a machdep.cpu.features | grep SSE4.2" - RESULT_VARIABLE SUPPORT_SSE42 - OUTPUT_QUIET - ERROR_QUIET) - - execute_process(COMMAND sh -c "sysctl -a machdep.cpu.features | grep AVX2" - RESULT_VARIABLE SUPPORT_AVX2 - OUTPUT_QUIET - ERROR_QUIET) - - execute_process(COMMAND sh -c "sysctl -a machdep.cpu.features | grep AVX512" - RESULT_VARIABLE SUPPORT_AVX512 - OUTPUT_QUIET - ERROR_QUIET) + execute_process(COMMAND sh -c "sysctl -a machdep.cpu.features | grep FMA" + RESULT_VARIABLE SUPPORT_FMA + OUTPUT_QUIET + ERROR_QUIET) + + execute_process(COMMAND sh -c "sysctl -a machdep.cpu.features | grep SSE4.2" + RESULT_VARIABLE SUPPORT_SSE42 + OUTPUT_QUIET + ERROR_QUIET) + + execute_process(COMMAND sh -c "sysctl -a machdep.cpu.features | grep AVX2" + RESULT_VARIABLE SUPPORT_AVX2 + OUTPUT_QUIET + ERROR_QUIET) + + execute_process(COMMAND sh -c "sysctl -a machdep.cpu.features | grep AVX512" + RESULT_VARIABLE SUPPORT_AVX512 + OUTPUT_QUIET + ERROR_QUIET) else() - #Linux - execute_process(COMMAND grep -q fma /proc/cpuinfo - RESULT_VARIABLE SUPPORT_FMA - OUTPUT_QUIET - ERROR_QUIET) - - execute_process(COMMAND grep -q sse4_2 /proc/cpuinfo - RESULT_VARIABLE SUPPORT_SSE42 - OUTPUT_QUIET - ERROR_QUIET) - - execute_process(COMMAND grep -q avx2 /proc/cpuinfo - RESULT_VARIABLE SUPPORT_AVX2 - OUTPUT_QUIET - ERROR_QUIET) - - execute_process(COMMAND grep -q avx512 /proc/cpuinfo - RESULT_VARIABLE SUPPORT_AVX512 - OUTPUT_QUIET - ERROR_QUIET) + #Linux + execute_process(COMMAND grep -q fma /proc/cpuinfo + RESULT_VARIABLE SUPPORT_FMA + OUTPUT_QUIET + ERROR_QUIET) + + execute_process(COMMAND grep -q sse4_2 /proc/cpuinfo + RESULT_VARIABLE SUPPORT_SSE42 + OUTPUT_QUIET + ERROR_QUIET) + + execute_process(COMMAND grep -q avx2 /proc/cpuinfo + RESULT_VARIABLE SUPPORT_AVX2 + OUTPUT_QUIET + ERROR_QUIET) + + execute_process(COMMAND grep -q avx512 /proc/cpuinfo + RESULT_VARIABLE SUPPORT_AVX512 + OUTPUT_QUIET + ERROR_QUIET) endif() @@ -138,13 +138,13 @@ file(GLOB_RECURSE metrics_cpp CONFIGURE_DEPENDS metrics/*.cpp - ) +) file(GLOB_RECURSE metrics_cppm CONFIGURE_DEPENDS metrics/*.cppm - ) +) file(GLOB_RECURSE planner_cpp @@ -248,6 +248,7 @@ add_dependencies(infinity_core thrift thriftnb parquet_static snappy) target_include_directories(infinity_core PUBLIC ${Python3_INCLUDE_DIRS}) target_include_directories(infinity_core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") target_include_directories(infinity_core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/parser") +target_include_directories(infinity_core PUBLIC "${CMAKE_SOURCE_DIR}/third_party") target_include_directories(infinity_core PUBLIC "${CMAKE_SOURCE_DIR}/third_party/spdlog/include") target_include_directories(infinity_core PUBLIC "${CMAKE_SOURCE_DIR}/third_party/cli11") target_include_directories(infinity_core PUBLIC "${CMAKE_SOURCE_DIR}/third_party/ctpl") @@ -274,6 +275,9 @@ target_include_directories(infinity_core PUBLIC "${CMAKE_SOURCE_DIR}/third_party target_include_directories(infinity_core PUBLIC "${CMAKE_BINARY_DIR}/third_party/snappy/") target_include_directories(infinity_core PUBLIC "${CMAKE_SOURCE_DIR}/third_party/thrift/lib/cpp/src") target_include_directories(infinity_core PUBLIC "${CMAKE_BINARY_DIR}/third_party/thrift/") +target_include_directories(infinity_core PUBLIC "${CMAKE_SOURCE_DIR}/third_party/minio-cpp/include") +target_include_directories(infinity_core PUBLIC "${CMAKE_SOURCE_DIR}/third_party/curlpp/include") +target_include_directories(infinity_core PUBLIC "${CMAKE_SOURCE_DIR}/third_party/curl/include") target_include_directories(infinity_core PUBLIC "${CMAKE_SOURCE_DIR}/third_party/darts/") if (NOT SUPPORT_FMA EQUAL 0) @@ -311,7 +315,7 @@ add_executable(infinity # ${network_cppm} #) -add_dependencies(infinity oatpp) +add_dependencies(infinity oatpp miniocpp) target_link_libraries(infinity infinity_core @@ -330,80 +334,84 @@ target_link_libraries(infinity event.a c++.a c++abi.a -# profiler + # profiler oatpp.a parquet.a arrow.a snappy.a ${JEMALLOC_STATIC_LIB} + miniocpp.a ) target_link_directories(infinity PUBLIC "${CMAKE_BINARY_DIR}/lib") target_link_directories(infinity PUBLIC "${CMAKE_BINARY_DIR}/third_party/oatpp/src/") target_link_directories(infinity PUBLIC "${CMAKE_BINARY_DIR}/third_party/arrow/") target_link_directories(infinity PUBLIC "${CMAKE_BINARY_DIR}/third_party/snappy/") +target_link_directories(infinity PUBLIC "${CMAKE_BINARY_DIR}/third_party/minio-cpp/") target_include_directories(infinity PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") target_include_directories(infinity PUBLIC "${CMAKE_SOURCE_DIR}/third_party/thrift/lib/cpp/src") target_include_directories(infinity PUBLIC "${CMAKE_BINARY_DIR}/third_party/thrift/") if (SKBUILD) -add_library(embedded_infinity_ext MODULE - embedded_infinity_ext.cpp - embedded_infinity/wrap_infinity.cpp -) - -target_sources(embedded_infinity_ext - PRIVATE - FILE_SET core_cxx_modules TYPE CXX_MODULES FILES - embedded_infinity/wrap_infinity.cppm -) - -nanobind_compile_options(embedded_infinity_ext) -nanobind_link_options(embedded_infinity_ext) -set_target_properties(embedded_infinity_ext PROPERTIES LINKER_LANGUAGE CXX) -set(libname "nanobind-static-abi3") -nanobind_build_library(${libname}) -nanobind_extension_abi3(embedded_infinity_ext) -add_dependencies(embedded_infinity_ext oatpp) - -target_link_libraries(embedded_infinity_ext PRIVATE - ${libname} - infinity_core - sql_parser - onnxruntime_mlas - zsv_parser - newpfor - fastpfor - jma - opencc - dl - lz4.a - atomic.a - event.a - oatpp.a - c++.a - c++abi.a - parquet.a - arrow.a - thrift.a - thriftnb.a - snappy.a - ${JEMALLOC_STATIC_LIB} -) - -# WARN: python modules shall not link to static libstdc++!!! -target_link_options(embedded_infinity_ext PRIVATE -static-libgcc) -set_target_properties(embedded_infinity_ext PROPERTIES CXX_VISIBILITY_PRESET hidden) - -target_link_directories(embedded_infinity_ext PUBLIC "${CMAKE_BINARY_DIR}/lib") -target_link_directories(embedded_infinity_ext PUBLIC "${CMAKE_BINARY_DIR}/third_party/oatpp/src/") -target_link_directories(embedded_infinity_ext PUBLIC "${CMAKE_BINARY_DIR}/third_party/arrow/") -target_link_directories(embedded_infinity_ext PUBLIC "${CMAKE_BINARY_DIR}/third_party/snappy/") -nanobind_disable_stack_protector(embedded_infinity_ext) -nanobind_opt_size(embedded_infinity_ext) -nanobind_strip(embedded_infinity_ext) - -install(TARGETS embedded_infinity_ext LIBRARY DESTINATION infinity_embedded) + add_library(embedded_infinity_ext MODULE + embedded_infinity_ext.cpp + embedded_infinity/wrap_infinity.cpp + ) + + target_sources(embedded_infinity_ext + PRIVATE + FILE_SET core_cxx_modules TYPE CXX_MODULES FILES + embedded_infinity/wrap_infinity.cppm + ) + + nanobind_compile_options(embedded_infinity_ext) + nanobind_link_options(embedded_infinity_ext) + set_target_properties(embedded_infinity_ext PROPERTIES LINKER_LANGUAGE CXX) + set(libname "nanobind-static-abi3") + nanobind_build_library(${libname}) + nanobind_extension_abi3(embedded_infinity_ext) + add_dependencies(embedded_infinity_ext oatpp miniocpp) + + target_link_libraries(embedded_infinity_ext PRIVATE + ${libname} + infinity_core + sql_parser + onnxruntime_mlas + zsv_parser + newpfor + fastpfor + jma + opencc + dl + lz4.a + atomic.a + event.a + oatpp.a + c++.a + c++abi.a + parquet.a + arrow.a + thrift.a + thriftnb.a + snappy.a + ${JEMALLOC_STATIC_LIB} + miniocpp.a + ) + + # WARN: python modules shall not link to static libstdc++!!! + target_link_options(embedded_infinity_ext PRIVATE -static-libgcc) + set_target_properties(embedded_infinity_ext PROPERTIES CXX_VISIBILITY_PRESET hidden) + + target_link_directories(embedded_infinity_ext PUBLIC "${CMAKE_BINARY_DIR}/lib") + target_link_directories(embedded_infinity_ext PUBLIC "${CMAKE_BINARY_DIR}/third_party/oatpp/src/") + target_link_directories(embedded_infinity_ext PUBLIC "${CMAKE_BINARY_DIR}/third_party/arrow/") + target_link_directories(embedded_infinity_ext PUBLIC "${CMAKE_BINARY_DIR}/third_party/snappy/") + target_link_directories(embedded_infinity_ext PUBLIC "${CMAKE_BINARY_DIR}/third_party/minio-cpp/") + nanobind_disable_stack_protector(embedded_infinity_ext) + nanobind_opt_size(embedded_infinity_ext) + nanobind_strip(embedded_infinity_ext) + + install(TARGETS embedded_infinity_ext LIBRARY DESTINATION infinity_embedded) endif () ### unit test @@ -502,6 +510,7 @@ add_executable(unit_test ) set_target_properties(unit_test PROPERTIES OUTPUT_NAME test_main) +add_dependencies(unit_test oatpp miniocpp pugixml-static curlpp_static inih libcurl_static) target_link_libraries(unit_test gtest @@ -527,6 +536,13 @@ target_link_libraries(unit_test thrift.a thriftnb.a event.a + miniocpp.a + pugixml-static + curlpp_static + inih.a + libcurl_static + ssl.a + crypto.a ) add_dependencies(unit_test oatpp) @@ -535,6 +551,12 @@ target_link_directories(unit_test PUBLIC "${CMAKE_BINARY_DIR}/lib") target_link_directories(unit_test PUBLIC "${CMAKE_BINARY_DIR}/third_party/arrow/") target_link_directories(unit_test PUBLIC "${CMAKE_BINARY_DIR}/third_party/snappy/") target_link_directories(unit_test PUBLIC "${CMAKE_BINARY_DIR}/third_party/oatpp/src/") +target_link_directories(unit_test PUBLIC "${CMAKE_BINARY_DIR}/third_party/minio-cpp/") +target_link_directories(unit_test PUBLIC "${CMAKE_BINARY_DIR}/third_party/pugixml/") +target_link_directories(unit_test PUBLIC "${CMAKE_BINARY_DIR}/third_party/curlpp/") +target_link_directories(unit_test PUBLIC "${CMAKE_BINARY_DIR}/third_party/curl/") +target_link_directories(unit_test PUBLIC "${CMAKE_BINARY_DIR}/third_party/") +target_link_directories(unit_test PUBLIC "/usr/local/openssl30/lib64") target_sources(unit_test PRIVATE @@ -543,6 +565,7 @@ target_sources(unit_test ) target_include_directories(unit_test PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") +target_include_directories(unit_test PUBLIC "${CMAKE_SOURCE_DIR}/third_party") target_include_directories(unit_test PUBLIC "${CMAKE_SOURCE_DIR}/third_party/spdlog/include") target_include_directories(unit_test PUBLIC "${CMAKE_SOURCE_DIR}/third_party/ctpl") target_include_directories(unit_test PUBLIC "${CMAKE_SOURCE_DIR}/third_party/tomlplusplus") @@ -556,9 +579,10 @@ target_include_directories(unit_test PUBLIC "${CMAKE_BINARY_DIR}/third_party/thr target_include_directories(unit_test PUBLIC "${CMAKE_SOURCE_DIR}/third_party/pgm/include") target_include_directories(unit_test PUBLIC "${CMAKE_SOURCE_DIR}/third_party/googletest/googletest/include") target_include_directories(unit_test PUBLIC "${CMAKE_SOURCE_DIR}/third_party/opencc") +target_include_directories(unit_test PUBLIC "${CMAKE_SOURCE_DIR}/third_party/curlpp/include") +target_include_directories(unit_test PUBLIC "${CMAKE_SOURCE_DIR}/third_party/curl/include") target_include_directories(unit_test PUBLIC "${CMAKE_SOURCE_DIR}/third_party/darts") - # target_compile_options(unit_test PRIVATE $<$:-mavx2 -mfma -mf16c -mpopcnt>) if (SUPPORT_AVX2 EQUAL 0 OR SUPPORT_AVX512 EQUAL 0) message("Compiled by AVX2 or AVX512") diff --git a/src/storage/io/s3_client.cppm b/src/storage/io/s3_client.cppm new file mode 100644 index 0000000000..b08f901cd5 --- /dev/null +++ b/src/storage/io/s3_client.cppm @@ -0,0 +1,33 @@ +module; + +import stl; +#include + +export module s3_client; + +namespace infinity { + +export class S3Client { + public: + S3Client(String _url = "http://localhost:9000", + bool _https = false, + String _access_key = "minioadmin", + String _secret_key = "minioadmin") : url(_url), https(_https), access_key(_access_key), secret_key(_secret_key) {} + + ~S3Client() = default; + + virtual void DownloadObject(const String & bucket_name, const String &object_name, const String &file_path)=0; + + virtual void UploadObject(const String & bucket_name, const String &object_name, const String &file_path)=0; + + virtual void RemoveObject(const String & bucket_name, const String &object_name)=0; + + virtual void CopyObject(const String & src_bucket_name, const String &src_object_name, const String & dst_bucket_name, const String &dst_object_name)=0; + + protected: + String url; + bool https; + String access_key; + String secret_key; +}; +} \ No newline at end of file diff --git a/src/storage/io/s3_client_minio.cpp b/src/storage/io/s3_client_minio.cpp new file mode 100644 index 0000000000..c59d2cd1ce --- /dev/null +++ b/src/storage/io/s3_client_minio.cpp @@ -0,0 +1,98 @@ +module; + +import stl; +import infinity_exception; +#include + +module s3_client_minio; + +namespace infinity { +void S3ClientMinio::DownloadObject(const String & bucket_name, const String &object_name, const String &file_path){ + // Create S3 client. + minio::s3::Client client = GetClient(); + + // Create download object arguments. + minio::s3::DownloadObjectArgs args; + args.bucket = bucket_name; + args.object = object_name; + args.filename = file_path; + + // Call download object. + minio::s3::DownloadObjectResponse resp = client.DownloadObject(args); + + // Handle response. + if (resp) { + std::cout << object_name<<" is successfully downloaded to "< + +export module s3_client_minio; + +namespace infinity { + +class S3ClientMinio final: public S3Client { + public: + S3ClientMinio(String _url = "http://localhost:9000", + bool _https = false, + String _access_key = "minioadmin", + String _secret_key = "minioadmin") : S3Client(_url, _https, _access_key, _secret_key), + base_url(_url, _https), + provider(_access_key, _secret_key) {} + + ~S3ClientMinio() = default; + + void DownloadObject(const String & bucket_name, const String &object_name, const String &file_path); + + void UploadObject(const String & bucket_name, const String &object_name, const String &file_path); + + void RemoveObject(const String & bucket_name, const String &object_name); + + void CopyObject(const String & src_bucket_name, const String &src_object_name, const String & dst_bucket_name, const String &dst_object_name); + + minio::s3::Client GetClient() { return minio::s3::Client(base_url, &provider); } + + private: + minio::s3::BaseUrl base_url; + minio::creds::StaticProvider provider; +}; +} \ No newline at end of file diff --git a/src/storage/io/s3_file_system.cpp b/src/storage/io/s3_file_system.cpp new file mode 100644 index 0000000000..574ead262c --- /dev/null +++ b/src/storage/io/s3_file_system.cpp @@ -0,0 +1,478 @@ +module; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +import stl; +import file_system; +import file_system_type; + +import infinity_exception; +import third_party; +import logger; +import status; + +module s3_file_system; + +namespace infinity { + +namespace fs = std::filesystem; + +constexpr std::size_t BUFFER_SIZE = 4096; // Adjust buffer size as needed + +std::mutex S3FileSystem::mtx_{}; +HashMap S3FileSystem::mapped_files_{}; + +S3FileHandler::~S3FileHandler() { + if (fd_ != -1) { + int ret = close(fd_); + if (ret != 0) { + String error_message = fmt::format("Close file failed: {}", strerror(errno)); + UnrecoverableError(error_message); + } + fd_ = -1; + } +} + +Pair, Status> S3FileSystem::OpenFile(const String &path, u8 flags, FileLockType lock_type) { + if (!std::filesystem::path(path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", path); + UnrecoverableError(error_message); + } + + i32 file_flags{O_RDWR}; + bool read_flag = flags & FileFlags::READ_FLAG; + bool write_flag = flags & FileFlags::WRITE_FLAG; + if (read_flag && write_flag) { + file_flags = O_RDWR; + } else if (read_flag) { + file_flags = O_RDONLY; + } else if (write_flag) { + file_flags = O_WRONLY; + } else { + return {nullptr, Status::InvalidFileFlag(file_flags)}; + } + + if (write_flag) { + file_flags |= O_CLOEXEC; // Close fd when fork the process + if (flags & FileFlags::CREATE_FLAG) { + file_flags |= O_CREAT; + } else if (flags & FileFlags::TRUNCATE_CREATE) { + file_flags |= O_CREAT | O_TRUNC; + } + + if (flags & FileFlags::APPEND_FLAG) { + file_flags |= O_APPEND; + } +#if defined(__linux__) + if (flags & FileFlags::DIRECT_IO) { + file_flags |= O_DIRECT; + } +#endif + } + + i32 fd = open(path.c_str(), file_flags, 0666); + if (fd == -1) { + return {nullptr, Status::IOError(fmt::format("Can't open file: {}: {}", path, strerror(errno)))}; + } + // LOG_TRACE(fmt::format("[+] OPEN FILE: {}", path)); + + if (lock_type != FileLockType::kNoLock) { + struct flock file_lock {}; + memset(&file_lock, 0, sizeof(file_lock)); + if (lock_type == FileLockType::kReadLock) { + file_lock.l_type = F_RDLCK; + } else { + file_lock.l_type = F_WRLCK; + } + file_lock.l_whence = SEEK_SET; + file_lock.l_start = 0; + file_lock.l_len = 0; + if (fcntl(fd, F_SETLK, &file_lock) == -1) { + return {nullptr, Status::LockFileError(path, strerror(errno))}; + } + } + return {MakeUnique(*this, path, fd), Status::OK()}; +} + +void S3FileSystem::Close(FileHandler &file_handler) { + auto s3_file_handler = dynamic_cast(&file_handler); + i32 fd = s3_file_handler->fd_; + // set fd to -1 so that destructor of `S3FileHandler` will not close the file. + s3_file_handler->fd_ = -1; + if (close(fd) != 0) { + String error_message = fmt::format("Can't close file: {}: {}", file_handler.path_.string(), strerror(errno)); + UnrecoverableError(error_message); + } + // LOG_TRACE(fmt::format("[-] CLOSE FILE: {}", file_handler.path_.string())); +} + +void S3FileSystem::Rename(const String &old_path, const String &new_path) { + if (!std::filesystem::path(old_path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", old_path); + UnrecoverableError(error_message); + } + if (!std::filesystem::path(new_path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", new_path); + UnrecoverableError(error_message); + } + if (rename(old_path.c_str(), new_path.c_str()) != 0) { + String error_message = fmt::format("Can't rename file: {}, {}", old_path, strerror(errno)); + UnrecoverableError(error_message); + } +} + +i64 S3FileSystem::Read(FileHandler &file_handler, void *data, u64 nbytes) { + i32 fd = ((S3FileHandler &)file_handler).fd_; + i64 readen = 0; + while (readen < (i64)nbytes) { + SizeT a = nbytes - readen; + i64 read_count = read(fd, (char *)data + readen, a); + if (read_count == 0) { + break; + } + if (read_count == -1) { + String error_message = fmt::format("Can't read file: {}: {}", file_handler.path_.string(), strerror(errno)); + UnrecoverableError(error_message); + } + readen += read_count; + } + return readen; +} + +i64 S3FileSystem::Write(FileHandler &file_handler, const void *data, u64 nbytes) { + i32 fd = ((S3FileHandler &)file_handler).fd_; + i64 written = 0; + while (written < (i64)nbytes) { + i64 write_count = write(fd, (char *)data + written, nbytes - written); + if (write_count == -1) { + String error_message = fmt::format("Can't write file: {}: {}. fd: {}", file_handler.path_.string(), strerror(errno), fd); + UnrecoverableError(error_message); + } + written += write_count; + } + return written; +} + +i64 S3FileSystem::ReadAt(FileHandler &file_handler, i64 file_offset, void *data, u64 nbytes) { + i32 fd = ((S3FileHandler &)file_handler).fd_; + i64 readen = 0; + while (readen < (i64)nbytes) { + i64 read_count = pread(fd, (char *)data + readen, nbytes - readen, file_offset + readen); + if (read_count == 0) { + break; + } + if (read_count == -1) { + String error_message = fmt::format("Can't read file: {}: {}", file_handler.path_.string(), strerror(errno)); + UnrecoverableError(error_message); + } + readen += read_count; + } + return readen; +} + +i64 S3FileSystem::WriteAt(FileHandler &file_handler, i64 file_offset, const void *data, u64 nbytes) { + i32 fd = ((S3FileHandler &)file_handler).fd_; + i64 written = 0; + while (written < (i64)nbytes) { + i64 write_count = pwrite(fd, (char *)data + written, nbytes - written, file_offset + written); + if (write_count == -1) { + String error_message = fmt::format("Can't write file: {}: {}. fd: {}", file_handler.path_.string(), strerror(errno), fd); + UnrecoverableError(error_message); + } + written += write_count; + } + return written; +} + +void S3FileSystem::Seek(FileHandler &file_handler, i64 pos) { + i32 fd = ((S3FileHandler &)file_handler).fd_; + if ((off_t)-1 == lseek(fd, pos, SEEK_SET)) { + String error_message = fmt::format("Can't seek file: {}: {}", file_handler.path_.string(), strerror(errno)); + UnrecoverableError(error_message); + } +} + +SizeT S3FileSystem::GetFileSize(FileHandler &file_handler) { + i32 fd = ((S3FileHandler &)file_handler).fd_; + struct stat s {}; + if (fstat(fd, &s) == -1) { + return -1; + } + return s.st_size; +} + +void S3FileSystem::DeleteFile(const String &file_name) { + if (!std::filesystem::path(file_name).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", file_name); + UnrecoverableError(error_message); + } + std::error_code error_code; + Path p{file_name}; + bool is_deleted = std::filesystem::remove(p, error_code); + if (error_code.value() == 0) { + if (!is_deleted) { + LOG_WARN(fmt::format("Failed to delete file: {}: {}", file_name, strerror(errno))); + } + } else { + String error_message = fmt::format("Delete file {} exception: {}", file_name, strerror(errno)); + UnrecoverableError(error_message); + } +} + +void S3FileSystem::SyncFile(FileHandler &file_handler) { + i32 fd = ((S3FileHandler &)file_handler).fd_; + if (fsync(fd) != 0) { + String error_message = fmt::format("fsync failed: {}, {}", file_handler.path_.string(), strerror(errno)); + UnrecoverableError(error_message); + } +} + +void S3FileSystem::AppendFile(const String &dst_path, const String &src_path) { + if (!std::filesystem::path(dst_path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", dst_path); + UnrecoverableError(error_message); + } + if (!std::filesystem::path(src_path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", src_path); + UnrecoverableError(error_message); + } + Path dst{dst_path}; + Path src{src_path}; + std::ifstream srcFile(src, std::ios::binary); + if (!srcFile.is_open()) { + String error_message = fmt::format("Failed to open source file {}", src_path); + UnrecoverableError(error_message); + return; + } + std::ofstream dstFile(dst, std::ios::binary | std::ios::app); + if (!dstFile.is_open()) { + String error_message = fmt::format("Failed to open destination file {}", dst_path); + UnrecoverableError(error_message); + return; + } + char buffer[BUFFER_SIZE]; + while (srcFile.read(buffer, BUFFER_SIZE)) { + dstFile.write(buffer, srcFile.gcount()); + } + dstFile.write(buffer, srcFile.gcount()); + srcFile.close(); + dstFile.close(); +} + +void S3FileSystem::Truncate(const String &file_name, SizeT length) { + if (!std::filesystem::path(file_name).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", file_name); + UnrecoverableError(error_message); + } + std::error_code error_code; + std::filesystem::resize_file(file_name, length, error_code); + if (error_code.value() != 0) { + String error_message = fmt::format("Failed to truncate {} to size {}", file_name, strerror(errno)); + UnrecoverableError(error_message); + } +} + +// Directory related methods +bool S3FileSystem::Exists(const String &path) { + if(!std::filesystem::path(path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", path); + UnrecoverableError(error_message); + } + std::error_code error_code; + Path p{path}; + bool is_exists = std::filesystem::exists(p, error_code); + if (error_code.value() == 0) { + return is_exists; + } else { + String error_message = fmt::format("{} exists exception: {}", path, strerror(errno)); + UnrecoverableError(error_message); + } + return false; +} + +bool S3FileSystem::CreateDirectoryNoExp(const String &path) { + if(!std::filesystem::path(path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", path); + UnrecoverableError(error_message); + } + std::error_code error_code; + Path p{path}; + return std::filesystem::create_directories(p, error_code); +} + +void S3FileSystem::CreateDirectory(const String &path) { + if(!std::filesystem::path(path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", path); + UnrecoverableError(error_message); + } + std::error_code error_code; + Path p{path}; + std::filesystem::create_directories(p, error_code); + if (error_code.value() != 0) { + String error_message = fmt::format("{} create exception: {}", path, strerror(errno)); + UnrecoverableError(error_message); + } +} + +u64 S3FileSystem::DeleteDirectory(const String &path) { + if(!std::filesystem::path(path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", path); + UnrecoverableError(error_message); + } + + std::error_code error_code; + Path p{path}; + u64 removed_count = std::filesystem::remove_all(p, error_code); + if (error_code.value() != 0) { + String error_message = fmt::format("Delete directory {} exception: {}", path, error_code.message()); + UnrecoverableError(error_message); + } + return removed_count; +} + +void S3FileSystem::CleanupDirectory(const String &path) { + if(!std::filesystem::path(path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", path); + UnrecoverableError(error_message); + } + std::error_code error_code; + Path p{path}; + if (!fs::exists(p)) { + std::filesystem::create_directories(p, error_code); + if (error_code.value() != 0) { + String error_message = fmt::format("CleanupDirectory create {} exception: {}", path, error_code.message()); + UnrecoverableError(error_message); + } + return; + } + + try { + std::ranges::for_each(std::filesystem::directory_iterator{path}, [&](const auto &dir_entry) { std::filesystem::remove_all(dir_entry); }); + } catch (const std::filesystem::filesystem_error &e) { + String error_message = fmt::format("CleanupDirectory cleanup {} exception: {}", path, e.what()); + UnrecoverableError(error_message); + } +} + +Vector> S3FileSystem::ListDirectory(const String &path) { + if(!std::filesystem::path(path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", path); + UnrecoverableError(error_message); + } + Path dir_path(path); + if (!is_directory(dir_path)) { + String error_message = fmt::format("{} isn't a directory", path); + UnrecoverableError(error_message); + } + + Vector> file_array; + std::ranges::for_each(std::filesystem::directory_iterator{path}, + [&](const auto &dir_entry) { file_array.emplace_back(MakeShared(dir_entry)); }); + return file_array; +} + +String S3FileSystem::GetAbsolutePath(const String &path) { + if(!std::filesystem::path(path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", path); + UnrecoverableError(error_message); + } + Path p{path}; + return std::filesystem::absolute(p).string(); +} + +u64 S3FileSystem::GetFileSizeByPath(const String &path) { + if(!std::filesystem::path(path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", path); + UnrecoverableError(error_message); + } + return std::filesystem::file_size(path); +} + +u64 S3FileSystem::GetFolderSizeByPath(const String &path) { + if(!std::filesystem::path(path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", path); + UnrecoverableError(error_message); + } + u64 totalSize = 0; + + for (const auto &entry : std::filesystem::recursive_directory_iterator(path)) { + if (std::filesystem::is_regular_file(entry.status())) { + totalSize += std::filesystem::file_size(entry); + } + } + + return totalSize; +} + +String S3FileSystem::ConcatenateFilePath(const String &dir_path, const String &file_path) { + std::filesystem::path full_path = std::filesystem::path(dir_path) / file_path; + return full_path.string(); +} + +int S3FileSystem::MmapFile(const String &file_path, u8 *&data_ptr, SizeT &data_len) { + if (!std::filesystem::path(file_path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", file_path); + UnrecoverableError(error_message); + } + data_ptr = nullptr; + data_len = 0; + std::lock_guard lock(mtx_); + auto it = mapped_files_.find(file_path); + if (it != mapped_files_.end()) { + auto &mmap_info = it->second; + data_ptr = mmap_info.data_ptr_; + data_len = mmap_info.data_len_; + mmap_info.rc_++; + return 0; + } + long len_f = fs::file_size(file_path); + if (len_f == 0) + return -1; + int f = open(file_path.c_str(), O_RDONLY); + void *tmpd = mmap(NULL, len_f, PROT_READ, MAP_SHARED, f, 0); + if (tmpd == MAP_FAILED) + return -1; + close(f); + int rc = madvise(tmpd, + len_f, + MADV_NORMAL +#if defined(linux) || defined(__linux) || defined(__linux__) + | MADV_DONTDUMP +#endif + ); + if (rc < 0) + return -1; + data_ptr = (u8 *)tmpd; + data_len = len_f; + mapped_files_.emplace(file_path, MmapInfo{data_ptr, data_len, 1}); + return 0; +} + +int S3FileSystem::MunmapFile(const String &file_path) { + if(!std::filesystem::path(file_path).is_absolute()) { + String error_message = fmt::format("{} isn't absolute path.", file_path); + UnrecoverableError(error_message); + } + std::lock_guard lock(mtx_); + auto it = mapped_files_.find(file_path); + if (it == mapped_files_.end()) { + return -1; + } + auto &mmap_info = it->second; + mmap_info.rc_--; + if (mmap_info.rc_ == 0) { + munmap(mmap_info.data_ptr_, mmap_info.data_len_); + mapped_files_.erase(it); + } + return 0; +} + +} // namespace infinity \ No newline at end of file diff --git a/src/storage/io/s3_file_system.cppm b/src/storage/io/s3_file_system.cppm new file mode 100644 index 0000000000..f3e206b0c6 --- /dev/null +++ b/src/storage/io/s3_file_system.cppm @@ -0,0 +1,98 @@ +module; + +#include +import stl; +import file_system; +import file_system_type; +import status; +import infinity_exception; +import s3_client; + +export module s3_file_system; + +namespace infinity { + +export class S3FileHandler : public FileHandler { +public: + S3FileHandler(FileSystem &file_system, String path, i32 fd) : FileHandler(file_system, std::move(path)), fd_(fd) {} + + ~S3FileHandler() override; + +public: + i32 fd_{-1}; +}; + +export struct MmapInfo { + u8 *data_ptr_{}; + SizeT data_len_{}; + SizeT rc_{}; +}; + +export class S3FileSystem final : public FileSystem { +public: + S3FileSystem(S3Client& _s3_client) : + FileSystem(FileSystemType::kS3), s3_client(_s3_client) {} + + ~S3FileSystem() override = default; + + Pair, Status> OpenFile(const String &path, u8 flags, FileLockType lock_type) final; + + i64 Read(FileHandler &file_handler, void *data, u64 nbytes) final; + + i64 Write(FileHandler &file_handler, const void *data, u64 nbytes) final; + + i64 ReadAt(FileHandler &file_handler, i64 file_offset, void *data, u64 nbytes) final; + + i64 WriteAt(FileHandler &file_handler, i64 file_offset, const void *data, u64 nbytes) final; + + void Rename(const String &old_path, const String &new_path) final; + + void Seek(FileHandler &file_handler, i64 pos) final; + + SizeT GetFileSize(FileHandler &file_handler) final; + + void DeleteFile(const String &file_name) final; + + void SyncFile(FileHandler &file_handler) final; + + void Close(FileHandler &file_handler) final; + + void AppendFile(const String &dst_path, const String &src_path) final; + + void Truncate(const String &file_name, SizeT length); + + // Directory related methods + bool Exists(const String &path) final; // if file or directory exists + + void CreateDirectory(const String &path) final; + + // return true if successfully created directory + bool CreateDirectoryNoExp(const String &path); + + u64 DeleteDirectory(const String &path) final; + + // Remove everything recursively under given directory. Create it if it doesn't exist. + void CleanupDirectory(const String &path) final; + + Vector> ListDirectory(const String &path) final; + + String GetAbsolutePath(const String &path); + + static u64 GetFileSizeByPath(const String& path); + + static u64 GetFolderSizeByPath(const String& path); + + static String ConcatenateFilePath(const String& dir_path, const String& file_path); + + int MmapFile(const String &file_path, u8 *&data_ptr, SizeT &data_len); + + int MunmapFile(const String &file_path); + +private: + static std::mutex mtx_; + static HashMap mapped_files_; + + S3Client& s3_client; +}; + +} // namespace infinity \ No newline at end of file diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index eb50b889ea..a524c3bf61 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -76,4 +76,13 @@ add_subdirectory(ijma) ################################################################################ add_subdirectory(opencc) +add_subdirectory(minio-cpp) +add_subdirectory(curlpp) +add_subdirectory(pugixml) +add_library(inih + inih/cpp/INIReader.cpp + inih/ini.c +) + +add_subdirectory(curl) \ No newline at end of file diff --git a/third_party/curl/CMake/CMakeConfigurableFile.in b/third_party/curl/CMake/CMakeConfigurableFile.in new file mode 100644 index 0000000000..a3d2bc4a28 --- /dev/null +++ b/third_party/curl/CMake/CMakeConfigurableFile.in @@ -0,0 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +@CMAKE_CONFIGURABLE_FILE_CONTENT@ diff --git a/third_party/curl/CMake/CurlSymbolHiding.cmake b/third_party/curl/CMake/CurlSymbolHiding.cmake new file mode 100644 index 0000000000..07f4fc0b6d --- /dev/null +++ b/third_party/curl/CMake/CurlSymbolHiding.cmake @@ -0,0 +1,85 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +include(CheckCSourceCompiles) + +option(CURL_HIDDEN_SYMBOLS "Set to ON to hide libcurl internal symbols (=hide all symbols that aren't officially external)." ON) +mark_as_advanced(CURL_HIDDEN_SYMBOLS) + +if(WIN32 AND (ENABLE_DEBUG OR ENABLE_CURLDEBUG)) + # We need to export internal debug functions, + # e.g. curl_easy_perform_ev() or curl_dbg_*(), + # so disable symbol hiding for debug builds and for memory tracking. + set(CURL_HIDDEN_SYMBOLS OFF) +endif() + +if(CURL_HIDDEN_SYMBOLS) + set(SUPPORTS_SYMBOL_HIDING FALSE) + + if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT MSVC) + set(SUPPORTS_SYMBOL_HIDING TRUE) + set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))") + set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") + elseif(CMAKE_COMPILER_IS_GNUCC) + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.4) + # note: this is considered buggy prior to 4.0 but the autotools don't care, so let's ignore that fact + set(SUPPORTS_SYMBOL_HIDING TRUE) + set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))") + set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") + endif() + elseif(CMAKE_C_COMPILER_ID MATCHES "SunPro" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.0) + set(SUPPORTS_SYMBOL_HIDING TRUE) + set(_SYMBOL_EXTERN "__global") + set(_CFLAG_SYMBOLS_HIDE "-xldscope=hidden") + elseif(CMAKE_C_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.0) + # note: this should probably just check for version 9.1.045 but I'm not 100% sure + # so let's do it the same way autotools do. + set(SUPPORTS_SYMBOL_HIDING TRUE) + set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))") + set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") + check_c_source_compiles("#include + int main (void) { printf(\"icc fvisibility bug test\"); return 0; }" _no_bug) + if(NOT _no_bug) + set(SUPPORTS_SYMBOL_HIDING FALSE) + set(_SYMBOL_EXTERN "") + set(_CFLAG_SYMBOLS_HIDE "") + endif() + elseif(MSVC) + set(SUPPORTS_SYMBOL_HIDING TRUE) + endif() + + set(HIDES_CURL_PRIVATE_SYMBOLS ${SUPPORTS_SYMBOL_HIDING}) +elseif(MSVC) + if(NOT CMAKE_VERSION VERSION_LESS 3.7) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) #present since 3.4.3 but broken + set(HIDES_CURL_PRIVATE_SYMBOLS FALSE) + else() + message(WARNING "Hiding private symbols regardless CURL_HIDDEN_SYMBOLS being disabled.") + set(HIDES_CURL_PRIVATE_SYMBOLS TRUE) + endif() +else() + set(HIDES_CURL_PRIVATE_SYMBOLS FALSE) +endif() + +set(CURL_CFLAG_SYMBOLS_HIDE ${_CFLAG_SYMBOLS_HIDE}) +set(CURL_EXTERN_SYMBOL ${_SYMBOL_EXTERN}) diff --git a/third_party/curl/CMake/CurlTests.c b/third_party/curl/CMake/CurlTests.c new file mode 100644 index 0000000000..f04abd79b2 --- /dev/null +++ b/third_party/curl/CMake/CurlTests.c @@ -0,0 +1,430 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#ifdef HAVE_FCNTL_O_NONBLOCK +/* headers for FCNTL_O_NONBLOCK test */ +#include +#include +#include +/* */ +#if defined(sun) || defined(__sun__) || \ + defined(__SUNPRO_C) || defined(__SUNPRO_CC) +# if defined(__SVR4) || defined(__srv4__) +# define PLATFORM_SOLARIS +# else +# define PLATFORM_SUNOS4 +# endif +#endif +#if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX41) +# define PLATFORM_AIX_V3 +#endif +/* */ +#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) +#error "O_NONBLOCK does not work on this platform" +#endif + +int main(void) +{ + /* O_NONBLOCK source test */ + int flags = 0; + if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK)) + return 1; + return 0; +} +#endif + +/* tests for gethostbyname_r */ +#if defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) || \ + defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \ + defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT) +# define _REENTRANT + /* no idea whether _REENTRANT is always set, just invent a new flag */ +# define TEST_GETHOSTBYFOO_REENTRANT +#endif +#if defined(HAVE_GETHOSTBYNAME_R_3) || \ + defined(HAVE_GETHOSTBYNAME_R_5) || \ + defined(HAVE_GETHOSTBYNAME_R_6) || \ + defined(TEST_GETHOSTBYFOO_REENTRANT) +#include +#include +int main(void) +{ + char *address = "example.com"; + int length = 0; + int type = 0; + struct hostent h; + int rc = 0; +#if defined(HAVE_GETHOSTBYNAME_R_3) || \ + defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) + struct hostent_data hdata; +#elif defined(HAVE_GETHOSTBYNAME_R_5) || \ + defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \ + defined(HAVE_GETHOSTBYNAME_R_6) || \ + defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT) + char buffer[8192]; + int h_errnop; + struct hostent *hp; +#endif + +#if defined(HAVE_GETHOSTBYNAME_R_3) || \ + defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) + rc = gethostbyname_r(address, &h, &hdata); +#elif defined(HAVE_GETHOSTBYNAME_R_5) || \ + defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) + rc = gethostbyname_r(address, &h, buffer, 8192, &h_errnop); + (void)hp; /* not used for test */ +#elif defined(HAVE_GETHOSTBYNAME_R_6) || \ + defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT) + rc = gethostbyname_r(address, &h, buffer, 8192, &hp, &h_errnop); +#endif + + (void)length; + (void)type; + (void)rc; + return 0; +} +#endif + +#ifdef HAVE_IN_ADDR_T +#include +#include +#include +int main(void) +{ + if((in_addr_t *) 0) + return 0; + if(sizeof(in_addr_t)) + return 0; + ; + return 0; +} +#endif + +#ifdef HAVE_BOOL_T +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDBOOL_H +#include +#endif +int main(void) +{ + if(sizeof(bool *)) + return 0; + ; + return 0; +} +#endif + +#ifdef STDC_HEADERS +#include +#include +#include +#include +int main(void) { return 0; } +#endif + +#ifdef HAVE_FILE_OFFSET_BITS +#ifdef _FILE_OFFSET_BITS +#undef _FILE_OFFSET_BITS +#endif +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int main(void) { ; return 0; } +#endif + +#ifdef HAVE_IOCTLSOCKET +/* includes start */ +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#endif +int main(void) +{ + /* ioctlsocket source code */ + int socket; + unsigned long flags = ioctlsocket(socket, FIONBIO, &flags); + ; + return 0; +} + +#endif + +#ifdef HAVE_IOCTLSOCKET_CAMEL +/* includes start */ +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#endif +int main(void) +{ + /* IoctlSocket source code */ + if(0 != IoctlSocket(0, 0, 0)) + return 1; + ; + return 0; +} +#endif + +#ifdef HAVE_IOCTLSOCKET_CAMEL_FIONBIO +/* includes start */ +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#endif +int main(void) +{ + /* IoctlSocket source code */ + long flags = 0; + if(0 != IoctlSocket(0, FIONBIO, &flags)) + return 1; + ; + return 0; +} +#endif + +#ifdef HAVE_IOCTLSOCKET_FIONBIO +/* includes start */ +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#endif +int main(void) +{ + unsigned long flags = 0; + if(0 != ioctlsocket(0, FIONBIO, &flags)) + return 1; + ; + return 0; +} +#endif + +#ifdef HAVE_IOCTL_FIONBIO +/* headers for FIONBIO test */ +/* includes start */ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_STROPTS_H +# include +#endif +int main(void) +{ + int flags = 0; + if(0 != ioctl(0, FIONBIO, &flags)) + return 1; + ; + return 0; +} +#endif + +#ifdef HAVE_IOCTL_SIOCGIFADDR +/* headers for FIONBIO test */ +/* includes start */ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_STROPTS_H +# include +#endif +#include +int main(void) +{ + struct ifreq ifr; + if(0 != ioctl(0, SIOCGIFADDR, &ifr)) + return 1; + ; + return 0; +} +#endif + +#ifdef HAVE_SETSOCKOPT_SO_NONBLOCK +/* includes start */ +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#endif +/* includes start */ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +/* includes end */ +int main(void) +{ + if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0)) + return 1; + ; + return 0; +} +#endif + +#ifdef HAVE_GLIBC_STRERROR_R +#include +#include + +void check(char c) {} + +int main(void) +{ + char buffer[1024]; + /* This will not compile if strerror_r does not return a char* */ + check(strerror_r(EACCES, buffer, sizeof(buffer))[0]); + return 0; +} +#endif + +#ifdef HAVE_POSIX_STRERROR_R +#include +#include + +/* float, because a pointer can't be implicitly cast to float */ +void check(float f) {} + +int main(void) +{ + char buffer[1024]; + /* This will not compile if strerror_r does not return an int */ + check(strerror_r(EACCES, buffer, sizeof(buffer))); + return 0; +} +#endif + +#ifdef HAVE_FSETXATTR_6 +#include /* header from libc, not from libattr */ +int main(void) +{ + fsetxattr(0, 0, 0, 0, 0, 0); + return 0; +} +#endif + +#ifdef HAVE_FSETXATTR_5 +#include /* header from libc, not from libattr */ +int main(void) +{ + fsetxattr(0, 0, 0, 0, 0); + return 0; +} +#endif + +#ifdef HAVE_CLOCK_GETTIME_MONOTONIC +#include +int main(void) +{ + struct timespec ts = {0, 0}; + clock_gettime(CLOCK_MONOTONIC, &ts); + return 0; +} +#endif + +#ifdef HAVE_BUILTIN_AVAILABLE +int main(void) +{ + if(__builtin_available(macOS 10.12, iOS 5.0, *)) {} + return 0; +} +#endif + +#ifdef HAVE_ATOMIC +/* includes start */ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_STDATOMIC_H +# include +#endif +/* includes end */ + +int main(void) +{ + _Atomic int i = 1; + i = 0; /* Force an atomic-write operation. */ + return i; +} +#endif + +#ifdef HAVE_WIN32_WINNT +/* includes start */ +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# ifndef NOGDI +# define NOGDI +# endif +# include +#endif +/* includes end */ + +#define enquote(x) #x +#define expand(x) enquote(x) +#pragma message("_WIN32_WINNT=" expand(_WIN32_WINNT)) + +int main(void) +{ + return 0; +} +#endif diff --git a/third_party/curl/CMake/FindBearSSL.cmake b/third_party/curl/CMake/FindBearSSL.cmake new file mode 100644 index 0000000000..653ca9dde9 --- /dev/null +++ b/third_party/curl/CMake/FindBearSSL.cmake @@ -0,0 +1,32 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +find_path(BEARSSL_INCLUDE_DIRS bearssl.h) + +find_library(BEARSSL_LIBRARY bearssl) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(BEARSSL DEFAULT_MSG + BEARSSL_INCLUDE_DIRS BEARSSL_LIBRARY) + +mark_as_advanced(BEARSSL_INCLUDE_DIRS BEARSSL_LIBRARY) diff --git a/third_party/curl/CMake/FindBrotli.cmake b/third_party/curl/CMake/FindBrotli.cmake new file mode 100644 index 0000000000..7f316aaa1d --- /dev/null +++ b/third_party/curl/CMake/FindBrotli.cmake @@ -0,0 +1,43 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +include(FindPackageHandleStandardArgs) + +find_path(BROTLI_INCLUDE_DIR "brotli/decode.h") + +find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon) +find_library(BROTLIDEC_LIBRARY NAMES brotlidec) + +find_package_handle_standard_args(Brotli + FOUND_VAR + BROTLI_FOUND + REQUIRED_VARS + BROTLIDEC_LIBRARY + BROTLICOMMON_LIBRARY + BROTLI_INCLUDE_DIR + FAIL_MESSAGE + "Could NOT find Brotli" +) + +set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) +set(BROTLI_LIBRARIES ${BROTLIDEC_LIBRARY} ${BROTLICOMMON_LIBRARY}) diff --git a/third_party/curl/CMake/FindCARES.cmake b/third_party/curl/CMake/FindCARES.cmake new file mode 100644 index 0000000000..e82b1de4a0 --- /dev/null +++ b/third_party/curl/CMake/FindCARES.cmake @@ -0,0 +1,47 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# - Find c-ares +# Find the c-ares includes and library +# This module defines +# CARES_INCLUDE_DIR, where to find ares.h, etc. +# CARES_LIBRARIES, the libraries needed to use c-ares. +# CARES_FOUND, If false, do not try to use c-ares. +# also defined, but not for general use are +# CARES_LIBRARY, where to find the c-ares library. + +find_path(CARES_INCLUDE_DIR ares.h) + +set(CARES_NAMES ${CARES_NAMES} cares) +find_library(CARES_LIBRARY + NAMES ${CARES_NAMES} + ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CARES + REQUIRED_VARS CARES_LIBRARY CARES_INCLUDE_DIR) + +mark_as_advanced( + CARES_LIBRARY + CARES_INCLUDE_DIR + ) diff --git a/third_party/curl/CMake/FindGSS.cmake b/third_party/curl/CMake/FindGSS.cmake new file mode 100644 index 0000000000..9061e6c85b --- /dev/null +++ b/third_party/curl/CMake/FindGSS.cmake @@ -0,0 +1,312 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# - Try to find the GSS Kerberos library +# Once done this will define +# +# GSS_ROOT_DIR - Set this variable to the root installation of GSS +# +# Read-Only variables: +# GSS_FOUND - system has the Heimdal library +# GSS_FLAVOUR - "MIT" or "Heimdal" if anything found. +# GSS_INCLUDE_DIR - the Heimdal include directory +# GSS_LIBRARIES - The libraries needed to use GSS +# GSS_LINK_DIRECTORIES - Directories to add to linker search path +# GSS_LINKER_FLAGS - Additional linker flags +# GSS_COMPILER_FLAGS - Additional compiler flags +# GSS_VERSION - This is set to version advertised by pkg-config or read from manifest. +# In case the library is found but no version info available it'll be set to "unknown" + +set(_MIT_MODNAME mit-krb5-gssapi) +set(_HEIMDAL_MODNAME heimdal-gssapi) + +include(CheckIncludeFile) +include(CheckIncludeFiles) +include(CheckTypeSize) + +set(_GSS_ROOT_HINTS + "${GSS_ROOT_DIR}" + "$ENV{GSS_ROOT_DIR}" +) + +# try to find library using system pkg-config if user didn't specify root dir +if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}") + if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(_GSS_PKG ${_MIT_MODNAME} ${_HEIMDAL_MODNAME}) + list(APPEND _GSS_ROOT_HINTS "${_GSS_PKG_PREFIX}") + elseif(WIN32) + list(APPEND _GSS_ROOT_HINTS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos;InstallDir]") + endif() +endif() + +if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approach. + find_file(_GSS_CONFIGURE_SCRIPT + NAMES + "krb5-config" + HINTS + ${_GSS_ROOT_HINTS} + PATH_SUFFIXES + bin + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + ) + + # if not found in user-supplied directories, maybe system knows better + find_file(_GSS_CONFIGURE_SCRIPT + NAMES + "krb5-config" + PATH_SUFFIXES + bin + ) + + if(_GSS_CONFIGURE_SCRIPT) + execute_process( + COMMAND ${_GSS_CONFIGURE_SCRIPT} "--cflags" "gssapi" + OUTPUT_VARIABLE _GSS_CFLAGS + RESULT_VARIABLE _GSS_CONFIGURE_FAILED + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + message(STATUS "CFLAGS: ${_GSS_CFLAGS}") + if(NOT _GSS_CONFIGURE_FAILED) # 0 means success + # should also work in an odd case when multiple directories are given + string(STRIP "${_GSS_CFLAGS}" _GSS_CFLAGS) + string(REGEX REPLACE " +-I" ";" _GSS_CFLAGS "${_GSS_CFLAGS}") + string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1" _GSS_CFLAGS "${_GSS_CFLAGS}") + + foreach(_flag ${_GSS_CFLAGS}) + if(_flag MATCHES "^-I.*") + string(REGEX REPLACE "^-I" "" _val "${_flag}") + list(APPEND _GSS_INCLUDE_DIR "${_val}") + else() + list(APPEND _GSS_COMPILER_FLAGS "${_flag}") + endif() + endforeach() + endif() + + execute_process( + COMMAND ${_GSS_CONFIGURE_SCRIPT} "--libs" "gssapi" + OUTPUT_VARIABLE _GSS_LIB_FLAGS + RESULT_VARIABLE _GSS_CONFIGURE_FAILED + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + message(STATUS "LDFLAGS: ${_GSS_LIB_FLAGS}") + + if(NOT _GSS_CONFIGURE_FAILED) # 0 means success + # this script gives us libraries and link directories. Blah. We have to deal with it. + string(STRIP "${_GSS_LIB_FLAGS}" _GSS_LIB_FLAGS) + string(REGEX REPLACE " +-(L|l)" ";-\\1" _GSS_LIB_FLAGS "${_GSS_LIB_FLAGS}") + string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1" _GSS_LIB_FLAGS "${_GSS_LIB_FLAGS}") + + foreach(_flag ${_GSS_LIB_FLAGS}) + if(_flag MATCHES "^-l.*") + string(REGEX REPLACE "^-l" "" _val "${_flag}") + list(APPEND _GSS_LIBRARIES "${_val}") + elseif(_flag MATCHES "^-L.*") + string(REGEX REPLACE "^-L" "" _val "${_flag}") + list(APPEND _GSS_LINK_DIRECTORIES "${_val}") + else() + list(APPEND _GSS_LINKER_FLAGS "${_flag}") + endif() + endforeach() + endif() + + execute_process( + COMMAND ${_GSS_CONFIGURE_SCRIPT} "--version" + OUTPUT_VARIABLE _GSS_VERSION + RESULT_VARIABLE _GSS_CONFIGURE_FAILED + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + # older versions may not have the "--version" parameter. In this case we just don't care. + if(_GSS_CONFIGURE_FAILED) + set(_GSS_VERSION 0) + endif() + + execute_process( + COMMAND ${_GSS_CONFIGURE_SCRIPT} "--vendor" + OUTPUT_VARIABLE _GSS_VENDOR + RESULT_VARIABLE _GSS_CONFIGURE_FAILED + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + # older versions may not have the "--vendor" parameter. In this case we just don't care. + if(_GSS_CONFIGURE_FAILED) + set(GSS_FLAVOUR "Heimdal") # most probably, shouldn't really matter + else() + if(_GSS_VENDOR MATCHES ".*H|heimdal.*") + set(GSS_FLAVOUR "Heimdal") + else() + set(GSS_FLAVOUR "MIT") + endif() + endif() + + else() # either there is no config script or we are on a platform that doesn't provide one (Windows?) + + find_path(_GSS_INCLUDE_DIR + NAMES + "gssapi/gssapi.h" + HINTS + ${_GSS_ROOT_HINTS} + PATH_SUFFIXES + include + inc + ) + + if(_GSS_INCLUDE_DIR) #jay, we've found something + set(CMAKE_REQUIRED_INCLUDES "${_GSS_INCLUDE_DIR}") + check_include_files( "gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _GSS_HAVE_MIT_HEADERS) + + if(_GSS_HAVE_MIT_HEADERS) + set(GSS_FLAVOUR "MIT") + else() + # prevent compiling the header - just check if we can include it + list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__ROKEN_H__) + check_include_file( "roken.h" _GSS_HAVE_ROKEN_H) + + check_include_file( "heimdal/roken.h" _GSS_HAVE_HEIMDAL_ROKEN_H) + if(_GSS_HAVE_ROKEN_H OR _GSS_HAVE_HEIMDAL_ROKEN_H) + set(GSS_FLAVOUR "Heimdal") + endif() + list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D__ROKEN_H__) + endif() + else() + # I'm not convinced if this is the right way but this is what autotools do at the moment + find_path(_GSS_INCLUDE_DIR + NAMES + "gssapi.h" + HINTS + ${_GSS_ROOT_HINTS} + PATH_SUFFIXES + include + inc + ) + + if(_GSS_INCLUDE_DIR) + set(GSS_FLAVOUR "Heimdal") + endif() + endif() + + # if we have headers, check if we can link libraries + if(GSS_FLAVOUR) + set(_GSS_LIBDIR_SUFFIXES "") + set(_GSS_LIBDIR_HINTS ${_GSS_ROOT_HINTS}) + get_filename_component(_GSS_CALCULATED_POTENTIAL_ROOT "${_GSS_INCLUDE_DIR}" PATH) + list(APPEND _GSS_LIBDIR_HINTS ${_GSS_CALCULATED_POTENTIAL_ROOT}) + + if(WIN32) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND _GSS_LIBDIR_SUFFIXES "lib/AMD64") + if(GSS_FLAVOUR STREQUAL "MIT") + set(_GSS_LIBNAME "gssapi64") + else() + set(_GSS_LIBNAME "libgssapi") + endif() + else() + list(APPEND _GSS_LIBDIR_SUFFIXES "lib/i386") + if(GSS_FLAVOUR STREQUAL "MIT") + set(_GSS_LIBNAME "gssapi32") + else() + set(_GSS_LIBNAME "libgssapi") + endif() + endif() + else() + list(APPEND _GSS_LIBDIR_SUFFIXES "lib;lib64") # those suffixes are not checked for HINTS + if(GSS_FLAVOUR STREQUAL "MIT") + set(_GSS_LIBNAME "gssapi_krb5") + else() + set(_GSS_LIBNAME "gssapi") + endif() + endif() + + find_library(_GSS_LIBRARIES + NAMES + ${_GSS_LIBNAME} + HINTS + ${_GSS_LIBDIR_HINTS} + PATH_SUFFIXES + ${_GSS_LIBDIR_SUFFIXES} + ) + + endif() + endif() +else() + if(_GSS_PKG_${_MIT_MODNAME}_VERSION) + set(GSS_FLAVOUR "MIT") + set(_GSS_VERSION _GSS_PKG_${_MIT_MODNAME}_VERSION) + else() + set(GSS_FLAVOUR "Heimdal") + set(_GSS_VERSION _GSS_PKG_${_MIT_HEIMDAL}_VERSION) + endif() +endif() + +set(GSS_INCLUDE_DIR ${_GSS_INCLUDE_DIR}) +set(GSS_LIBRARIES ${_GSS_LIBRARIES}) +set(GSS_LINK_DIRECTORIES ${_GSS_LINK_DIRECTORIES}) +set(GSS_LINKER_FLAGS ${_GSS_LINKER_FLAGS}) +set(GSS_COMPILER_FLAGS ${_GSS_COMPILER_FLAGS}) +set(GSS_VERSION ${_GSS_VERSION}) + +if(GSS_FLAVOUR) + if(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "Heimdal") + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(HEIMDAL_MANIFEST_FILE "Heimdal.Application.amd64.manifest") + else() + set(HEIMDAL_MANIFEST_FILE "Heimdal.Application.x86.manifest") + endif() + + if(EXISTS "${GSS_INCLUDE_DIR}/${HEIMDAL_MANIFEST_FILE}") + file(STRINGS "${GSS_INCLUDE_DIR}/${HEIMDAL_MANIFEST_FILE}" heimdal_version_str + REGEX "^.*version=\"[0-9]\\.[^\"]+\".*$") + + string(REGEX MATCH "[0-9]\\.[^\"]+" + GSS_VERSION "${heimdal_version_str}") + endif() + + if(NOT GSS_VERSION) + set(GSS_VERSION "Heimdal Unknown") + endif() + elseif(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "MIT") + get_filename_component(_MIT_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME CACHE) + if(WIN32 AND _MIT_VERSION) + set(GSS_VERSION "${_MIT_VERSION}") + else() + set(GSS_VERSION "MIT Unknown") + endif() + endif() +endif() + +include(FindPackageHandleStandardArgs) + +set(_GSS_REQUIRED_VARS GSS_LIBRARIES GSS_FLAVOUR) + +find_package_handle_standard_args(GSS + REQUIRED_VARS + ${_GSS_REQUIRED_VARS} + VERSION_VAR + GSS_VERSION + FAIL_MESSAGE + "Could NOT find GSS, try to set the path to GSS root folder in the system variable GSS_ROOT_DIR" +) + +mark_as_advanced(GSS_INCLUDE_DIR GSS_LIBRARIES) diff --git a/third_party/curl/CMake/FindLibPSL.cmake b/third_party/curl/CMake/FindLibPSL.cmake new file mode 100644 index 0000000000..9ad4bc6394 --- /dev/null +++ b/third_party/curl/CMake/FindLibPSL.cmake @@ -0,0 +1,45 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# - Try to find the libpsl library +# Once done this will define +# +# LIBPSL_FOUND - system has the libpsl library +# LIBPSL_INCLUDE_DIR - the libpsl include directory +# LIBPSL_LIBRARY - the libpsl library name + +find_path(LIBPSL_INCLUDE_DIR libpsl.h) + +find_library(LIBPSL_LIBRARY NAMES psl libpsl) + +if(LIBPSL_INCLUDE_DIR) + file(STRINGS "${LIBPSL_INCLUDE_DIR}/libpsl.h" libpsl_version_str REGEX "^#define[\t ]+PSL_VERSION[\t ]+\"(.*)\"") + string(REGEX REPLACE "^.*\"([^\"]+)\"" "\\1" LIBPSL_VERSION "${libpsl_version_str}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibPSL + REQUIRED_VARS LIBPSL_LIBRARY LIBPSL_INCLUDE_DIR + VERSION_VAR LIBPSL_VERSION) + +mark_as_advanced(LIBPSL_INCLUDE_DIR LIBPSL_LIBRARY) diff --git a/third_party/curl/CMake/FindLibSSH2.cmake b/third_party/curl/CMake/FindLibSSH2.cmake new file mode 100644 index 0000000000..3466b6bfce --- /dev/null +++ b/third_party/curl/CMake/FindLibSSH2.cmake @@ -0,0 +1,45 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# - Try to find the libssh2 library +# Once done this will define +# +# LIBSSH2_FOUND - system has the libssh2 library +# LIBSSH2_INCLUDE_DIR - the libssh2 include directory +# LIBSSH2_LIBRARY - the libssh2 library name + +find_path(LIBSSH2_INCLUDE_DIR libssh2.h) + +find_library(LIBSSH2_LIBRARY NAMES ssh2 libssh2) + +if(LIBSSH2_INCLUDE_DIR) + file(STRINGS "${LIBSSH2_INCLUDE_DIR}/libssh2.h" libssh2_version_str REGEX "^#define[\t ]+LIBSSH2_VERSION[\t ]+\"(.*)\"") + string(REGEX REPLACE "^.*\"([^\"]+)\"" "\\1" LIBSSH2_VERSION "${libssh2_version_str}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibSSH2 + REQUIRED_VARS LIBSSH2_LIBRARY LIBSSH2_INCLUDE_DIR + VERSION_VAR LIBSSH2_VERSION) + +mark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY) diff --git a/third_party/curl/CMake/FindMSH3.cmake b/third_party/curl/CMake/FindMSH3.cmake new file mode 100644 index 0000000000..7d9c6b6544 --- /dev/null +++ b/third_party/curl/CMake/FindMSH3.cmake @@ -0,0 +1,70 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +#[=======================================================================[.rst: +FindMSH3 +---------- + +Find the msh3 library + +Result Variables +^^^^^^^^^^^^^^^^ + +``MSH3_FOUND`` + System has msh3 +``MSH3_INCLUDE_DIRS`` + The msh3 include directories. +``MSH3_LIBRARIES`` + The libraries needed to use msh3 +#]=======================================================================] +if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(PC_MSH3 libmsh3) +endif() + +find_path(MSH3_INCLUDE_DIR msh3.h + HINTS + ${PC_MSH3_INCLUDEDIR} + ${PC_MSH3_INCLUDE_DIRS} +) + +find_library(MSH3_LIBRARY NAMES msh3 + HINTS + ${PC_MSH3_LIBDIR} + ${PC_MSH3_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MSH3 + REQUIRED_VARS + MSH3_LIBRARY + MSH3_INCLUDE_DIR +) + +if(MSH3_FOUND) + set(MSH3_LIBRARIES ${MSH3_LIBRARY}) + set(MSH3_INCLUDE_DIRS ${MSH3_INCLUDE_DIR}) +endif() + +mark_as_advanced(MSH3_INCLUDE_DIRS MSH3_LIBRARIES) diff --git a/third_party/curl/CMake/FindMbedTLS.cmake b/third_party/curl/CMake/FindMbedTLS.cmake new file mode 100644 index 0000000000..7692a25871 --- /dev/null +++ b/third_party/curl/CMake/FindMbedTLS.cmake @@ -0,0 +1,36 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h) + +find_library(MBEDTLS_LIBRARY mbedtls) +find_library(MBEDX509_LIBRARY mbedx509) +find_library(MBEDCRYPTO_LIBRARY mbedcrypto) + +set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}") + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MbedTLS DEFAULT_MSG + MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) + +mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) diff --git a/third_party/curl/CMake/FindNGHTTP2.cmake b/third_party/curl/CMake/FindNGHTTP2.cmake new file mode 100644 index 0000000000..88ac037417 --- /dev/null +++ b/third_party/curl/CMake/FindNGHTTP2.cmake @@ -0,0 +1,41 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +include(FindPackageHandleStandardArgs) + +find_path(NGHTTP2_INCLUDE_DIR "nghttp2/nghttp2.h") + +find_library(NGHTTP2_LIBRARY NAMES nghttp2 nghttp2_static) + +find_package_handle_standard_args(NGHTTP2 + FOUND_VAR + NGHTTP2_FOUND + REQUIRED_VARS + NGHTTP2_LIBRARY + NGHTTP2_INCLUDE_DIR +) + +set(NGHTTP2_INCLUDE_DIRS ${NGHTTP2_INCLUDE_DIR}) +set(NGHTTP2_LIBRARIES ${NGHTTP2_LIBRARY}) + +mark_as_advanced(NGHTTP2_INCLUDE_DIRS NGHTTP2_LIBRARIES) diff --git a/third_party/curl/CMake/FindNGHTTP3.cmake b/third_party/curl/CMake/FindNGHTTP3.cmake new file mode 100644 index 0000000000..9b13e6c6ff --- /dev/null +++ b/third_party/curl/CMake/FindNGHTTP3.cmake @@ -0,0 +1,78 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +#[=======================================================================[.rst: +FindNGHTTP3 +---------- + +Find the nghttp3 library + +Result Variables +^^^^^^^^^^^^^^^^ + +``NGHTTP3_FOUND`` + System has nghttp3 +``NGHTTP3_INCLUDE_DIRS`` + The nghttp3 include directories. +``NGHTTP3_LIBRARIES`` + The libraries needed to use nghttp3 +``NGHTTP3_VERSION`` + version of nghttp3. +#]=======================================================================] + +if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(PC_NGHTTP3 libnghttp3) +endif() + +find_path(NGHTTP3_INCLUDE_DIR nghttp3/nghttp3.h + HINTS + ${PC_NGHTTP3_INCLUDEDIR} + ${PC_NGHTTP3_INCLUDE_DIRS} +) + +find_library(NGHTTP3_LIBRARY NAMES nghttp3 + HINTS + ${PC_NGHTTP3_LIBDIR} + ${PC_NGHTTP3_LIBRARY_DIRS} +) + +if(PC_NGHTTP3_VERSION) + set(NGHTTP3_VERSION ${PC_NGHTTP3_VERSION}) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(NGHTTP3 + REQUIRED_VARS + NGHTTP3_LIBRARY + NGHTTP3_INCLUDE_DIR + VERSION_VAR NGHTTP3_VERSION +) + +if(NGHTTP3_FOUND) + set(NGHTTP3_LIBRARIES ${NGHTTP3_LIBRARY}) + set(NGHTTP3_INCLUDE_DIRS ${NGHTTP3_INCLUDE_DIR}) +endif() + +mark_as_advanced(NGHTTP3_INCLUDE_DIRS NGHTTP3_LIBRARIES) diff --git a/third_party/curl/CMake/FindNGTCP2.cmake b/third_party/curl/CMake/FindNGTCP2.cmake new file mode 100644 index 0000000000..7ea466582a --- /dev/null +++ b/third_party/curl/CMake/FindNGTCP2.cmake @@ -0,0 +1,117 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +#[=======================================================================[.rst: +FindNGTCP2 +---------- + +Find the ngtcp2 library + +This module accepts optional COMPONENTS to control the crypto library (these are +mutually exclusive):: + + quictls, LibreSSL: Use libngtcp2_crypto_quictls + BoringSSL, AWS-LC: Use libngtcp2_crypto_boringssl + wolfSSL: Use libngtcp2_crypto_wolfssl + GnuTLS: Use libngtcp2_crypto_gnutls + +Result Variables +^^^^^^^^^^^^^^^^ + +``NGTCP2_FOUND`` + System has ngtcp2 +``NGTCP2_INCLUDE_DIRS`` + The ngtcp2 include directories. +``NGTCP2_LIBRARIES`` + The libraries needed to use ngtcp2 +``NGTCP2_VERSION`` + version of ngtcp2. +#]=======================================================================] + +if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(PC_NGTCP2 libngtcp2) +endif() + +find_path(NGTCP2_INCLUDE_DIR ngtcp2/ngtcp2.h + HINTS + ${PC_NGTCP2_INCLUDEDIR} + ${PC_NGTCP2_INCLUDE_DIRS} +) + +find_library(NGTCP2_LIBRARY NAMES ngtcp2 + HINTS + ${PC_NGTCP2_LIBDIR} + ${PC_NGTCP2_LIBRARY_DIRS} +) + +if(PC_NGTCP2_VERSION) + set(NGTCP2_VERSION ${PC_NGTCP2_VERSION}) +endif() + +if(NGTCP2_FIND_COMPONENTS) + set(NGTCP2_CRYPTO_BACKEND "") + foreach(component IN LISTS NGTCP2_FIND_COMPONENTS) + if(component MATCHES "^(BoringSSL|quictls|wolfSSL|GnuTLS)") + if(NGTCP2_CRYPTO_BACKEND) + message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected") + endif() + set(NGTCP2_CRYPTO_BACKEND ${component}) + endif() + endforeach() + + if(NGTCP2_CRYPTO_BACKEND) + string(TOLOWER "ngtcp2_crypto_${NGTCP2_CRYPTO_BACKEND}" _crypto_library) + if(UNIX) + pkg_search_module(PC_${_crypto_library} lib${_crypto_library}) + endif() + find_library(${_crypto_library}_LIBRARY + NAMES + ${_crypto_library} + HINTS + ${PC_${_crypto_library}_LIBDIR} + ${PC_${_crypto_library}_LIBRARY_DIRS} + ) + if(${_crypto_library}_LIBRARY) + set(NGTCP2_${NGTCP2_CRYPTO_BACKEND}_FOUND TRUE) + set(NGTCP2_CRYPTO_LIBRARY ${${_crypto_library}_LIBRARY}) + endif() + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(NGTCP2 + REQUIRED_VARS + NGTCP2_LIBRARY + NGTCP2_INCLUDE_DIR + VERSION_VAR NGTCP2_VERSION + HANDLE_COMPONENTS +) + +if(NGTCP2_FOUND) + set(NGTCP2_LIBRARIES ${NGTCP2_LIBRARY} ${NGTCP2_CRYPTO_LIBRARY}) + set(NGTCP2_INCLUDE_DIRS ${NGTCP2_INCLUDE_DIR}) +endif() + +mark_as_advanced(NGTCP2_INCLUDE_DIRS NGTCP2_LIBRARIES) diff --git a/third_party/curl/CMake/FindQUICHE.cmake b/third_party/curl/CMake/FindQUICHE.cmake new file mode 100644 index 0000000000..0488463d55 --- /dev/null +++ b/third_party/curl/CMake/FindQUICHE.cmake @@ -0,0 +1,70 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +#[=======================================================================[.rst: +FindQUICHE +---------- + +Find the quiche library + +Result Variables +^^^^^^^^^^^^^^^^ + +``QUICHE_FOUND`` + System has quiche +``QUICHE_INCLUDE_DIRS`` + The quiche include directories. +``QUICHE_LIBRARIES`` + The libraries needed to use quiche +#]=======================================================================] +if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(PC_QUICHE quiche) +endif() + +find_path(QUICHE_INCLUDE_DIR quiche.h + HINTS + ${PC_QUICHE_INCLUDEDIR} + ${PC_QUICHE_INCLUDE_DIRS} +) + +find_library(QUICHE_LIBRARY NAMES quiche + HINTS + ${PC_QUICHE_LIBDIR} + ${PC_QUICHE_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(QUICHE + REQUIRED_VARS + QUICHE_LIBRARY + QUICHE_INCLUDE_DIR +) + +if(QUICHE_FOUND) + set(QUICHE_LIBRARIES ${QUICHE_LIBRARY}) + set(QUICHE_INCLUDE_DIRS ${QUICHE_INCLUDE_DIR}) +endif() + +mark_as_advanced(QUICHE_INCLUDE_DIRS QUICHE_LIBRARIES) diff --git a/third_party/curl/CMake/FindWolfSSL.cmake b/third_party/curl/CMake/FindWolfSSL.cmake new file mode 100644 index 0000000000..7336c8f4dd --- /dev/null +++ b/third_party/curl/CMake/FindWolfSSL.cmake @@ -0,0 +1,60 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_WOLFSSL QUIET "wolfssl") + +find_path(WolfSSL_INCLUDE_DIR + NAMES "wolfssl/ssl.h" + HINTS ${PC_WOLFSSL_INCLUDE_DIRS} +) + +find_library(WolfSSL_LIBRARY + NAMES "wolfssl" + HINTS ${PC_WOLFSSL_LIBRARY_DIRS} +) + +if(WolfSSL_INCLUDE_DIR) + set(_version_regex "^#define[ \t]+LIBWOLFSSL_VERSION_STRING[ \t]+\"([^\"]+)\".*") + file(STRINGS "${WolfSSL_INCLUDE_DIR}/wolfssl/version.h" + WolfSSL_VERSION REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" + WolfSSL_VERSION "${WolfSSL_VERSION}") + unset(_version_regex) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(WolfSSL + REQUIRED_VARS + WolfSSL_INCLUDE_DIR + WolfSSL_LIBRARY + VERSION_VAR WolfSSL_VERSION +) + +if(WolfSSL_FOUND) + set(WolfSSL_INCLUDE_DIRS ${WolfSSL_INCLUDE_DIR}) + set(WolfSSL_LIBRARIES ${WolfSSL_LIBRARY}) +endif() + +mark_as_advanced(WolfSSL_INCLUDE_DIR WolfSSL_LIBRARY) diff --git a/third_party/curl/CMake/FindZstd.cmake b/third_party/curl/CMake/FindZstd.cmake new file mode 100644 index 0000000000..0ea9e0c871 --- /dev/null +++ b/third_party/curl/CMake/FindZstd.cmake @@ -0,0 +1,78 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +#[=======================================================================[.rst: +FindZstd +---------- + +Find the zstd library + +Result Variables +^^^^^^^^^^^^^^^^ + +``Zstd_FOUND`` + System has zstd +``Zstd_INCLUDE_DIRS`` + The zstd include directories. +``Zstd_LIBRARIES`` + The libraries needed to use zstd +#]=======================================================================] + +if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(PC_Zstd libzstd) +endif() + +find_path(Zstd_INCLUDE_DIR zstd.h + HINTS + ${PC_Zstd_INCLUDEDIR} + ${PC_Zstd_INCLUDE_DIRS} +) + +find_library(Zstd_LIBRARY NAMES zstd + HINTS + ${PC_Zstd_LIBDIR} + ${PC_Zstd_LIBRARY_DIRS} +) + +if(Zstd_INCLUDE_DIR) + file(READ "${Zstd_INCLUDE_DIR}/zstd.h" _zstd_header) + string(REGEX MATCH ".*define ZSTD_VERSION_MAJOR *([0-9]+).*define ZSTD_VERSION_MINOR *([0-9]+).*define ZSTD_VERSION_RELEASE *([0-9]+)" _zstd_ver "${_zstd_header}") + set(Zstd_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Zstd + REQUIRED_VARS + Zstd_LIBRARY + Zstd_INCLUDE_DIR + VERSION_VAR Zstd_VERSION +) + +if(Zstd_FOUND) + set(Zstd_LIBRARIES ${Zstd_LIBRARY}) + set(Zstd_INCLUDE_DIRS ${Zstd_INCLUDE_DIR}) +endif() + +mark_as_advanced(Zstd_INCLUDE_DIRS Zstd_LIBRARIES) diff --git a/third_party/curl/CMake/Macros.cmake b/third_party/curl/CMake/Macros.cmake new file mode 100644 index 0000000000..d5439fcc04 --- /dev/null +++ b/third_party/curl/CMake/Macros.cmake @@ -0,0 +1,80 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +#File defines convenience macros for available feature testing + +# Check if header file exists and add it to the list. +# This macro is intended to be called multiple times with a sequence of +# possibly dependent header files. Some headers depend on others to be +# compiled correctly. +macro(check_include_file_concat FILE VARIABLE) + check_include_files("${CURL_INCLUDES};${FILE}" ${VARIABLE}) + if(${VARIABLE}) + set(CURL_INCLUDES ${CURL_INCLUDES} ${FILE}) + set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D${VARIABLE}") + endif() +endmacro() + +# For other curl specific tests, use this macro. +macro(curl_internal_test CURL_TEST) + if(NOT DEFINED "${CURL_TEST}") + set(MACRO_CHECK_FUNCTION_DEFINITIONS + "-D${CURL_TEST} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS}") + if(CMAKE_REQUIRED_LIBRARIES) + set(CURL_TEST_ADD_LIBRARIES + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") + endif() + + message(STATUS "Performing Test ${CURL_TEST}") + try_compile(${CURL_TEST} + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + "${CURL_TEST_ADD_LIBRARIES}" + OUTPUT_VARIABLE OUTPUT) + if(${CURL_TEST}) + set(${CURL_TEST} 1 CACHE INTERNAL "Curl test ${FUNCTION}") + message(STATUS "Performing Test ${CURL_TEST} - Success") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Performing Test ${CURL_TEST} passed with the following output:\n" + "${OUTPUT}\n") + else() + message(STATUS "Performing Test ${CURL_TEST} - Failed") + set(${CURL_TEST} "" CACHE INTERNAL "Curl test ${FUNCTION}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Performing Test ${CURL_TEST} failed with the following output:\n" + "${OUTPUT}\n") + endif() + endif() +endmacro() + +macro(optional_dependency DEPENDENCY) + set(CURL_${DEPENDENCY} AUTO CACHE STRING "Build curl with ${DEPENDENCY} support (AUTO, ON or OFF)") + set_property(CACHE CURL_${DEPENDENCY} PROPERTY STRINGS AUTO ON OFF) + + if(CURL_${DEPENDENCY} STREQUAL AUTO) + find_package(${DEPENDENCY}) + elseif(CURL_${DEPENDENCY}) + find_package(${DEPENDENCY} REQUIRED) + endif() +endmacro() diff --git a/third_party/curl/CMake/OtherTests.cmake b/third_party/curl/CMake/OtherTests.cmake new file mode 100644 index 0000000000..2fddb867c8 --- /dev/null +++ b/third_party/curl/CMake/OtherTests.cmake @@ -0,0 +1,184 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +include(CheckCSourceCompiles) +include(CheckCSourceRuns) +include(CheckTypeSize) + +macro(add_header_include check header) + if(${check}) + set(_source_epilogue "${_source_epilogue} + #include <${header}>") + endif() +endmacro() + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +if(NOT DEFINED HAVE_STRUCT_SOCKADDR_STORAGE) + set(CMAKE_EXTRA_INCLUDE_FILES) + if(WIN32) + set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") + set(CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN") + set(CMAKE_REQUIRED_LIBRARIES "ws2_32") + elseif(HAVE_SYS_SOCKET_H) + set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") + endif() + check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE) + set(HAVE_STRUCT_SOCKADDR_STORAGE ${HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE}) +endif() + +if(NOT WIN32) + set(_source_epilogue "#undef inline") + add_header_include(HAVE_SYS_TYPES_H "sys/types.h") + add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h") + check_c_source_compiles("${_source_epilogue} + int main(void) + { + int flag = MSG_NOSIGNAL; + (void)flag; + return 0; + }" HAVE_MSG_NOSIGNAL) +endif() + +set(_source_epilogue "#undef inline") +add_header_include(HAVE_SYS_TIME_H "sys/time.h") +check_c_source_compiles("${_source_epilogue} + #include + int main(void) + { + struct timeval ts; + ts.tv_sec = 0; + ts.tv_usec = 0; + (void)ts; + return 0; + }" HAVE_STRUCT_TIMEVAL) + +unset(CMAKE_TRY_COMPILE_TARGET_TYPE) + +if(NOT CMAKE_CROSSCOMPILING AND NOT APPLE) + set(_source_epilogue "#undef inline") + add_header_include(HAVE_SYS_POLL_H "sys/poll.h") + add_header_include(HAVE_POLL_H "poll.h") + check_c_source_runs("${_source_epilogue} + #include + #include + int main(void) + { + if(0 != poll(0, 0, 10)) { + return 1; /* fail */ + } + else { + /* detect the 10.12 poll() breakage */ + struct timeval before, after; + int rc; + size_t us; + + gettimeofday(&before, NULL); + rc = poll(NULL, 0, 500); + gettimeofday(&after, NULL); + + us = (after.tv_sec - before.tv_sec) * 1000000 + + (after.tv_usec - before.tv_usec); + + if(us < 400000) { + return 1; + } + } + return 0; + }" HAVE_POLL_FINE) +endif() + +# Detect HAVE_GETADDRINFO_THREADSAFE + +if(WIN32) + set(HAVE_GETADDRINFO_THREADSAFE ${HAVE_GETADDRINFO}) +elseif(NOT HAVE_GETADDRINFO) + set(HAVE_GETADDRINFO_THREADSAFE FALSE) +elseif(APPLE OR + CMAKE_SYSTEM_NAME STREQUAL "AIX" OR + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "HP-UX" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "SunOS") + set(HAVE_GETADDRINFO_THREADSAFE TRUE) +elseif(CMAKE_SYSTEM_NAME MATCHES "BSD") + set(HAVE_GETADDRINFO_THREADSAFE FALSE) +endif() + +if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE) + set(_source_epilogue "#undef inline") + add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h") + add_header_include(HAVE_SYS_TIME_H "sys/time.h") + add_header_include(HAVE_NETDB_H "netdb.h") + check_c_source_compiles("${_source_epilogue} + int main(void) + { + #ifdef h_errno + return 0; + #else + #error force compilation error + #endif + }" HAVE_H_ERRNO) + + if(NOT HAVE_H_ERRNO) + check_c_source_compiles("${_source_epilogue} + int main(void) + { + h_errno = 2; + return h_errno != 0 ? 1 : 0; + }" HAVE_H_ERRNO_ASSIGNABLE) + + if(NOT HAVE_H_ERRNO_ASSIGNABLE) + check_c_source_compiles("${_source_epilogue} + int main(void) + { + #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) + return 0; + #elif defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 700) + return 0; + #else + #error force compilation error + #endif + }" HAVE_H_ERRNO_SBS_ISSUE_7) + endif() + endif() + + if(HAVE_H_ERRNO OR HAVE_H_ERRNO_ASSIGNABLE OR HAVE_H_ERRNO_SBS_ISSUE_7) + set(HAVE_GETADDRINFO_THREADSAFE TRUE) + endif() +endif() + +if(NOT WIN32 AND NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW) + set(_source_epilogue "#undef inline") + add_header_include(HAVE_SYS_TYPES_H "sys/types.h") + add_header_include(HAVE_SYS_TIME_H "sys/time.h") + check_c_source_compiles("${_source_epilogue} + #include + int main(void) + { + struct timespec ts; + (void)clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + return 0; + }" HAVE_CLOCK_GETTIME_MONOTONIC_RAW) +endif() diff --git a/third_party/curl/CMake/PickyWarnings.cmake b/third_party/curl/CMake/PickyWarnings.cmake new file mode 100644 index 0000000000..96e45f0e8c --- /dev/null +++ b/third_party/curl/CMake/PickyWarnings.cmake @@ -0,0 +1,246 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +include(CheckCCompilerFlag) + +unset(WPICKY) + +if(CURL_WERROR AND + ((CMAKE_COMPILER_IS_GNUCC AND + NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0 AND + NOT CMAKE_VERSION VERSION_LESS 3.23.0) OR # check_symbol_exists() incompatible with GCC -pedantic-errors in earlier CMake versions + CMAKE_C_COMPILER_ID MATCHES "Clang")) + set(WPICKY "${WPICKY} -pedantic-errors") +endif() + +if(APPLE AND + (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3)) + set(WPICKY "${WPICKY} -Werror=partial-availability") # clang 3.6 appleclang 6.3 +endif() + +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") + set(WPICKY "${WPICKY} -Werror-implicit-function-declaration") # clang 1.0 gcc 2.95 +endif() + +if(PICKY_COMPILER) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") + + # https://clang.llvm.org/docs/DiagnosticsReference.html + # https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html + + # WPICKY_ENABLE = Options we want to enable as-is. + # WPICKY_DETECT = Options we want to test first and enable if available. + + # Prefer the -Wextra alias with clang. + if(CMAKE_C_COMPILER_ID MATCHES "Clang") + set(WPICKY_ENABLE "-Wextra") + else() + set(WPICKY_ENABLE "-W") + endif() + + list(APPEND WPICKY_ENABLE + -Wall -pedantic + ) + + # ---------------------------------- + # Add new options here, if in doubt: + # ---------------------------------- + set(WPICKY_DETECT + ) + + # Assume these options always exist with both clang and gcc. + # Require clang 3.0 / gcc 2.95 or later. + list(APPEND WPICKY_ENABLE + -Wbad-function-cast # clang 2.7 gcc 2.95 + -Wconversion # clang 2.7 gcc 2.95 + -Winline # clang 1.0 gcc 1.0 + -Wmissing-declarations # clang 1.0 gcc 2.7 + -Wmissing-prototypes # clang 1.0 gcc 1.0 + -Wnested-externs # clang 1.0 gcc 2.7 + -Wno-long-long # clang 1.0 gcc 2.95 + -Wno-multichar # clang 1.0 gcc 2.95 + -Wpointer-arith # clang 1.0 gcc 1.4 + -Wshadow # clang 1.0 gcc 2.95 + -Wsign-compare # clang 1.0 gcc 2.95 + -Wundef # clang 1.0 gcc 2.95 + -Wunused # clang 1.1 gcc 2.95 + -Wwrite-strings # clang 1.0 gcc 1.4 + ) + + # Always enable with clang, version dependent with gcc + set(WPICKY_COMMON_OLD + -Waddress # clang 2.7 gcc 4.3 + -Wattributes # clang 2.7 gcc 4.1 + -Wcast-align # clang 1.0 gcc 4.2 + -Wdeclaration-after-statement # clang 1.0 gcc 3.4 + -Wdiv-by-zero # clang 2.7 gcc 4.1 + -Wempty-body # clang 2.7 gcc 4.3 + -Wendif-labels # clang 1.0 gcc 3.3 + -Wfloat-equal # clang 1.0 gcc 2.96 (3.0) + -Wformat-security # clang 2.7 gcc 4.1 + -Wignored-qualifiers # clang 2.8 gcc 4.3 + -Wmissing-field-initializers # clang 2.7 gcc 4.1 + -Wmissing-noreturn # clang 2.7 gcc 4.1 + -Wno-format-nonliteral # clang 1.0 gcc 2.96 (3.0) + -Wno-system-headers # clang 1.0 gcc 3.0 + # -Wpadded # clang 2.9 gcc 4.1 # Not used because we cannot change public structs + -Wold-style-definition # clang 2.7 gcc 3.4 + -Wredundant-decls # clang 2.7 gcc 4.1 + -Wsign-conversion # clang 2.9 gcc 4.3 + -Wno-error=sign-conversion # FIXME + -Wstrict-prototypes # clang 1.0 gcc 3.3 + # -Wswitch-enum # clang 2.7 gcc 4.1 # Not used because this basically disallows default case + -Wtype-limits # clang 2.7 gcc 4.3 + -Wunreachable-code # clang 2.7 gcc 4.1 + # -Wunused-macros # clang 2.7 gcc 4.1 # Not practical + -Wunused-parameter # clang 2.7 gcc 4.1 + -Wvla # clang 2.8 gcc 4.3 + ) + + set(WPICKY_COMMON + -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.3 + -Wenum-conversion # clang 3.2 gcc 10.0 appleclang 4.6 g++ 11.0 + -Wpragmas # clang 3.5 gcc 4.1 appleclang 6.0 + -Wunused-const-variable # clang 3.4 gcc 6.0 appleclang 5.1 + ) + + if(CMAKE_C_COMPILER_ID MATCHES "Clang") + list(APPEND WPICKY_ENABLE + ${WPICKY_COMMON_OLD} + -Wshift-sign-overflow # clang 2.9 + -Wshorten-64-to-32 # clang 1.0 + -Wlanguage-extension-token # clang 3.0 + -Wformat=2 # clang 3.0 gcc 4.8 + ) + # Enable based on compiler version + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3)) + list(APPEND WPICKY_ENABLE + ${WPICKY_COMMON} + -Wunreachable-code-break # clang 3.5 appleclang 6.0 + -Wheader-guard # clang 3.4 appleclang 5.1 + -Wsometimes-uninitialized # clang 3.2 appleclang 4.6 + ) + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.9) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.3)) + list(APPEND WPICKY_ENABLE + -Wcomma # clang 3.9 appleclang 8.3 + -Wmissing-variable-declarations # clang 3.2 appleclang 4.6 + ) + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 7.0) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.3)) + list(APPEND WPICKY_ENABLE + -Wassign-enum # clang 7.0 appleclang 10.3 + -Wextra-semi-stmt # clang 7.0 appleclang 10.3 + ) + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.0) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 12.4)) + list(APPEND WPICKY_ENABLE + -Wimplicit-fallthrough # clang 4.0 gcc 7.0 appleclang 12.4 # we have silencing markup for clang 10.0 and above only + ) + endif() + else() # gcc + list(APPEND WPICKY_DETECT + ${WPICKY_COMMON} + ) + # Enable based on compiler version + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3) + list(APPEND WPICKY_ENABLE + ${WPICKY_COMMON_OLD} + -Wclobbered # gcc 4.3 + -Wmissing-parameter-type # gcc 4.3 + -Wold-style-declaration # gcc 4.3 + -Wstrict-aliasing=3 # gcc 4.0 + -Wtrampolines # gcc 4.3 + ) + endif() + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5 AND MINGW) + list(APPEND WPICKY_ENABLE + -Wno-pedantic-ms-format # gcc 4.5 (mingw-only) + ) + endif() + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8) + list(APPEND WPICKY_ENABLE + -Wformat=2 # clang 3.0 gcc 4.8 + ) + endif() + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0) + list(APPEND WPICKY_ENABLE + -Warray-bounds=2 -ftree-vrp # clang 3.0 gcc 5.0 (clang default: -Warray-bounds) + ) + endif() + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.0) + list(APPEND WPICKY_ENABLE + -Wduplicated-cond # gcc 6.0 + -Wnull-dereference # clang 3.0 gcc 6.0 (clang default) + -fdelete-null-pointer-checks + -Wshift-negative-value # clang 3.7 gcc 6.0 (clang default) + -Wshift-overflow=2 # clang 3.0 gcc 6.0 (clang default: -Wshift-overflow) + ) + endif() + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 7.0) + list(APPEND WPICKY_ENABLE + -Walloc-zero # gcc 7.0 + -Wduplicated-branches # gcc 7.0 + -Wformat-overflow=2 # gcc 7.0 + -Wformat-truncation=2 # gcc 7.0 + -Wimplicit-fallthrough # clang 4.0 gcc 7.0 + -Wrestrict # gcc 7.0 + ) + endif() + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.0) + list(APPEND WPICKY_ENABLE + -Warith-conversion # gcc 10.0 + ) + endif() + endif() + + # + + foreach(_CCOPT IN LISTS WPICKY_ENABLE) + set(WPICKY "${WPICKY} ${_CCOPT}") + endforeach() + + foreach(_CCOPT IN LISTS WPICKY_DETECT) + # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new + # test result in. + string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname) + # GCC only warns about unknown -Wno- options if there are also other diagnostic messages, + # so test for the positive form instead + string(REPLACE "-Wno-" "-W" _CCOPT_ON "${_CCOPT}") + check_c_compiler_flag(${_CCOPT_ON} ${_optvarname}) + if(${_optvarname}) + set(WPICKY "${WPICKY} ${_CCOPT}") + endif() + endforeach() + endif() +endif() + +if(WPICKY) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WPICKY}") + message(STATUS "Picky compiler options:${WPICKY}") +endif() diff --git a/third_party/curl/CMake/Platforms/WindowsCache.cmake b/third_party/curl/CMake/Platforms/WindowsCache.cmake new file mode 100644 index 0000000000..5346f595ee --- /dev/null +++ b/third_party/curl/CMake/Platforms/WindowsCache.cmake @@ -0,0 +1,192 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +if(NOT WIN32) + message(FATAL_ERROR "This file should be included on Windows platform only") +endif() + +set(HAVE_LOCALE_H 1) + +if(MINGW) + set(HAVE_SNPRINTF 1) + set(HAVE_UNISTD_H 1) + set(HAVE_LIBGEN_H 1) + set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() + set(HAVE_STDBOOL_H 1) + set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") + set(HAVE_STRTOLL 1) + set(HAVE_BASENAME 1) + set(HAVE_STRCASECMP 1) + set(HAVE_FTRUNCATE 1) + set(HAVE_SYS_PARAM_H 1) + set(HAVE_SYS_TIME_H 1) + set(HAVE_GETTIMEOFDAY 1) +else() + set(HAVE_LIBGEN_H 0) + set(HAVE_STRCASECMP 0) + set(HAVE_FTRUNCATE 0) + set(HAVE_SYS_PARAM_H 0) + set(HAVE_SYS_TIME_H 0) + set(HAVE_GETTIMEOFDAY 0) + if(MSVC) + set(HAVE_UNISTD_H 0) + set(HAVE_LOCALE_H 1) + set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() + set(HAVE_STDATOMIC_H 0) + if(NOT MSVC_VERSION LESS 1800) + set(HAVE_STDBOOL_H 1) + set(HAVE_STRTOLL 1) + else() + set(HAVE_STDBOOL_H 0) + set(HAVE_STRTOLL 0) + endif() + set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") + if(NOT MSVC_VERSION LESS 1900) + set(HAVE_SNPRINTF 1) + else() + set(HAVE_SNPRINTF 0) + endif() + set(HAVE_BASENAME 0) + set(HAVE_STRTOK_R 0) + set(HAVE_FILE_OFFSET_BITS 0) + set(HAVE_ATOMIC 0) + endif() +endif() + +# Available in Windows XP and newer +set(HAVE_GETADDRINFO 1) +set(HAVE_FREEADDRINFO 1) + +set(HAVE_FCHMOD 0) +set(HAVE_SOCKETPAIR 0) +set(HAVE_SENDMSG 0) +set(HAVE_ALARM 0) +set(HAVE_FCNTL 0) +set(HAVE_GETPPID 0) +set(HAVE_UTIMES 0) +set(HAVE_GETPWUID_R 0) +set(HAVE_STRERROR_R 0) +set(HAVE_SIGINTERRUPT 0) +set(HAVE_PIPE 0) +set(HAVE_EVENTFD 0) +set(HAVE_IF_NAMETOINDEX 0) +set(HAVE_GETRLIMIT 0) +set(HAVE_SETRLIMIT 0) +set(HAVE_FSETXATTR 0) +set(HAVE_LIBSOCKET 0) +set(HAVE_SETLOCALE 1) +set(HAVE_SETMODE 1) +set(HAVE_GETPEERNAME 1) +set(HAVE_GETSOCKNAME 1) +set(HAVE_GETHOSTNAME 1) +set(HAVE_LIBZ 0) + +set(HAVE_RECV 1) +set(HAVE_SEND 1) +set(HAVE_STROPTS_H 0) +set(HAVE_SYS_XATTR_H 0) +set(HAVE_ARC4RANDOM 0) +set(HAVE_FNMATCH 0) +set(HAVE_SCHED_YIELD 0) +set(HAVE_ARPA_INET_H 0) +set(HAVE_FCNTL_H 1) +set(HAVE_IFADDRS_H 0) +set(HAVE_IO_H 1) +set(HAVE_NETDB_H 0) +set(HAVE_NETINET_IN_H 0) +set(HAVE_NETINET_TCP_H 0) +set(HAVE_NETINET_UDP_H 0) +set(HAVE_NET_IF_H 0) +set(HAVE_IOCTL_SIOCGIFADDR 0) +set(HAVE_POLL_H 0) +set(HAVE_POLL_FINE 0) +set(HAVE_PWD_H 0) +set(HAVE_STRINGS_H 0) # mingw-w64 has it (wrapper to string.h) +set(HAVE_SYS_EVENTFD_H 0) +set(HAVE_SYS_FILIO_H 0) +set(HAVE_SYS_WAIT_H 0) +set(HAVE_SYS_IOCTL_H 0) +set(HAVE_SYS_POLL_H 0) +set(HAVE_SYS_RESOURCE_H 0) +set(HAVE_SYS_SELECT_H 0) +set(HAVE_SYS_SOCKET_H 0) +set(HAVE_SYS_SOCKIO_H 0) +set(HAVE_SYS_STAT_H 1) +set(HAVE_SYS_TYPES_H 1) +set(HAVE_SYS_UN_H 0) +set(HAVE_SYS_UTIME_H 1) +set(HAVE_TERMIOS_H 0) +set(HAVE_TERMIO_H 0) +set(HAVE_UTIME_H 0) # mingw-w64 has it (wrapper to sys/utime.h) + +set(HAVE_DIRENT_H 0) +set(HAVE_OPENDIR 0) + +set(HAVE_FSEEKO 0) +set(HAVE__FSEEKI64 1) +set(HAVE_SOCKET 1) +set(HAVE_SELECT 1) +set(HAVE_STRDUP 1) +set(HAVE_STRICMP 1) +set(HAVE_STRCMPI 1) +set(HAVE_MEMRCHR 0) +set(HAVE_CLOSESOCKET 1) +set(HAVE_SIGSETJMP 0) +set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1) +set(HAVE_GETPASS_R 0) +set(HAVE_GETPWUID 0) +set(HAVE_GETEUID 0) +set(HAVE_UTIME 1) +set(HAVE_GMTIME_R 0) +set(HAVE_GETHOSTBYNAME_R 0) +set(HAVE_SIGNAL 1) +set(HAVE_SIGACTION 0) +set(HAVE_LINUX_TCP_H 0) +set(HAVE_GLIBC_STRERROR_R 0) +set(HAVE_MACH_ABSOLUTE_TIME 0) +set(HAVE_GETIFADDRS 0) +set(HAVE_FCNTL_O_NONBLOCK 0) +set(HAVE_IOCTLSOCKET 1) +set(HAVE_IOCTLSOCKET_CAMEL 0) +set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0) +set(HAVE_IOCTLSOCKET_FIONBIO 1) +set(HAVE_IOCTL_FIONBIO 0) +set(HAVE_SETSOCKOPT_SO_NONBLOCK 0) +set(HAVE_POSIX_STRERROR_R 0) +set(HAVE_MSG_NOSIGNAL 0) +set(HAVE_STRUCT_TIMEVAL 1) +set(HAVE_STRUCT_SOCKADDR_STORAGE 1) + +set(HAVE_GETHOSTBYNAME_R_3 0) +set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0) +set(HAVE_GETHOSTBYNAME_R_5 0) +set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0) +set(HAVE_GETHOSTBYNAME_R_6 0) +set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0) + +set(HAVE_O_NONBLOCK 0) +set(HAVE_IN_ADDR_T 0) +set(STDC_HEADERS 1) + +set(HAVE_SIZEOF_SUSECONDS_T 0) +set(HAVE_SIZEOF_SA_FAMILY_T 0) diff --git a/third_party/curl/CMake/Utilities.cmake b/third_party/curl/CMake/Utilities.cmake new file mode 100644 index 0000000000..84a40f4e3c --- /dev/null +++ b/third_party/curl/CMake/Utilities.cmake @@ -0,0 +1,35 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# File containing various utilities + +# Returns number of arguments that evaluate to true +function(count_true output_count_var) + set(lst_len 0) + foreach(option_var IN LISTS ARGN) + if(${option_var}) + math(EXPR lst_len "${lst_len} + 1") + endif() + endforeach() + set(${output_count_var} ${lst_len} PARENT_SCOPE) +endfunction() diff --git a/third_party/curl/CMake/cmake_uninstall.cmake.in b/third_party/curl/CMake/cmake_uninstall.cmake.in new file mode 100644 index 0000000000..47aec8d42c --- /dev/null +++ b/third_party/curl/CMake/cmake_uninstall.cmake.in @@ -0,0 +1,49 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") +endif() + +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@") +endif() +message(${CMAKE_INSTALL_PREFIX}) + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else() + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/third_party/curl/CMake/curl-config.cmake.in b/third_party/curl/CMake/curl-config.cmake.in new file mode 100644 index 0000000000..2ce8625ef9 --- /dev/null +++ b/third_party/curl/CMake/curl-config.cmake.in @@ -0,0 +1,44 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +if(@USE_OPENSSL@) + find_dependency(OpenSSL @OPENSSL_VERSION_MAJOR@) +endif() +if(@USE_ZLIB@) + find_dependency(ZLIB @ZLIB_VERSION_MAJOR@) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") +check_required_components("@PROJECT_NAME@") + +# Alias for either shared or static library +if(NOT TARGET @PROJECT_NAME@::libcurl) + add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@) +endif() + +# For compatibility with CMake's FindCURL.cmake +set(CURL_LIBRARIES @PROJECT_NAME@::libcurl) +set_and_check(CURL_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") diff --git a/third_party/curl/CMakeLists.txt b/third_party/curl/CMakeLists.txt new file mode 100644 index 0000000000..ac5b4859c2 --- /dev/null +++ b/third_party/curl/CMakeLists.txt @@ -0,0 +1,2180 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# by Tetetest and Sukender (Benoit Neil) + +# Note: By default this CMake build script detects the version of some +# dependencies using `check_symbol_exists`. Those checks do not work in +# the case that both CURL and its dependency are included as sub-projects +# in a larger build using `FetchContent`. To support that case, additional +# variables may be defined by the parent project, ideally in the "extra" +# find package redirect file: +# https://cmake.org/cmake/help/latest/module/FetchContent.html#integrating-with-find-package +# +# The following variables are available: +# HAVE_SSL_SET0_WBIO: `SSL_set0_wbio` present in OpenSSL/wolfSSL +# HAVE_OPENSSL_SRP: `SSL_CTX_set_srp_username` present in OpenSSL/wolfSSL +# HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS +# HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL +# HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in quiche +# HAVE_ECH: ECH API checks for OpenSSL, BoringSSL or wolfSSL +# +# For each of the above variables, if the variable is DEFINED (either +# to ON or OFF), the symbol detection is skipped. If the variable is +# NOT DEFINED, the symbol detection is performed. + +cmake_minimum_required(VERSION 3.7...3.16 FATAL_ERROR) +message(STATUS "Using CMake version ${CMAKE_VERSION}") + +# Collect command-line arguments for buildinfo.txt. +# Must reside at the top of the script to work as expected. +get_cmake_property(_cache_vars CACHE_VARIABLES) +unset(_cmake_args) +foreach(_cache_var ${_cache_vars}) + get_property(_cache_var_helpstring CACHE ${_cache_var} PROPERTY HELPSTRING) + if(_cache_var_helpstring STREQUAL "No help, variable specified on the command line.") + get_property(_cache_var_type CACHE ${_cache_var} PROPERTY TYPE) + if(_cache_var_type STREQUAL "UNINITIALIZED") + set(_cache_var_type) + else() + set(_cache_var_type ":${_cache_var_type}") + endif() + set(_cmake_args "${_cmake_args} -D${_cache_var}${_cache_var_type}=\"${${_cache_var}}\"") + endif() +endforeach() + +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") +include(Utilities) +include(Macros) +include(CMakeDependentOption) +include(CheckCCompilerFlag) + +project(CURL C) + +unset(_target_flags) +if(APPLE) + set(_target_flags "${_target_flags} APPLE") +endif() +if(UNIX) + set(_target_flags "${_target_flags} UNIX") +endif() +if(WIN32) + set(_target_flags "${_target_flags} WIN32") +endif() +if(CYGWIN) + set(_target_flags "${_target_flags} CYGWIN") +endif() +if(MSYS) + set(_target_flags "${_target_flags} MSYS") +endif() +if(CMAKE_COMPILER_IS_GNUCC) + set(_target_flags "${_target_flags} GCC") +endif() +if(MINGW) + set(_target_flags "${_target_flags} MINGW") +endif() +if(MSVC) + set(_target_flags "${_target_flags} MSVC") +endif() +if(VCPKG_TOOLCHAIN) + set(_target_flags "${_target_flags} VCPKG") +endif() +if(CMAKE_CROSSCOMPILING) + set(_target_flags "${_target_flags} CROSS") +endif() +message(STATUS "CMake platform flags:${_target_flags}") + +if(CMAKE_CROSSCOMPILING) + message(STATUS "Cross-compiling: " + "${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR} -> " + "${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}") +endif() + +function(curl_dumpvars) # Dump all defined variables with their values + message("::group::CMake Variable Dump") + get_cmake_property(_vars VARIABLES) + foreach(_var ${_vars}) + message("${_var} = ${${_var}}") + endforeach() + message("::endgroup::") +endfunction() + +file(STRINGS "${CURL_SOURCE_DIR}/include/curl/curlver.h" _curl_version_h_contents REGEX "#define LIBCURL_VERSION( |_NUM )") +string(REGEX MATCH "#define LIBCURL_VERSION \"[^\"]*" CURL_VERSION ${_curl_version_h_contents}) +string(REGEX REPLACE "[^\"]+\"" "" CURL_VERSION ${CURL_VERSION}) +string(REGEX MATCH "#define LIBCURL_VERSION_NUM 0x[0-9a-fA-F]+" CURL_VERSION_NUM ${_curl_version_h_contents}) +string(REGEX REPLACE "[^0]+0x" "" CURL_VERSION_NUM ${CURL_VERSION_NUM}) +unset(_curl_version_h_contents) + +message(STATUS "curl version=[${CURL_VERSION}]") + +if(CMAKE_C_COMPILER_TARGET) + set(OS "\"${CMAKE_C_COMPILER_TARGET}\"") +else() + set(OS "\"${CMAKE_SYSTEM_NAME}\"") +endif() + +include_directories("${CURL_SOURCE_DIR}/include") + +if(NOT DEFINED CMAKE_UNITY_BUILD_BATCH_SIZE) + set(CMAKE_UNITY_BUILD_BATCH_SIZE 0) +endif() + +option(CURL_WERROR "Turn compiler warnings into errors" OFF) +option(PICKY_COMPILER "Enable picky compiler options" ON) +option(BUILD_CURL_EXE "Build curl executable" OFF) +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) +option(BUILD_STATIC_LIBS "Build static libraries" ON) +option(BUILD_STATIC_CURL "Build curl executable with static libcurl" OFF) +option(ENABLE_ARES "Enable c-ares support" OFF) +option(CURL_DISABLE_INSTALL "Disable installation targets" OFF) + +if(WIN32) + option(CURL_STATIC_CRT "Build libcurl with static CRT on Windows (/MT)" OFF) + if(CURL_STATIC_CRT) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd") + endif() + + option(ENABLE_UNICODE "Use the Unicode version of the Windows API functions" OFF) + if(ENABLE_UNICODE) + add_definitions("-DUNICODE" "-D_UNICODE") + if(MINGW) + add_compile_options("-municode") + endif() + endif() + + set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string") + if(CURL_TARGET_WINDOWS_VERSION) + add_definitions("-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") + set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") + endif() + + # Detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT + curl_internal_test(HAVE_WIN32_WINNT) + if(HAVE_WIN32_WINNT) + string(REGEX MATCH ".*_WIN32_WINNT=0x[0-9a-fA-F]+" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") + string(REGEX REPLACE ".*_WIN32_WINNT=" "" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") + string(REGEX REPLACE "0x([0-9a-f][0-9a-f][0-9a-f])$" "0x0\\1" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") # pad to 4 digits + string(TOLOWER "${CURL_TEST_OUTPUT}" HAVE_WIN32_WINNT) + message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}") + endif() + # Avoid storing HAVE_WIN32_WINNT in CMake cache + unset(HAVE_WIN32_WINNT CACHE) +endif() +option(CURL_LTO "Enable compiler Link Time Optimizations" OFF) + +cmake_dependent_option(ENABLE_THREADED_RESOLVER "Enable threaded DNS lookup" + ON "NOT ENABLE_ARES" + OFF) + +include(PickyWarnings) + +option(ENABLE_DEBUG "Enable curl debug features" OFF) +option(ENABLE_CURLDEBUG "Enable TrackMemory feature" ${ENABLE_DEBUG}) + +if(MSVC) + set(ENABLE_CURLDEBUG OFF) # FIXME: TrackMemory + MSVC fails test 558 and 1330. Tested with static build, Debug mode. +endif() + +if(ENABLE_DEBUG) + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "DEBUGBUILD") +endif() + +if(ENABLE_CURLDEBUG) + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURLDEBUG") +endif() + +# For debug libs and exes, add "-d" postfix +if(NOT DEFINED CMAKE_DEBUG_POSTFIX) + set(CMAKE_DEBUG_POSTFIX "-d") +endif() + +set(LIB_STATIC "libcurl_static") +set(LIB_SHARED "libcurl_shared") + +if(NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS) + set(BUILD_STATIC_LIBS ON) +endif() +if(NOT BUILD_STATIC_CURL AND NOT BUILD_SHARED_LIBS) + set(BUILD_STATIC_CURL ON) +elseif(BUILD_STATIC_CURL AND NOT BUILD_STATIC_LIBS) + set(BUILD_STATIC_CURL OFF) +endif() + +# Lib flavour selected for curl tool +set(LIB_SELECTED_FOR_EXE ${LIB_STATIC}) +#if(BUILD_STATIC_CURL) +# set(LIB_SELECTED_FOR_EXE ${LIB_STATIC}) +#else() +# set(LIB_SELECTED_FOR_EXE ${LIB_SHARED}) +#endif() + +# Lib flavour selected for example and test programs. +if(BUILD_SHARED_LIBS) + set(LIB_SELECTED ${LIB_SHARED}) +else() + set(LIB_SELECTED ${LIB_STATIC}) +endif() + +# Override to force-disable or force-enable the use of pkg-config. +if(UNIX OR (MSVC AND VCPKG_TOOLCHAIN)) # Keep in sync with CMake/curl-config.cmake.in + set(_curl_use_pkgconfig_default ON) +else() + set(_curl_use_pkgconfig_default OFF) +endif() +option(CURL_USE_PKGCONFIG "Enable pkg-config to detect dependencies" ${_curl_use_pkgconfig_default}) + +# Initialize CURL_LIBS +set(CURL_LIBS "") +set(CURL_LIBDIRS "") +set(LIBCURL_PC_REQUIRES_PRIVATE "") + +if(ENABLE_ARES) + set(USE_ARES 1) + find_package(Cares REQUIRED) + list(APPEND CURL_LIBS ${CARES_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libcares") + add_definitions("-DCARES_NO_DEPRECATED") # Ignore c-ares deprecation warnings +endif() + +include(CurlSymbolHiding) + +option(CURL_ENABLE_EXPORT_TARGET "Enable CMake export target" ON) +mark_as_advanced(CURL_ENABLE_EXPORT_TARGET) + +option(CURL_DISABLE_ALTSVC "Disable alt-svc support" OFF) +mark_as_advanced(CURL_DISABLE_ALTSVC) +option(CURL_DISABLE_SRP "Disable TLS-SRP support" OFF) +mark_as_advanced(CURL_DISABLE_SRP) +option(CURL_DISABLE_COOKIES "Disable cookies support" OFF) +mark_as_advanced(CURL_DISABLE_COOKIES) +option(CURL_DISABLE_BASIC_AUTH "Disable Basic authentication" OFF) +mark_as_advanced(CURL_DISABLE_BASIC_AUTH) +option(CURL_DISABLE_BEARER_AUTH "Disable Bearer authentication" OFF) +mark_as_advanced(CURL_DISABLE_BEARER_AUTH) +option(CURL_DISABLE_DIGEST_AUTH "Disable Digest authentication" OFF) +mark_as_advanced(CURL_DISABLE_DIGEST_AUTH) +option(CURL_DISABLE_KERBEROS_AUTH "Disable Kerberos authentication" OFF) +mark_as_advanced(CURL_DISABLE_KERBEROS_AUTH) +option(CURL_DISABLE_NEGOTIATE_AUTH "Disable negotiate authentication" OFF) +mark_as_advanced(CURL_DISABLE_NEGOTIATE_AUTH) +option(CURL_DISABLE_AWS "Disable AWS-SIG4" OFF) +mark_as_advanced(CURL_DISABLE_AWS) +option(CURL_DISABLE_DICT "Disable DICT" OFF) +mark_as_advanced(CURL_DISABLE_DICT) +option(CURL_DISABLE_DOH "Disable DNS-over-HTTPS" OFF) +mark_as_advanced(CURL_DISABLE_DOH) +option(CURL_DISABLE_FILE "Disable FILE" OFF) +mark_as_advanced(CURL_DISABLE_FILE) +cmake_dependent_option(CURL_DISABLE_FORM_API "Disable form-api" + OFF "NOT CURL_DISABLE_MIME" + ON) +mark_as_advanced(CURL_DISABLE_FORM_API) +option(CURL_DISABLE_FTP "Disable FTP" OFF) +mark_as_advanced(CURL_DISABLE_FTP) +option(CURL_DISABLE_GETOPTIONS "Disable curl_easy_options API for existing options to curl_easy_setopt" OFF) +mark_as_advanced(CURL_DISABLE_GETOPTIONS) +option(CURL_DISABLE_GOPHER "Disable Gopher" OFF) +mark_as_advanced(CURL_DISABLE_GOPHER) +option(CURL_DISABLE_HEADERS_API "Disable headers-api support" OFF) +mark_as_advanced(CURL_DISABLE_HEADERS_API) +option(CURL_DISABLE_HSTS "Disable HSTS support" OFF) +mark_as_advanced(CURL_DISABLE_HSTS) +option(CURL_DISABLE_HTTP "Disable HTTP" OFF) +mark_as_advanced(CURL_DISABLE_HTTP) +option(CURL_DISABLE_HTTP_AUTH "Disable all HTTP authentication methods" OFF) +mark_as_advanced(CURL_DISABLE_HTTP_AUTH) +option(CURL_DISABLE_IMAP "Disable IMAP" OFF) +mark_as_advanced(CURL_DISABLE_IMAP) +option(CURL_DISABLE_LDAP "Disable LDAP" OFF) +mark_as_advanced(CURL_DISABLE_LDAP) +option(CURL_DISABLE_LDAPS "Disable LDAPS" ${CURL_DISABLE_LDAP}) +mark_as_advanced(CURL_DISABLE_LDAPS) +option(CURL_DISABLE_LIBCURL_OPTION "Disable --libcurl option from the curl tool" OFF) +mark_as_advanced(CURL_DISABLE_LIBCURL_OPTION) +option(CURL_DISABLE_MIME "Disable MIME support" OFF) +mark_as_advanced(CURL_DISABLE_MIME) +option(CURL_DISABLE_MQTT "Disable MQTT" OFF) +mark_as_advanced(CURL_DISABLE_BINDLOCAL) +option(CURL_DISABLE_BINDLOCAL "Disable local binding support" OFF) +mark_as_advanced(CURL_DISABLE_MQTT) +option(CURL_DISABLE_NETRC "Disable netrc parser" OFF) +mark_as_advanced(CURL_DISABLE_NETRC) +option(CURL_DISABLE_NTLM "Disable NTLM support" OFF) +mark_as_advanced(CURL_DISABLE_NTLM) +option(CURL_DISABLE_PARSEDATE "Disable date parsing" OFF) +mark_as_advanced(CURL_DISABLE_PARSEDATE) +option(CURL_DISABLE_POP3 "Disable POP3" OFF) +mark_as_advanced(CURL_DISABLE_POP3) +option(CURL_DISABLE_PROGRESS_METER "Disable built-in progress meter" OFF) +mark_as_advanced(CURL_DISABLE_PROGRESS_METER) +option(CURL_DISABLE_PROXY "Disable proxy support" OFF) +mark_as_advanced(CURL_DISABLE_PROXY) +option(CURL_DISABLE_RTSP "Disable RTSP" OFF) +mark_as_advanced(CURL_DISABLE_SHA512_256) +option(CURL_DISABLE_SHA512_256 "Disable SHA-512/256 hash algorithm" OFF) +mark_as_advanced(CURL_DISABLE_RTSP) +option(CURL_DISABLE_SHUFFLE_DNS "Disable shuffle DNS feature" OFF) +mark_as_advanced(CURL_DISABLE_SHUFFLE_DNS) +option(CURL_DISABLE_SMB "Disable SMB" OFF) +mark_as_advanced(CURL_DISABLE_SMB) +option(CURL_DISABLE_SMTP "Disable SMTP" OFF) +mark_as_advanced(CURL_DISABLE_SMTP) +option(CURL_DISABLE_SOCKETPAIR "Disable use of socketpair for curl_multi_poll" OFF) +mark_as_advanced(CURL_DISABLE_SOCKETPAIR) +option(CURL_DISABLE_TELNET "Disable Telnet" OFF) +mark_as_advanced(CURL_DISABLE_TELNET) +option(CURL_DISABLE_TFTP "Disable TFTP" OFF) +mark_as_advanced(CURL_DISABLE_TFTP) +option(CURL_DISABLE_VERBOSE_STRINGS "Disable verbose strings" OFF) +mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS) + +if(CURL_DISABLE_HTTP) + set(CURL_DISABLE_RTSP ON) + set(CURL_DISABLE_ALTSVC ON) + set(CURL_DISABLE_HSTS ON) +endif() + +# Corresponds to HTTP_ONLY in lib/curl_setup.h +option(HTTP_ONLY "Disable all protocols except HTTP (This overrides all CURL_DISABLE_* options)" ON) +mark_as_advanced(HTTP_ONLY) + +if(HTTP_ONLY) + set(CURL_DISABLE_DICT ON) + set(CURL_DISABLE_FILE ON) + set(CURL_DISABLE_FTP ON) + set(CURL_DISABLE_GOPHER ON) + set(CURL_DISABLE_IMAP ON) + set(CURL_DISABLE_LDAP ON) + set(CURL_DISABLE_LDAPS ON) + set(CURL_DISABLE_MQTT ON) + set(CURL_DISABLE_POP3 ON) + set(CURL_DISABLE_RTSP ON) + set(CURL_DISABLE_SMB ON) + set(CURL_DISABLE_SMTP ON) + set(CURL_DISABLE_TELNET ON) + set(CURL_DISABLE_TFTP ON) +endif() + +if(WINDOWS_STORE) + set(CURL_DISABLE_TELNET ON) # telnet code needs fixing to compile for UWP. +endif() + +option(ENABLE_IPV6 "Enable IPv6 support" ON) +mark_as_advanced(ENABLE_IPV6) +if(ENABLE_IPV6 AND NOT WIN32) + include(CheckStructHasMember) + check_struct_has_member("struct sockaddr_in6" "sin6_addr" "netinet/in.h" + HAVE_SOCKADDR_IN6_SIN6_ADDR) + check_struct_has_member("struct sockaddr_in6" "sin6_scope_id" "netinet/in.h" + HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) + if(NOT HAVE_SOCKADDR_IN6_SIN6_ADDR) + message(WARNING "struct sockaddr_in6 not available, disabling IPv6 support") + # Force the feature off as this name is used as guard macro... + set(ENABLE_IPV6 OFF CACHE BOOL "Enable IPv6 support" FORCE) + endif() + + if(APPLE AND NOT ENABLE_ARES) + set(_use_core_foundation_and_core_services ON) + + find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration") + mark_as_advanced(SYSTEMCONFIGURATION_FRAMEWORK) + if(NOT SYSTEMCONFIGURATION_FRAMEWORK) + message(FATAL_ERROR "SystemConfiguration framework not found") + endif() + + list(APPEND CURL_LIBS "-framework SystemConfiguration") + endif() +endif() +if(ENABLE_IPV6) + set(USE_IPV6 ON) +endif() + +find_package(Perl) + +option(BUILD_LIBCURL_DOCS "Build libcurl man pages" OFF) +option(BUILD_MISC_DOCS "Build misc man pages (e.g. curl-config and mk-ca-bundle)" OFF) +option(ENABLE_CURL_MANUAL "Build the man page for curl and enable its -M/--manual option" OFF) + +if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) + if(PERL_FOUND) + set(HAVE_MANUAL_TOOLS ON) + endif() + if(NOT HAVE_MANUAL_TOOLS) + message(WARNING "Perl not found. Will not build manuals.") + endif() +endif() + +# Disable warnings on Borland to avoid changing 3rd party code. +if(BORLAND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w-") +endif() + +# If we are on AIX, do the _ALL_SOURCE magic +if(CMAKE_SYSTEM_NAME STREQUAL "AIX") + add_definitions("-D_ALL_SOURCE") +endif() + +# If we are on Haiku, make sure that the network library is brought in. +if(CMAKE_SYSTEM_NAME STREQUAL "Haiku") + list(APPEND CURL_LIBS "network") +endif() + +# Include all the necessary files for macros +include(CMakePushCheckState) +include(CheckFunctionExists) +include(CheckIncludeFile) +include(CheckIncludeFiles) +include(CheckLibraryExists) +include(CheckSymbolExists) +include(CheckTypeSize) +include(CheckCSourceCompiles) + +# Preload settings on Windows +if(WIN32) + include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake) +endif() + +if(ENABLE_THREADED_RESOLVER) + if(WIN32) + set(USE_THREADS_WIN32 ON) + else() + find_package(Threads REQUIRED) + set(USE_THREADS_POSIX ${CMAKE_USE_PTHREADS_INIT}) + set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT}) + list(APPEND CURL_LIBS ${CMAKE_THREAD_LIBS_INIT}) + endif() +endif() + +# Check for all needed libraries +check_library_exists("socket" "connect" "" HAVE_LIBSOCKET) +if(HAVE_LIBSOCKET) + set(CURL_LIBS "socket;${CURL_LIBS}") +endif() + +check_function_exists("gethostname" HAVE_GETHOSTNAME) + +if(WIN32) + list(APPEND CURL_LIBS "ws2_32" "bcrypt") +endif() + +# Check SSL libraries +option(CURL_ENABLE_SSL "Enable SSL support" ON) + +if(CURL_DEFAULT_SSL_BACKEND) + set(_valid_default_ssl_backend FALSE) +endif() + +if(APPLE) + cmake_dependent_option(CURL_USE_SECTRANSP "Enable Apple OS native SSL/TLS" OFF CURL_ENABLE_SSL OFF) +endif() +if(WIN32) + cmake_dependent_option(CURL_USE_SCHANNEL "Enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF) + option(CURL_WINDOWS_SSPI "Enable SSPI on Windows" ${CURL_USE_SCHANNEL}) +endif() +cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_WOLFSSL "Enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_GNUTLS "Enable GnuTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS" OFF CURL_ENABLE_SSL OFF) + +set(_openssl_default ON) +if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_WOLFSSL) + set(_openssl_default OFF) +endif() +cmake_dependent_option(CURL_USE_OPENSSL "Enable OpenSSL for SSL/TLS" ${_openssl_default} CURL_ENABLE_SSL OFF) +set(CURL_USE_OPENSSL OFF) +option(USE_OPENSSL_QUIC "Use OpenSSL and nghttp3 libraries for HTTP/3 support" OFF) +if(USE_OPENSSL_QUIC AND NOT CURL_USE_OPENSSL) + message(WARNING "OpenSSL QUIC has been requested, but without enabling OpenSSL. Will not enable QUIC.") + set(USE_OPENSSL_QUIC OFF) +endif() +option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF) + +count_true(_enabled_ssl_options_count + CURL_USE_SCHANNEL + CURL_USE_SECTRANSP + CURL_USE_OPENSSL + CURL_USE_MBEDTLS + CURL_USE_BEARSSL + CURL_USE_WOLFSSL + CURL_USE_GNUTLS + CURL_USE_RUSTLS +) +if(_enabled_ssl_options_count GREATER 1) + set(CURL_WITH_MULTI_SSL ON) +elseif(_enabled_ssl_options_count EQUAL 0) + set(CURL_DISABLE_HSTS ON) +endif() +message(status "++++++${_enabled_ssl_options_count}") +if(CURL_USE_SCHANNEL) + set(_ssl_enabled ON) + set(USE_SCHANNEL ON) # Windows native SSL/TLS support + set(USE_WINDOWS_SSPI ON) # CURL_USE_SCHANNEL implies CURL_WINDOWS_SSPI + + if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "schannel") + set(_valid_default_ssl_backend TRUE) + endif() +endif() +if(CURL_WINDOWS_SSPI) + set(USE_WINDOWS_SSPI ON) +endif() + +if(CURL_USE_SECTRANSP) + set(_use_core_foundation_and_core_services ON) + + find_library(SECURITY_FRAMEWORK "Security") + mark_as_advanced(SECURITY_FRAMEWORK) + if(NOT SECURITY_FRAMEWORK) + message(FATAL_ERROR "Security framework not found") + endif() + + set(_ssl_enabled ON) + set(USE_SECTRANSP ON) + list(APPEND CURL_LIBS "-framework Security") + + if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "secure-transport") + set(_valid_default_ssl_backend TRUE) + endif() + + message(WARNING "Secure Transport does not support TLS 1.3.") +endif() + +if(_use_core_foundation_and_core_services) + find_library(COREFOUNDATION_FRAMEWORK "CoreFoundation") + mark_as_advanced(COREFOUNDATION_FRAMEWORK) + find_library(CORESERVICES_FRAMEWORK "CoreServices") + mark_as_advanced(CORESERVICES_FRAMEWORK) + + if(NOT COREFOUNDATION_FRAMEWORK) + message(FATAL_ERROR "CoreFoundation framework not found") + endif() + if(NOT CORESERVICES_FRAMEWORK) + message(FATAL_ERROR "CoreServices framework not found") + endif() + + list(APPEND CURL_LIBS "-framework CoreFoundation" "-framework CoreServices") +endif() + +if(CURL_USE_OPENSSL) + find_package(OpenSSL REQUIRED) + set(_ssl_enabled ON) + set(USE_OPENSSL ON) + + # Depend on OpenSSL via imported targets. This allows our dependents to + # get our dependencies transitively. + list(APPEND CURL_LIBS OpenSSL::SSL OpenSSL::Crypto) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "openssl") + + if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "openssl") + set(_valid_default_ssl_backend TRUE) + endif() + set(_curl_ca_bundle_supported TRUE) + + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + if(NOT DEFINED HAVE_BORINGSSL) + check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL) + endif() + if(NOT DEFINED HAVE_AWSLC) + check_symbol_exists("OPENSSL_IS_AWSLC" "openssl/base.h" HAVE_AWSLC) + endif() +endif() + +if(CURL_USE_MBEDTLS) + find_package(MbedTLS REQUIRED) + set(_ssl_enabled ON) + set(USE_MBEDTLS ON) + list(APPEND CURL_LIBS ${MBEDTLS_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "mbedtls") + include_directories(${MBEDTLS_INCLUDE_DIRS}) + + if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "mbedtls") + set(_valid_default_ssl_backend TRUE) + endif() + set(_curl_ca_bundle_supported TRUE) +endif() + +if(CURL_USE_BEARSSL) + find_package(BearSSL REQUIRED) + set(_ssl_enabled ON) + set(USE_BEARSSL ON) + list(APPEND CURL_LIBS ${BEARSSL_LIBRARIES}) + include_directories(${BEARSSL_INCLUDE_DIRS}) + + if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "bearssl") + set(_valid_default_ssl_backend TRUE) + endif() + set(_curl_ca_bundle_supported TRUE) + + message(WARNING "BearSSL does not support TLS 1.3.") +endif() + +if(CURL_USE_WOLFSSL) + find_package(WolfSSL REQUIRED) + set(_ssl_enabled ON) + set(USE_WOLFSSL ON) + list(APPEND CURL_LIBS ${WOLFSSL_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "wolfssl") + include_directories(${WOLFSSL_INCLUDE_DIRS}) + + if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "wolfssl") + set(_valid_default_ssl_backend TRUE) + endif() + set(_curl_ca_bundle_supported TRUE) +endif() + +if(CURL_USE_GNUTLS) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(GNUTLS "gnutls") + if(GNUTLS_FOUND) + set(GNUTLS_LIBRARIES ${GNUTLS_LINK_LIBRARIES}) + endif() + endif() + if(NOT GNUTLS_FOUND) + find_package(GnuTLS REQUIRED) + endif() + find_package(Nettle REQUIRED) + set(_ssl_enabled ON) + set(USE_GNUTLS ON) + list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES} ${NETTLE_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${NETTLE_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "gnutls" "nettle") + include_directories(${GNUTLS_INCLUDE_DIRS} ${NETTLE_INCLUDE_DIRS}) + link_directories(${NETTLE_LIBRARY_DIRS}) + if(NETTLE_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NETTLE_CFLAGS}") + endif() + + if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "gnutls") + set(_valid_default_ssl_backend TRUE) + endif() + set(_curl_ca_bundle_supported TRUE) + + if(NOT DEFINED HAVE_GNUTLS_SRP AND NOT CURL_DISABLE_SRP) + cmake_push_check_state() + set(CMAKE_REQUIRED_INCLUDES ${GNUTLS_INCLUDE_DIRS}) + set(CMAKE_REQUIRED_LIBRARIES ${GNUTLS_LIBRARIES}) + check_symbol_exists("gnutls_srp_verifier" "gnutls/gnutls.h" HAVE_GNUTLS_SRP) + cmake_pop_check_state() + endif() +endif() + +if(CURL_USE_RUSTLS) + find_package(Rustls REQUIRED) + set(_ssl_enabled ON) + set(USE_RUSTLS ON) + list(APPEND CURL_LIBS ${RUSTLS_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "rustls") + include_directories(${RUSTLS_INCLUDE_DIRS}) + + if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "rustls") + set(_valid_default_ssl_backend TRUE) + endif() + set(_curl_ca_bundle_supported TRUE) +endif() + +if(CURL_DEFAULT_SSL_BACKEND AND NOT _valid_default_ssl_backend) + message(FATAL_ERROR "CURL_DEFAULT_SSL_BACKEND '${CURL_DEFAULT_SSL_BACKEND}' not enabled.") +endif() + +# Keep ZLIB detection after TLS detection, +# and before calling openssl_check_symbol_exists(). + +set(HAVE_LIBZ OFF) +set(USE_ZLIB OFF) +optional_dependency(ZLIB) +if(ZLIB_FOUND) + set(HAVE_LIBZ ON) + set(USE_ZLIB ON) + + # Depend on ZLIB via imported targets. This allows our dependents to + # get our dependencies transitively. + list(APPEND CURL_LIBS ZLIB::ZLIB) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "zlib") + list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS}) +endif() + +option(CURL_BROTLI "Use brotli" OFF) +set(HAVE_BROTLI OFF) +if(CURL_BROTLI) + find_package(Brotli REQUIRED) + if(BROTLI_FOUND) + set(HAVE_BROTLI ON) + list(APPEND CURL_LIBS ${BROTLI_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libbrotlidec") + include_directories(${BROTLI_INCLUDE_DIRS}) + list(APPEND CMAKE_REQUIRED_INCLUDES ${BROTLI_INCLUDE_DIRS}) + endif() +endif() + +option(CURL_ZSTD "Use zstd" OFF) +set(HAVE_ZSTD OFF) +if(CURL_ZSTD) + find_package(Zstd REQUIRED) + if(ZSTD_FOUND AND NOT ZSTD_VERSION VERSION_LESS 1.0.0) + set(HAVE_ZSTD ON) + list(APPEND CURL_LIBS ${ZSTD_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libzstd") + include_directories(${ZSTD_INCLUDE_DIRS}) + else() + message(WARNING "zstd v1.0.0 or newer is required, disabling zstd support.") + endif() +endif() + +# Check symbol in an OpenSSL-like TLS backend, or in _extra_libs depending on it. +macro(openssl_check_symbol_exists _symbol _files _variable _extra_libs) + cmake_push_check_state() + if(USE_OPENSSL) + set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") + set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") + if(HAVE_LIBZ) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}") + endif() + if(WIN32) + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") + list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt") # for OpenSSL/LibreSSL + endif() + elseif(USE_WOLFSSL) + set(CMAKE_REQUIRED_INCLUDES "${WOLFSSL_INCLUDE_DIRS}") + set(CMAKE_REQUIRED_LIBRARIES "${WOLFSSL_LIBRARIES}") + if(HAVE_LIBZ) + list(APPEND CMAKE_REQUIRED_INCLUDES "${ZLIB_INCLUDE_DIRS}") # Public wolfSSL headers require zlib headers + list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}") + endif() + if(WIN32) + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32" "crypt32") + endif() + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DHAVE_UINTPTR_T") # to pull in stdint.h (as of wolfSSL v5.5.4) + endif() + list(APPEND CMAKE_REQUIRED_LIBRARIES "${_extra_libs}") + check_symbol_exists("${_symbol}" "${_files}" "${_variable}") + cmake_pop_check_state() +endmacro() + +# Ensure that the OpenSSL fork actually supports QUIC. +macro(openssl_check_quic) + if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD) + if(USE_OPENSSL) + openssl_check_symbol_exists("SSL_CTX_set_quic_method" "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD "") + elseif(USE_WOLFSSL) + openssl_check_symbol_exists("wolfSSL_set_quic_method" "wolfssl/options.h;wolfssl/openssl/ssl.h" + HAVE_SSL_CTX_SET_QUIC_METHOD "") + endif() + endif() + if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD) + message(FATAL_ERROR "QUIC support is missing in OpenSSL fork. Try setting -DOPENSSL_ROOT_DIR") + endif() +endmacro() + +if(USE_WOLFSSL) + openssl_check_symbol_exists("wolfSSL_DES_ecb_encrypt" "wolfssl/openssl/des.h" HAVE_WOLFSSL_DES_ECB_ENCRYPT "") + openssl_check_symbol_exists("wolfSSL_BIO_set_shutdown" "wolfssl/ssl.h" HAVE_WOLFSSL_FULL_BIO "") +endif() + +if(USE_OPENSSL OR USE_WOLFSSL) + if(NOT DEFINED HAVE_SSL_SET0_WBIO) + openssl_check_symbol_exists("SSL_set0_wbio" "openssl/ssl.h" HAVE_SSL_SET0_WBIO "") + endif() + if(NOT DEFINED HAVE_OPENSSL_SRP AND NOT CURL_DISABLE_SRP) + openssl_check_symbol_exists("SSL_CTX_set_srp_username" "openssl/ssl.h" HAVE_OPENSSL_SRP "") + endif() +endif() + +option(USE_HTTPSRR "Enable HTTPS RR support for ECH (experimental)" OFF) +option(USE_ECH "Enable ECH support" OFF) +if(USE_ECH) + if(USE_OPENSSL OR USE_WOLFSSL) + # Be sure that the TLS library actually supports ECH. + if(NOT DEFINED HAVE_ECH) + if(USE_OPENSSL AND HAVE_BORINGSSL) + openssl_check_symbol_exists("SSL_set1_ech_config_list" "openssl/ssl.h" HAVE_ECH "") + elseif(USE_OPENSSL) + openssl_check_symbol_exists("SSL_ech_set1_echconfig" "openssl/ech.h" HAVE_ECH "") + elseif(USE_WOLFSSL) + openssl_check_symbol_exists("wolfSSL_CTX_GenerateEchConfig" "wolfssl/options.h;wolfssl/ssl.h" HAVE_ECH "") + endif() + endif() + if(NOT HAVE_ECH) + message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/wolfSSL") + else() + message(STATUS "ECH enabled.") + endif() + else() + message(FATAL_ERROR "ECH requires ECH-enablded OpenSSL, BoringSSL or wolfSSL") + endif() +endif() + +option(USE_NGHTTP2 "Use nghttp2 library" OFF) +set(USE_NGHTTP2 OFF) +if(USE_NGHTTP2) + find_package(NGHTTP2) + if(NGHTTP2_FOUND) + include_directories(${NGHTTP2_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${NGHTTP2_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libnghttp2") + else() + set(USE_NGHTTP2 OFF) + endif() +endif() + +option(USE_NGTCP2 "Use ngtcp2 and nghttp3 libraries for HTTP/3 support" OFF) +if(USE_NGTCP2) + if(USE_OPENSSL OR USE_WOLFSSL) + if(USE_WOLFSSL) + find_package(NGTCP2 REQUIRED "wolfSSL") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libngtcp2_crypto_wolfssl") + elseif(HAVE_BORINGSSL OR HAVE_AWSLC) + find_package(NGTCP2 REQUIRED "BoringSSL") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libngtcp2_crypto_boringssl") + else() + find_package(NGTCP2 REQUIRED "quictls") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libngtcp2_crypto_quictls") + endif() + openssl_check_quic() + elseif(USE_GNUTLS) + find_package(NGTCP2 REQUIRED "GnuTLS") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libngtcp2_crypto_gnutls") + else() + message(FATAL_ERROR "ngtcp2 requires OpenSSL, wolfSSL or GnuTLS") + endif() + set(USE_NGTCP2 ON) + include_directories(${NGTCP2_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${NGTCP2_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libngtcp2") + + find_package(NGHTTP3 REQUIRED) + set(USE_NGHTTP3 ON) + include_directories(${NGHTTP3_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${NGHTTP3_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libnghttp3") +endif() + +option(USE_QUICHE "Use quiche library for HTTP/3 support" OFF) +if(USE_QUICHE) + if(USE_NGTCP2) + message(FATAL_ERROR "Only one HTTP/3 backend can be selected!") + endif() + find_package(Quiche REQUIRED) + if(NOT HAVE_BORINGSSL) + message(FATAL_ERROR "quiche requires BoringSSL") + endif() + openssl_check_quic() + set(USE_QUICHE ON) + include_directories(${QUICHE_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${QUICHE_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "quiche") + if(NOT DEFINED HAVE_QUICHE_CONN_SET_QLOG_FD) + cmake_push_check_state() + set(CMAKE_REQUIRED_INCLUDES "${QUICHE_INCLUDE_DIRS}") + set(CMAKE_REQUIRED_LIBRARIES "${QUICHE_LIBRARIES}") + check_symbol_exists("quiche_conn_set_qlog_fd" "quiche.h" HAVE_QUICHE_CONN_SET_QLOG_FD) + cmake_pop_check_state() + endif() +endif() + +option(USE_MSH3 "Use msquic library for HTTP/3 support" OFF) +if(USE_MSH3) + if(USE_NGTCP2 OR USE_QUICHE) + message(FATAL_ERROR "Only one HTTP/3 backend can be selected!") + endif() + find_package(MSH3 REQUIRED) + set(USE_MSH3 ON) + include_directories(${MSH3_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${MSH3_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libmsh3") +endif() + +if(USE_OPENSSL_QUIC) + if(USE_NGTCP2 OR USE_QUICHE OR USE_MSH3) + message(FATAL_ERROR "Only one HTTP/3 backend can be selected!") + endif() + find_package(OpenSSL 3.3.0 REQUIRED) + + find_package(NGHTTP3 REQUIRED) + set(USE_NGHTTP3 ON) + include_directories(${NGHTTP3_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${NGHTTP3_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libnghttp3") +endif() + +if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC)) + message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.") +endif() + +if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP)) + set(USE_TLS_SRP 1) +endif() + +if(NOT CURL_DISABLE_LDAP) + if(WIN32 AND NOT WINDOWS_STORE) + option(USE_WIN32_LDAP "Use Windows LDAP implementation" ON) + if(USE_WIN32_LDAP) + list(APPEND CURL_LIBS "wldap32") + if(NOT CURL_DISABLE_LDAPS) + set(HAVE_LDAP_SSL ON) + endif() + endif() + endif() + + set(CMAKE_LDAP_LIB "ldap" CACHE STRING "Name or full path to ldap library") + set(CMAKE_LBER_LIB "lber" CACHE STRING "Name or full path to lber library") + + # Now that we know, we are not using Windows LDAP... + if(NOT USE_WIN32_LDAP) + # Check for LDAP + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) + check_library_exists("${CMAKE_LDAP_LIB}" "ldap_init" "" HAVE_LIBLDAP) + if(HAVE_LIBLDAP) + check_library_exists("${CMAKE_LDAP_LIB};${CMAKE_LBER_LIB}" "ber_init" "" HAVE_LIBLBER) + else() + check_library_exists("${CMAKE_LBER_LIB}" "ber_init" "" HAVE_LIBLBER) + endif() + + set(CMAKE_REQUIRED_INCLUDES_BAK ${CMAKE_REQUIRED_INCLUDES}) + set(CMAKE_LDAP_INCLUDE_DIR "" CACHE STRING "Path to LDAP include directory") + if(CMAKE_LDAP_INCLUDE_DIR) + list(APPEND CMAKE_REQUIRED_INCLUDES ${CMAKE_LDAP_INCLUDE_DIR}) + endif() + check_include_file_concat("ldap.h" HAVE_LDAP_H) + check_include_file_concat("lber.h" HAVE_LBER_H) + + if(NOT HAVE_LDAP_H) + message(STATUS "LDAP_H not found CURL_DISABLE_LDAP set ON") + set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE) + set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_BAK}) # LDAP includes will not be used + elseif(NOT HAVE_LIBLDAP) + message(STATUS "LDAP library '${CMAKE_LDAP_LIB}' not found CURL_DISABLE_LDAP set ON") + set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE) + set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_BAK}) # LDAP includes will not be used + else() + if(CMAKE_LDAP_INCLUDE_DIR) + include_directories(${CMAKE_LDAP_INCLUDE_DIR}) + endif() + set(NEED_LBER_H ON) + unset(_header_list) + if(WIN32) + list(APPEND _header_list "windows.h") + endif() + if(HAVE_SYS_TYPES_H) + list(APPEND _header_list "sys/types.h") + endif() + list(APPEND _header_list "ldap.h") + + set(_include_string "") + foreach(_header IN LISTS _header_list) + set(_include_string "${_include_string}#include <${_header}>\n") + endforeach() + + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DLDAP_DEPRECATED=1") + list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB}) + set(CURL_LIBS "${CMAKE_LDAP_LIB};${CURL_LIBS}") + if(HAVE_LIBLBER) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB}) + set(CURL_LIBS "${CMAKE_LBER_LIB};${CURL_LIBS}") + endif() + + check_c_source_compiles(" + ${_include_string} + int main(int argc, char ** argv) + { + BerValue *bvp = NULL; + BerElement *bep = ber_init(bvp); + ber_free(bep, 1); + return 0; + }" NOT_NEED_LBER_H) + if(NOT_NEED_LBER_H) + set(NEED_LBER_H OFF) + else() + set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -DNEED_LBER_H") + endif() + + check_function_exists("ldap_url_parse" HAVE_LDAP_URL_PARSE) + check_function_exists("ldap_init_fd" HAVE_LDAP_INIT_FD) + + unset(CMAKE_REQUIRED_LIBRARIES) + + check_include_file("ldap_ssl.h" HAVE_LDAP_SSL_H) + + if(HAVE_LDAP_INIT_FD) + set(USE_OPENLDAP ON) + add_definitions("-DLDAP_DEPRECATED=1") + endif() + if(NOT CURL_DISABLE_LDAPS) + set(HAVE_LDAP_SSL ON) + endif() + endif() + endif() +endif() + +# No ldap, no ldaps. +if(CURL_DISABLE_LDAP) + if(NOT CURL_DISABLE_LDAPS) + message(STATUS "LDAP needs to be enabled to support LDAPS") + set(CURL_DISABLE_LDAPS ON CACHE BOOL "" FORCE) + endif() +endif() + +if(WIN32) + option(USE_WIN32_IDN "Use WinIDN for IDN support" OFF) + if(USE_WIN32_IDN) + list(APPEND CURL_LIBS "normaliz") + endif() +else() + set(USE_WIN32_IDN OFF) +endif() + +if(APPLE) + option(USE_APPLE_IDN "Use Apple built-in IDN support" OFF) + if(USE_APPLE_IDN) + cmake_push_check_state() + set(CMAKE_REQUIRED_LIBRARIES "icucore") + check_symbol_exists("uidna_openUTS46" "unicode/uidna.h" HAVE_APPLE_IDN) + cmake_pop_check_state() + if(HAVE_APPLE_IDN) + list(APPEND CURL_LIBS "icucore" "iconv") + else() + set(USE_APPLE_IDN OFF) + endif() + endif() +else() + set(USE_APPLE_IDN OFF) +endif() + +# Check for libidn2 +option(USE_LIBIDN2 "Use libidn2 for IDN support" OFF) +set(USE_LIBIDN2 OFF) +set(HAVE_IDN2_H OFF) +set(HAVE_LIBIDN2 OFF) +if(USE_LIBIDN2 AND NOT USE_APPLE_IDN AND NOT USE_WIN32_IDN) + find_package(Libidn2) + if(LIBIDN2_FOUND) + set(CURL_LIBS "${LIBIDN2_LIBRARIES};${CURL_LIBS}") + list(APPEND CURL_LIBDIRS ${LIBIDN2_LIBRARY_DIRS}) + set(LIBCURL_PC_REQUIRES_PRIVATE "libidn2;${LIBCURL_PC_REQUIRES_PRIVATE}") + include_directories(${LIBIDN2_INCLUDE_DIRS}) + link_directories(${LIBIDN2_LIBRARY_DIRS}) + if(LIBIDN2_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBIDN2_CFLAGS}") + endif() + set(HAVE_IDN2_H 1) + set(HAVE_LIBIDN2 1) + endif() +endif() + +# libpsl +option(CURL_USE_LIBPSL "Use libpsl" OFF) +set(CURL_USE_LIBPSL OFF) +mark_as_advanced(CURL_USE_LIBPSL) +set(USE_LIBPSL OFF) + +if(CURL_USE_LIBPSL) + find_package(Libpsl) # TODO: add REQUIRED to match autotools + if(LIBPSL_FOUND) + + list(APPEND CURL_LIBS ${LIBPSL_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libpsl") + list(APPEND CMAKE_REQUIRED_INCLUDES "${LIBPSL_INCLUDE_DIRS}") + include_directories(${LIBPSL_INCLUDE_DIRS}) + set(USE_LIBPSL ON) + else() + message(WARNING "libpsl is enabled, but not found.") + endif() +endif() + +# libssh2 +option(CURL_USE_LIBSSH2 "Use libssh2" OFF) # FIXME: default is OFF in autotools +mark_as_advanced(CURL_USE_LIBSSH2) +set(USE_LIBSSH2 OFF) + +if(CURL_USE_LIBSSH2) + find_package(Libssh2) + if(LIBSSH2_FOUND) + list(APPEND CURL_LIBS ${LIBSSH2_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libssh2") + list(APPEND CMAKE_REQUIRED_INCLUDES "${LIBSSH2_INCLUDE_DIRS}") + include_directories(${LIBSSH2_INCLUDE_DIRS}) + set(USE_LIBSSH2 ON) + endif() +endif() + +# libssh +option(CURL_USE_LIBSSH "Use libssh" OFF) +mark_as_advanced(CURL_USE_LIBSSH) +if(NOT USE_LIBSSH2 AND CURL_USE_LIBSSH) + find_package(Libssh REQUIRED) + if(LIBSSH_FOUND) + list(APPEND CURL_LIBS ${LIBSSH_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBSSH_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libssh") + include_directories(${LIBSSH_INCLUDE_DIRS}) + link_directories(${LIBSSH_LIBRARY_DIRS}) + if(LIBSSH_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBSSH_CFLAGS}") + endif() + set(USE_LIBSSH ON) + endif() +endif() + +# wolfSSH +option(CURL_USE_WOLFSSH "Use wolfSSH" OFF) +mark_as_advanced(CURL_USE_WOLFSSH) +set(USE_WOLFSSH OFF) +if(NOT USE_LIBSSH2 AND NOT USE_LIBSSH AND CURL_USE_WOLFSSH) + if(USE_WOLFSSL) + find_package(WolfSSH) + if(WOLFSSH_FOUND) + list(APPEND CURL_LIBS ${WOLFSSH_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_INCLUDES "${WOLFSSH_INCLUDE_DIRS}") + include_directories(${WOLFSSH_INCLUDE_DIRS}) + set(USE_WOLFSSH ON) + endif() + else() + message(WARNING "wolfSSH requires wolfSSL. Skipping.") + endif() +endif() + +option(CURL_USE_GSASL "Use libgsasl" OFF) +mark_as_advanced(CURL_USE_GSASL) +if(CURL_USE_GSASL) + find_package(Libgsasl REQUIRED) + if(LIBGSASL_FOUND) + list(APPEND CURL_LIBS ${LIBGSASL_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBGSASL_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libgsasl") + include_directories(${LIBGSASL_INCLUDE_DIRS}) + link_directories(${LIBGSASL_LIBRARY_DIRS}) + if(LIBGSASL_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBGSASL_CFLAGS}") + endif() + set(USE_GSASL ON) + endif() +endif() + +option(CURL_USE_GSSAPI "Use GSSAPI implementation" OFF) +mark_as_advanced(CURL_USE_GSSAPI) + +if(CURL_USE_GSSAPI) + find_package(GSS) + + set(HAVE_GSSAPI ${GSS_FOUND}) + if(GSS_FOUND) + list(APPEND CMAKE_REQUIRED_INCLUDES ${GSS_INCLUDE_DIRS}) + + string(REPLACE ";" " " GSS_CFLAGS "${GSS_CFLAGS}") + string(REPLACE ";" " " GSS_LDFLAGS "${GSS_LDFLAGS}") + + foreach(_dir IN LISTS GSS_LIBRARY_DIRS) + set(GSS_LDFLAGS "${GSS_LDFLAGS} -L\"${_dir}\"") + endforeach() + + check_include_file_concat("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H) + check_include_file_concat("gssapi/gssapi_generic.h" HAVE_GSSAPI_GSSAPI_GENERIC_H) + check_include_file_concat("gssapi/gssapi_krb5.h" HAVE_GSSAPI_GSSAPI_KRB5_H) + + if(GSS_FLAVOUR STREQUAL "MIT") + set(_include_list "") + if(HAVE_GSSAPI_GSSAPI_H) + list(APPEND _include_list "gssapi/gssapi.h") + endif() + if(HAVE_GSSAPI_GSSAPI_GENERIC_H) + list(APPEND _include_list "gssapi/gssapi_generic.h") + endif() + if(HAVE_GSSAPI_GSSAPI_KRB5_H) + list(APPEND _include_list "gssapi/gssapi_krb5.h") + endif() + + if(NOT DEFINED HAVE_GSS_C_NT_HOSTBASED_SERVICE) + set(CMAKE_REQUIRED_FLAGS "${GSS_CFLAGS} ${GSS_LDFLAGS}") + set(CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES}) + check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" ${_include_list} HAVE_GSS_C_NT_HOSTBASED_SERVICE) + unset(CMAKE_REQUIRED_LIBRARIES) + endif() + if(NOT HAVE_GSS_C_NT_HOSTBASED_SERVICE) + set(HAVE_OLD_GSSMIT ON) + endif() + endif() + + include_directories(${GSS_INCLUDE_DIRS}) + link_directories(${GSS_LIBRARY_DIRS}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GSS_CFLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GSS_LDFLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GSS_LDFLAGS}") + list(APPEND CURL_LIBS ${GSS_LIBRARIES}) + if(GSS_FLAVOUR STREQUAL "MIT") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "mit-krb5-gssapi") + else() # Heimdal + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "heimdal-gssapi") + endif() + else() + message(WARNING "GSSAPI support has been requested but no supporting libraries found. Skipping.") + endif() +endif() + +# libuv +option(CURL_USE_LIBUV "Use libuv for event-based tests" OFF) +if(CURL_USE_LIBUV) + if(NOT ENABLE_DEBUG) + message(FATAL_ERROR "Using libuv without debug support enabled is useless") + endif() + find_package(Libuv REQUIRED) + if(LIBUV_FOUND) + list(APPEND CURL_LIBS ${LIBUV_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBUV_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libuv") + include_directories(${LIBUV_INCLUDE_DIRS}) + link_directories(${LIBUV_LIBRARY_DIRS}) + if(LIBUV_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBUV_CFLAGS}") + endif() + set(USE_LIBUV ON) + set(HAVE_UV_H ON) + endif() +endif() + +option(USE_LIBRTMP "Enable librtmp from rtmpdump" OFF) +if(USE_LIBRTMP) + cmake_push_check_state() + set(_extra_libs "rtmp") + if(WIN32) + list(APPEND _extra_libs "winmm") + endif() + openssl_check_symbol_exists("RTMP_Init" "librtmp/rtmp.h" HAVE_LIBRTMP "${_extra_libs}") + cmake_pop_check_state() + if(HAVE_LIBRTMP) + list(APPEND CURL_LIBS "rtmp") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "librtmp") + if(WIN32) + list(APPEND CURL_LIBS "winmm") + endif() + else() + message(WARNING "librtmp requested, but not found or missing OpenSSL. Skipping.") + set(USE_LIBRTMP OFF) + endif() +endif() + +option(ENABLE_UNIX_SOCKETS "Enable Unix domain sockets support" ON) +if(ENABLE_UNIX_SOCKETS) + if(WIN32) + set(USE_UNIX_SOCKETS ON) + else() + include(CheckStructHasMember) + check_struct_has_member("struct sockaddr_un" "sun_path" "sys/un.h" USE_UNIX_SOCKETS) + endif() +else() + unset(USE_UNIX_SOCKETS CACHE) +endif() + +# +# CA handling +# +if(_curl_ca_bundle_supported) + set(CURL_CA_BUNDLE "auto" CACHE + STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + set(CURL_CA_FALLBACK OFF CACHE BOOL + "Set ON to use built-in CA store of TLS backend. Defaults to OFF") + set(CURL_CA_PATH "auto" CACHE + STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + set(CURL_CA_EMBED "" CACHE + STRING "Path to the CA bundle to embed into the curl tool.") + + if(CURL_CA_BUNDLE STREQUAL "") + message(FATAL_ERROR "Invalid value of CURL_CA_BUNDLE. Use 'none', 'auto' or file path.") + elseif(CURL_CA_BUNDLE STREQUAL "none") + unset(CURL_CA_BUNDLE CACHE) + elseif(CURL_CA_BUNDLE STREQUAL "auto") + unset(CURL_CA_BUNDLE CACHE) + if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32) + set(_curl_ca_bundle_autodetect TRUE) + endif() + else() + set(CURL_CA_BUNDLE_SET TRUE) + endif() + mark_as_advanced(CURL_CA_BUNDLE_SET) + + if(CURL_CA_PATH STREQUAL "") + message(FATAL_ERROR "Invalid value of CURL_CA_PATH. Use 'none', 'auto' or directory path.") + elseif(CURL_CA_PATH STREQUAL "none") + unset(CURL_CA_PATH CACHE) + elseif(CURL_CA_PATH STREQUAL "auto") + unset(CURL_CA_PATH CACHE) + if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32) + set(_curl_ca_path_autodetect TRUE) + endif() + else() + set(CURL_CA_PATH_SET TRUE) + endif() + mark_as_advanced(CURL_CA_PATH_SET) + + if(CURL_CA_BUNDLE_SET AND _curl_ca_path_autodetect) + # Skip auto-detection of unset CA path because CA bundle is set explicitly + elseif(CURL_CA_PATH_SET AND _curl_ca_bundle_autodetect) + # Skip auto-detection of unset CA bundle because CA path is set explicitly + elseif(_curl_ca_bundle_autodetect OR _curl_ca_path_autodetect) + # First try auto-detecting a CA bundle, then a CA path + + if(_curl_ca_bundle_autodetect) + foreach(_search_ca_bundle_path IN ITEMS + "/etc/ssl/certs/ca-certificates.crt" + "/etc/pki/tls/certs/ca-bundle.crt" + "/usr/share/ssl/certs/ca-bundle.crt" + "/usr/local/share/certs/ca-root-nss.crt" + "/etc/ssl/cert.pem") + if(EXISTS "${_search_ca_bundle_path}") + message(STATUS "Found CA bundle: ${_search_ca_bundle_path}") + set(CURL_CA_BUNDLE "${_search_ca_bundle_path}" CACHE + STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + set(CURL_CA_BUNDLE_SET TRUE CACHE BOOL "Path to the CA bundle has been set") + break() + endif() + endforeach() + endif() + + if(_curl_ca_path_autodetect AND NOT CURL_CA_PATH_SET) + set(_search_ca_path "/etc/ssl/certs") + file(GLOB _curl_ca_files_found "${_search_ca_path}/[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].0") + if(_curl_ca_files_found) + unset(_curl_ca_files_found) + message(STATUS "Found CA path: ${_search_ca_path}") + set(CURL_CA_PATH "${_search_ca_path}" CACHE + STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + set(CURL_CA_PATH_SET TRUE CACHE BOOL "Path to the CA bundle has been set") + endif() + endif() + endif() + + set(CURL_CA_EMBED_SET FALSE) + if(BUILD_CURL_EXE AND NOT CURL_CA_EMBED STREQUAL "") + if(EXISTS "${CURL_CA_EMBED}") + set(CURL_CA_EMBED_SET TRUE) + else() + message(FATAL_ERROR "CA bundle to embed is missing: '${CURL_CA_EMBED}'") + endif() + endif() +endif() + +# Check for header files +if(WIN32) + set(CURL_INCLUDES ${CURL_INCLUDES} "winsock2.h") + set(CURL_INCLUDES ${CURL_INCLUDES} "ws2tcpip.h") + set(CURL_INCLUDES ${CURL_INCLUDES} "windows.h") + + if(HAVE_WIN32_WINNT) + if(HAVE_WIN32_WINNT LESS 0x0501) + # Windows XP is required for freeaddrinfo, getaddrinfo + message(FATAL_ERROR "Building for Windows XP or newer is required.") + endif() + + # Pre-fill detection results based on target OS version + if(MINGW OR MSVC) + if(HAVE_WIN32_WINNT LESS 0x0600) + set(HAVE_INET_NTOP 0) + set(HAVE_INET_PTON 0) + else() # Windows Vista or newer + set(HAVE_INET_NTOP 1) + set(HAVE_INET_PTON 1) + endif() + unset(HAVE_INET_NTOP CACHE) + unset(HAVE_INET_PTON CACHE) + endif() + endif() +endif() + +check_include_file_concat("sys/eventfd.h" HAVE_SYS_EVENTFD_H) +check_include_file_concat("sys/filio.h" HAVE_SYS_FILIO_H) +check_include_file_concat("sys/wait.h" HAVE_SYS_WAIT_H) +check_include_file_concat("sys/ioctl.h" HAVE_SYS_IOCTL_H) +check_include_file_concat("sys/param.h" HAVE_SYS_PARAM_H) +check_include_file_concat("sys/poll.h" HAVE_SYS_POLL_H) +check_include_file_concat("sys/resource.h" HAVE_SYS_RESOURCE_H) +check_include_file_concat("sys/select.h" HAVE_SYS_SELECT_H) +check_include_file_concat("sys/socket.h" HAVE_SYS_SOCKET_H) +check_include_file_concat("sys/sockio.h" HAVE_SYS_SOCKIO_H) +check_include_file_concat("sys/stat.h" HAVE_SYS_STAT_H) +check_include_file_concat("sys/time.h" HAVE_SYS_TIME_H) +check_include_file_concat("sys/types.h" HAVE_SYS_TYPES_H) +check_include_file_concat("sys/un.h" HAVE_SYS_UN_H) +check_include_file_concat("sys/utime.h" HAVE_SYS_UTIME_H) +check_include_file_concat("sys/xattr.h" HAVE_SYS_XATTR_H) +check_include_file_concat("arpa/inet.h" HAVE_ARPA_INET_H) +check_include_file_concat("dirent.h" HAVE_DIRENT_H) +check_include_file_concat("fcntl.h" HAVE_FCNTL_H) +check_include_file_concat("ifaddrs.h" HAVE_IFADDRS_H) +check_include_file_concat("io.h" HAVE_IO_H) +check_include_file_concat("libgen.h" HAVE_LIBGEN_H) +check_include_file_concat("locale.h" HAVE_LOCALE_H) +check_include_file_concat("net/if.h" HAVE_NET_IF_H) +check_include_file_concat("netdb.h" HAVE_NETDB_H) +check_include_file_concat("netinet/in.h" HAVE_NETINET_IN_H) +check_include_file_concat("netinet/tcp.h" HAVE_NETINET_TCP_H) +check_include_file_concat("netinet/udp.h" HAVE_NETINET_UDP_H) +check_include_file("linux/tcp.h" HAVE_LINUX_TCP_H) + +check_include_file_concat("poll.h" HAVE_POLL_H) +check_include_file_concat("pwd.h" HAVE_PWD_H) +check_include_file_concat("stdatomic.h" HAVE_STDATOMIC_H) +check_include_file_concat("stdbool.h" HAVE_STDBOOL_H) +check_include_file_concat("strings.h" HAVE_STRINGS_H) +check_include_file_concat("stropts.h" HAVE_STROPTS_H) +check_include_file_concat("termio.h" HAVE_TERMIO_H) +check_include_file_concat("termios.h" HAVE_TERMIOS_H) +check_include_file_concat("unistd.h" HAVE_UNISTD_H) +check_include_file_concat("utime.h" HAVE_UTIME_H) + +check_type_size("size_t" SIZEOF_SIZE_T) +check_type_size("ssize_t" SIZEOF_SSIZE_T) +check_type_size("long long" SIZEOF_LONG_LONG) +check_type_size("long" SIZEOF_LONG) +check_type_size("int" SIZEOF_INT) +check_type_size("__int64" SIZEOF___INT64) +check_type_size("time_t" SIZEOF_TIME_T) +check_type_size("suseconds_t" SIZEOF_SUSECONDS_T) +if(NOT HAVE_SIZEOF_SSIZE_T) + if(SIZEOF_LONG EQUAL SIZEOF_SIZE_T) + set(ssize_t "long") + endif() + if(NOT ssize_t AND SIZEOF___INT64 EQUAL SIZEOF_SIZE_T) + set(ssize_t "__int64") + endif() +endif() +# off_t is sized later, after the HAVE_FILE_OFFSET_BITS test + +if(SIZEOF_LONG_LONG) + set(HAVE_LONGLONG 1) +endif() +if(SIZEOF_SUSECONDS_T) + set(HAVE_SUSECONDS_T 1) +endif() + +# Check for some functions that are used +if(WIN32) + set(CMAKE_REQUIRED_LIBRARIES "ws2_32") +elseif(HAVE_LIBSOCKET) + set(CMAKE_REQUIRED_LIBRARIES "socket") +endif() + +check_symbol_exists("fnmatch" "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH) +check_symbol_exists("basename" "${CURL_INCLUDES};string.h" HAVE_BASENAME) +check_symbol_exists("opendir" "${CURL_INCLUDES};dirent.h" HAVE_OPENDIR) +check_symbol_exists("socket" "${CURL_INCLUDES}" HAVE_SOCKET) +check_symbol_exists("sched_yield" "${CURL_INCLUDES};sched.h" HAVE_SCHED_YIELD) +check_symbol_exists("socketpair" "${CURL_INCLUDES}" HAVE_SOCKETPAIR) +check_symbol_exists("recv" "${CURL_INCLUDES}" HAVE_RECV) +check_symbol_exists("send" "${CURL_INCLUDES}" HAVE_SEND) +check_symbol_exists("sendmsg" "${CURL_INCLUDES}" HAVE_SENDMSG) +check_symbol_exists("select" "${CURL_INCLUDES}" HAVE_SELECT) +check_symbol_exists("strdup" "${CURL_INCLUDES};string.h" HAVE_STRDUP) +check_symbol_exists("strtok_r" "${CURL_INCLUDES};string.h" HAVE_STRTOK_R) +check_symbol_exists("strcasecmp" "${CURL_INCLUDES};string.h" HAVE_STRCASECMP) +check_symbol_exists("stricmp" "${CURL_INCLUDES};string.h" HAVE_STRICMP) +check_symbol_exists("strcmpi" "${CURL_INCLUDES};string.h" HAVE_STRCMPI) +check_symbol_exists("memrchr" "${CURL_INCLUDES};string.h" HAVE_MEMRCHR) +check_symbol_exists("alarm" "${CURL_INCLUDES}" HAVE_ALARM) +check_symbol_exists("arc4random" "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM) +check_symbol_exists("fcntl" "${CURL_INCLUDES}" HAVE_FCNTL) +check_symbol_exists("getppid" "${CURL_INCLUDES}" HAVE_GETPPID) +check_symbol_exists("utimes" "${CURL_INCLUDES}" HAVE_UTIMES) + +check_symbol_exists("gettimeofday" "${CURL_INCLUDES}" HAVE_GETTIMEOFDAY) +check_symbol_exists("closesocket" "${CURL_INCLUDES}" HAVE_CLOSESOCKET) +check_symbol_exists("sigsetjmp" "${CURL_INCLUDES};setjmp.h" HAVE_SIGSETJMP) +check_symbol_exists("getpass_r" "${CURL_INCLUDES}" HAVE_GETPASS_R) +check_symbol_exists("getpwuid" "${CURL_INCLUDES}" HAVE_GETPWUID) +check_symbol_exists("getpwuid_r" "${CURL_INCLUDES}" HAVE_GETPWUID_R) +check_symbol_exists("geteuid" "${CURL_INCLUDES}" HAVE_GETEUID) +check_symbol_exists("utime" "${CURL_INCLUDES}" HAVE_UTIME) +check_symbol_exists("gmtime_r" "${CURL_INCLUDES};stdlib.h;time.h" HAVE_GMTIME_R) + +check_symbol_exists("gethostbyname_r" "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R) + +check_symbol_exists("signal" "${CURL_INCLUDES};signal.h" HAVE_SIGNAL) +check_symbol_exists("strtoll" "${CURL_INCLUDES};stdlib.h" HAVE_STRTOLL) +check_symbol_exists("strerror_r" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_STRERROR_R) +check_symbol_exists("sigaction" "signal.h" HAVE_SIGACTION) +check_symbol_exists("siginterrupt" "${CURL_INCLUDES};signal.h" HAVE_SIGINTERRUPT) +check_symbol_exists("getaddrinfo" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_GETADDRINFO) +check_symbol_exists("getifaddrs" "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS) +check_symbol_exists("freeaddrinfo" "${CURL_INCLUDES}" HAVE_FREEADDRINFO) +check_symbol_exists("pipe" "${CURL_INCLUDES}" HAVE_PIPE) +check_symbol_exists("eventfd" "${CURL_INCLUDES};sys/eventfd.h" HAVE_EVENTFD) +check_symbol_exists("ftruncate" "${CURL_INCLUDES}" HAVE_FTRUNCATE) +check_symbol_exists("_fseeki64" "${CURL_INCLUDES};stdio.h" HAVE__FSEEKI64) +check_symbol_exists("getpeername" "${CURL_INCLUDES}" HAVE_GETPEERNAME) +check_symbol_exists("getsockname" "${CURL_INCLUDES}" HAVE_GETSOCKNAME) +check_symbol_exists("if_nametoindex" "${CURL_INCLUDES}" HAVE_IF_NAMETOINDEX) +check_symbol_exists("getrlimit" "${CURL_INCLUDES}" HAVE_GETRLIMIT) +check_symbol_exists("setlocale" "${CURL_INCLUDES}" HAVE_SETLOCALE) +check_symbol_exists("setmode" "${CURL_INCLUDES}" HAVE_SETMODE) +check_symbol_exists("setrlimit" "${CURL_INCLUDES}" HAVE_SETRLIMIT) + +if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900)) + # Earlier MSVC compilers had faulty snprintf implementations + check_symbol_exists("snprintf" "stdio.h" HAVE_SNPRINTF) +endif() +check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) +check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) +if(MSVC AND (MSVC_VERSION LESS_EQUAL 1600)) + set(HAVE_INET_NTOP OFF) +endif() +check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) + +check_symbol_exists("fsetxattr" "${CURL_INCLUDES}" HAVE_FSETXATTR) +if(HAVE_FSETXATTR) + foreach(_curl_test IN ITEMS HAVE_FSETXATTR_5 HAVE_FSETXATTR_6) + curl_internal_test(${_curl_test}) + endforeach() +endif() + +set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") +check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T) +set(HAVE_SA_FAMILY_T ${HAVE_SIZEOF_SA_FAMILY_T}) +set(CMAKE_EXTRA_INCLUDE_FILES "") + +if(WIN32) + set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") + check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY) + set(HAVE_ADDRESS_FAMILY ${HAVE_SIZEOF_ADDRESS_FAMILY}) + set(CMAKE_EXTRA_INCLUDE_FILES "") +endif() + +# Do curl specific tests +foreach(_curl_test IN ITEMS + HAVE_FCNTL_O_NONBLOCK + HAVE_IOCTLSOCKET + HAVE_IOCTLSOCKET_CAMEL + HAVE_IOCTLSOCKET_CAMEL_FIONBIO + HAVE_IOCTLSOCKET_FIONBIO + HAVE_IOCTL_FIONBIO + HAVE_IOCTL_SIOCGIFADDR + HAVE_SETSOCKOPT_SO_NONBLOCK + HAVE_O_NONBLOCK + HAVE_GETHOSTBYNAME_R_3 + HAVE_GETHOSTBYNAME_R_5 + HAVE_GETHOSTBYNAME_R_6 + HAVE_GETHOSTBYNAME_R_3_REENTRANT + HAVE_GETHOSTBYNAME_R_5_REENTRANT + HAVE_GETHOSTBYNAME_R_6_REENTRANT + HAVE_IN_ADDR_T + HAVE_BOOL_T + STDC_HEADERS + HAVE_FILE_OFFSET_BITS + HAVE_ATOMIC + ) + curl_internal_test(${_curl_test}) +endforeach() + +if(HAVE_FILE_OFFSET_BITS) + set(_FILE_OFFSET_BITS 64) + set(CMAKE_REQUIRED_FLAGS "-D_FILE_OFFSET_BITS=64") +endif() +check_type_size("off_t" SIZEOF_OFF_T) + +# fseeko may not exist with _FILE_OFFSET_BITS=64 but can exist with +# _FILE_OFFSET_BITS unset or 32 (e.g. Android ARMv7 with NDK 26b and API level < 24) +# so we need to test fseeko after testing for _FILE_OFFSET_BITS +check_symbol_exists("fseeko" "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO) + +if(HAVE_FSEEKO) + set(HAVE_DECL_FSEEKO 1) +endif() + +# Include this header to get the type +set(CMAKE_REQUIRED_INCLUDES "${CURL_SOURCE_DIR}/include") +set(CMAKE_EXTRA_INCLUDE_FILES "curl/system.h") +check_type_size("curl_off_t" SIZEOF_CURL_OFF_T) +set(CMAKE_EXTRA_INCLUDE_FILES "curl/curl.h") +check_type_size("curl_socket_t" SIZEOF_CURL_SOCKET_T) +set(CMAKE_EXTRA_INCLUDE_FILES "") + +if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) + # On non-Windows and not cross-compiling, check for writable argv[] + include(CheckCSourceRuns) + check_c_source_runs(" + int main(int argc, char **argv) + { + (void)argc; + argv[0][0] = ' '; + return (argv[0][0] == ' ')?0:1; + }" HAVE_WRITABLE_ARGV) +endif() + +unset(CMAKE_REQUIRED_FLAGS) + +option(ENABLE_WEBSOCKETS "Enable WebSockets (experimental)" OFF) + +if(ENABLE_WEBSOCKETS) + if(SIZEOF_CURL_OFF_T GREATER 4) + set(USE_WEBSOCKETS ON) + else() + message(WARNING "curl_off_t is too small to enable WebSockets") + endif() +endif() + +foreach(_curl_test IN ITEMS + HAVE_GLIBC_STRERROR_R + HAVE_POSIX_STRERROR_R + ) + curl_internal_test(${_curl_test}) +endforeach() + +# Check for reentrant +foreach(_curl_test IN ITEMS + HAVE_GETHOSTBYNAME_R_3 + HAVE_GETHOSTBYNAME_R_5 + HAVE_GETHOSTBYNAME_R_6) + if(NOT ${_curl_test}) + if(${_curl_test}_REENTRANT) + set(NEED_REENTRANT 1) + endif() + endif() +endforeach() + +if(NEED_REENTRANT) + foreach(_curl_test IN ITEMS + HAVE_GETHOSTBYNAME_R_3 + HAVE_GETHOSTBYNAME_R_5 + HAVE_GETHOSTBYNAME_R_6) + set(${_curl_test} 0) + if(${_curl_test}_REENTRANT) + set(${_curl_test} 1) + endif() + endforeach() +endif() + +if(NOT WIN32) + # Check clock_gettime(CLOCK_MONOTONIC, x) support + curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC) +endif() + +if(APPLE) + # Check compiler support of __builtin_available() + curl_internal_test(HAVE_BUILTIN_AVAILABLE) +endif() + +# Some other minor tests + +if(NOT HAVE_IN_ADDR_T) + set(in_addr_t "unsigned long") +endif() + +# Check for nonblocking +set(HAVE_DISABLED_NONBLOCKING 1) +if(HAVE_FIONBIO OR + HAVE_IOCTLSOCKET OR + HAVE_IOCTLSOCKET_CASE OR + HAVE_O_NONBLOCK) + unset(HAVE_DISABLED_NONBLOCKING) +endif() + +if(CMAKE_COMPILER_IS_GNUCC AND APPLE) + include(CheckCCompilerFlag) + check_c_compiler_flag("-Wno-long-double" HAVE_C_FLAG_Wno_long_double) + if(HAVE_C_FLAG_Wno_long_double) + # The Mac version of GCC warns about use of long double. Disable it. + get_source_file_property(_mprintf_compile_flags "mprintf.c" COMPILE_FLAGS) + if(_mprintf_compile_flags) + set(_mprintf_compile_flags "${_mprintf_compile_flags} -Wno-long-double") + else() + set(_mprintf_compile_flags "-Wno-long-double") + endif() + set_source_files_properties("mprintf.c" PROPERTIES + COMPILE_FLAGS ${_mprintf_compile_flags}) + endif() +endif() + +include(CMake/OtherTests.cmake) + +add_definitions("-DHAVE_CONFIG_H") + +# For Windows, all compilers used by CMake should support large files +if(WIN32) + set(USE_WIN32_LARGE_FILES ON) + + # Use the manifest embedded in the Windows Resource + set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -DCURL_EMBED_MANIFEST") + + # We use crypto functions that are not available for UWP apps + if(NOT WINDOWS_STORE) + set(USE_WIN32_CRYPTO ON) + endif() + + # Link required libraries for USE_WIN32_CRYPTO or USE_SCHANNEL + if(USE_WIN32_CRYPTO OR USE_SCHANNEL) + list(APPEND CURL_LIBS "advapi32" "crypt32") + endif() +endif() + +if(MSVC) + # Disable default manifest added by CMake + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") + + add_definitions("-D_CRT_SECURE_NO_DEPRECATE" "-D_CRT_NONSTDC_NO_DEPRECATE") + if(CMAKE_C_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + else() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4") + endif() + + # Use multithreaded compilation on VS 2008+ + if(MSVC_VERSION GREATER_EQUAL 1500) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") + endif() +endif() + +if(CURL_WERROR) + if(MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX") + else() + # This assumes clang or gcc style options + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + endif() +endif() + +if(CURL_LTO) + if(CMAKE_VERSION VERSION_LESS 3.9) + message(FATAL_ERROR "Requested LTO but your cmake version ${CMAKE_VERSION} is to old. You need at least 3.9") + endif() + + cmake_policy(SET CMP0069 NEW) + + include(CheckIPOSupported) + check_ipo_supported(RESULT CURL_HAS_LTO OUTPUT CURL_LTO_ERROR LANGUAGES C) + if(CURL_HAS_LTO) + message(STATUS "LTO supported and enabled") + else() + message(FATAL_ERROR "LTO was requested - but compiler does not support it\n${CURL_LTO_ERROR}") + endif() +endif() + + +# Ugly (but functional) way to include "Makefile.inc" by transforming it +# (= regenerate it). +function(transform_makefile_inc _input_file _output_file) + file(READ ${_input_file} _makefile_inc_text) + string(REPLACE "$(top_srcdir)" "\${CURL_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text}) + string(REPLACE "$(top_builddir)" "\${CURL_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text}) + + string(REGEX REPLACE "\\\\\n" "!π!α!" _makefile_inc_text ${_makefile_inc_text}) + string(REGEX REPLACE "([a-zA-Z_][a-zA-Z0-9_]*)[\t ]*=[\t ]*([^\n]*)" "set(\\1 \\2)" _makefile_inc_text ${_makefile_inc_text}) + string(REPLACE "!π!α!" "\n" _makefile_inc_text ${_makefile_inc_text}) + + # Replace $() with ${} + string(REGEX REPLACE "\\$\\(([a-zA-Z_][a-zA-Z0-9_]*)\\)" "\${\\1}" _makefile_inc_text ${_makefile_inc_text}) + # Replace @@ with ${}, even if that may not be read by CMake scripts. + string(REGEX REPLACE "@([a-zA-Z_][a-zA-Z0-9_]*)@" "\${\\1}" _makefile_inc_text ${_makefile_inc_text}) + + file(WRITE ${_output_file} ${_makefile_inc_text}) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_input_file}") +endfunction() + +include(GNUInstallDirs) + +set(CURL_INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") +set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") +set(_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") +set(_project_config "${_generated_dir}/${PROJECT_NAME}Config.cmake") +set(_version_config "${_generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") + +cmake_dependent_option(BUILD_TESTING "Build tests" + ON "PERL_FOUND;NOT CURL_DISABLE_TESTS" + OFF) + +add_subdirectory(lib) + +if(BUILD_CURL_EXE) + add_subdirectory(src) +endif() + +# Helper to populate a list (_items) with a label when conditions +# (the remaining args) are satisfied +macro(_add_if _label) + # Needs to be a macro to allow this indirection + if(${ARGN}) + set(_items ${_items} "${_label}") + endif() +endmacro() + +# NTLM support requires crypto functions from various SSL libs. +# These conditions must match those in lib/curl_setup.h. +if(NOT CURL_DISABLE_NTLM AND + (USE_OPENSSL OR + USE_MBEDTLS OR + USE_GNUTLS OR + USE_SECTRANSP OR + USE_WIN32_CRYPTO OR + (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT))) + set(_use_curl_ntlm_core ON) +endif() + +# Clear list and try to detect available protocols +unset(_items) +_add_if("HTTP" NOT CURL_DISABLE_HTTP) +_add_if("IPFS" NOT CURL_DISABLE_HTTP) +_add_if("IPNS" NOT CURL_DISABLE_HTTP) +_add_if("HTTPS" NOT CURL_DISABLE_HTTP AND _ssl_enabled) +_add_if("FTP" NOT CURL_DISABLE_FTP) +_add_if("FTPS" NOT CURL_DISABLE_FTP AND _ssl_enabled) +_add_if("FILE" NOT CURL_DISABLE_FILE) +_add_if("TELNET" NOT CURL_DISABLE_TELNET) +_add_if("LDAP" NOT CURL_DISABLE_LDAP) +# CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS +_add_if("LDAPS" NOT CURL_DISABLE_LDAPS AND + ((USE_OPENLDAP AND _ssl_enabled) OR + (NOT USE_OPENLDAP AND HAVE_LDAP_SSL))) +_add_if("DICT" NOT CURL_DISABLE_DICT) +_add_if("TFTP" NOT CURL_DISABLE_TFTP) +_add_if("GOPHER" NOT CURL_DISABLE_GOPHER) +_add_if("GOPHERS" NOT CURL_DISABLE_GOPHER AND _ssl_enabled) +_add_if("POP3" NOT CURL_DISABLE_POP3) +_add_if("POP3S" NOT CURL_DISABLE_POP3 AND _ssl_enabled) +_add_if("IMAP" NOT CURL_DISABLE_IMAP) +_add_if("IMAPS" NOT CURL_DISABLE_IMAP AND _ssl_enabled) +_add_if("SMB" NOT CURL_DISABLE_SMB AND + _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) +_add_if("SMBS" NOT CURL_DISABLE_SMB AND _ssl_enabled AND + _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) +_add_if("SMTP" NOT CURL_DISABLE_SMTP) +_add_if("SMTPS" NOT CURL_DISABLE_SMTP AND _ssl_enabled) +_add_if("SCP" USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH) +_add_if("SFTP" USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH) +_add_if("RTSP" NOT CURL_DISABLE_RTSP) +_add_if("RTMP" USE_LIBRTMP) +_add_if("MQTT" NOT CURL_DISABLE_MQTT) +_add_if("WS" USE_WEBSOCKETS) +_add_if("WSS" USE_WEBSOCKETS AND _ssl_enabled) +if(_items) + list(SORT _items) +endif() +string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}") +string(TOLOWER "${SUPPORT_PROTOCOLS}" _support_protocols_lower) +message(STATUS "Protocols: ${_support_protocols_lower}") + +# Clear list and try to detect available features +unset(_items) +_add_if("SSL" _ssl_enabled) +_add_if("IPv6" ENABLE_IPV6) +_add_if("UnixSockets" USE_UNIX_SOCKETS) +_add_if("libz" HAVE_LIBZ) +_add_if("brotli" HAVE_BROTLI) +_add_if("gsasl" USE_GSASL) +_add_if("zstd" HAVE_ZSTD) +_add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) +_add_if("IDN" (HAVE_LIBIDN2 AND HAVE_IDN2_H) OR + USE_WIN32_IDN OR + USE_APPLE_IDN) +_add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND + ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES)) +_add_if("SSPI" USE_WINDOWS_SSPI) +_add_if("GSS-API" HAVE_GSSAPI) +_add_if("alt-svc" NOT CURL_DISABLE_ALTSVC) +_add_if("HSTS" NOT CURL_DISABLE_HSTS) +_add_if("SPNEGO" NOT CURL_DISABLE_NEGOTIATE_AUTH AND + (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) +_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND + (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) +_add_if("NTLM" NOT (CURL_DISABLE_NTLM) AND + (_use_curl_ntlm_core OR USE_WINDOWS_SSPI)) +_add_if("TLS-SRP" USE_TLS_SRP) +_add_if("HTTP2" USE_NGHTTP2) +_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC) +_add_if("MultiSSL" CURL_WITH_MULTI_SSL) +_add_if("HTTPS-proxy" _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS + OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR + USE_MBEDTLS OR USE_SECTRANSP OR + (USE_WOLFSSL AND HAVE_WOLFSSL_FULL_BIO))) +_add_if("Unicode" ENABLE_UNICODE) +_add_if("threadsafe" HAVE_ATOMIC OR + (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR + (WIN32 AND HAVE_WIN32_WINNT GREATER_EQUAL 0x0600)) +_add_if("Debug" ENABLE_DEBUG) +_add_if("TrackMemory" ENABLE_CURLDEBUG) +_add_if("ECH" _ssl_enabled AND HAVE_ECH) +_add_if("PSL" USE_LIBPSL) +_add_if("CAcert" CURL_CA_EMBED_SET) +if(_items) + if(NOT CMAKE_VERSION VERSION_LESS 3.13) + list(SORT _items CASE INSENSITIVE) + else() + list(SORT _items) + endif() +endif() +string(REPLACE ";" " " SUPPORT_FEATURES "${_items}") +message(STATUS "Features: ${SUPPORT_FEATURES}") + +# Clear list and collect SSL backends +unset(_items) +_add_if("Schannel" _ssl_enabled AND USE_SCHANNEL) +_add_if("OpenSSL" _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_LESS 3.0.0) +_add_if("OpenSSL v3+" _ssl_enabled AND USE_OPENSSL AND NOT OPENSSL_VERSION VERSION_LESS 3.0.0) +_add_if("Secure Transport" _ssl_enabled AND USE_SECTRANSP) +_add_if("mbedTLS" _ssl_enabled AND USE_MBEDTLS) +_add_if("BearSSL" _ssl_enabled AND USE_BEARSSL) +_add_if("wolfSSL" _ssl_enabled AND USE_WOLFSSL) +_add_if("GnuTLS" _ssl_enabled AND USE_GNUTLS) +_add_if("rustls" _ssl_enabled AND USE_RUSTLS) + +if(_items) + if(NOT CMAKE_VERSION VERSION_LESS 3.13) + list(SORT _items CASE INSENSITIVE) + else() + list(SORT _items) + endif() +endif() +string(REPLACE ";" " " SSL_BACKENDS "${_items}") +message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}") +if(CURL_DEFAULT_SSL_BACKEND) + message(STATUS "Default SSL backend: ${CURL_DEFAULT_SSL_BACKEND}") +endif() + +if(NOT CURL_DISABLE_INSTALL) + + # curl-config needs the following options to be set. + set(CC "${CMAKE_C_COMPILER}") + # TODO: probably put a -D... options here? + set(CONFIGURE_OPTIONS "") + set(CURLVERSION "${CURL_VERSION}") + set(VERSIONNUM "${CURL_VERSION_NUM}") + set(prefix "${CMAKE_INSTALL_PREFIX}") + set(exec_prefix "\${prefix}") + set(includedir "\${prefix}/include") + set(LDFLAGS "${CMAKE_SHARED_LINKER_FLAGS}") + set(libdir "${CMAKE_INSTALL_PREFIX}/lib") + # "a" (Linux) or "lib" (Windows) + string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}") + + set(_ldflags "") + set(LIBCURL_PC_LIBS_PRIVATE "") + + # Avoid getting unnecessary -L options for known system directories. + unset(_sys_libdirs) + foreach(_libdir IN LISTS CMAKE_SYSTEM_PREFIX_PATH) + if(_libdir MATCHES "/$") + set(_libdir "${_libdir}lib") + else() + set(_libdir "${_libdir}/lib") + endif() + if(IS_DIRECTORY "${_libdir}") + list(APPEND _sys_libdirs "${_libdir}") + endif() + if(DEFINED CMAKE_LIBRARY_ARCHITECTURE) + set(_libdir "${_libdir}/${CMAKE_LIBRARY_ARCHITECTURE}") + if(IS_DIRECTORY "${_libdir}") + list(APPEND _sys_libdirs "${_libdir}") + endif() + endif() + endforeach() + + foreach(_libdir IN LISTS CURL_LIBDIRS) + list(FIND _sys_libdirs "${_libdir}" _libdir_index) + if(_libdir_index LESS 0) + list(APPEND _ldflags "-L${_libdir}") + endif() + endforeach() + + foreach(_lib IN LISTS CMAKE_C_IMPLICIT_LINK_LIBRARIES CURL_LIBS) + if(TARGET "${_lib}") + set(_libname "${_lib}") + get_target_property(_imported "${_libname}" IMPORTED) + if(NOT _imported) + # Reading the LOCATION property on non-imported target will error out. + # Assume the user will not need this information in the .pc file. + continue() + endif() + get_target_property(_lib "${_libname}" LOCATION) + if(NOT _lib) + message(WARNING "Bad lib in library list: ${_libname}") + continue() + endif() + endif() + if(_lib MATCHES "^-") # '-framework ' + list(APPEND _ldflags "${_lib}") + elseif(_lib MATCHES ".*/.*") + # This gets a bit more complex, because we want to specify the + # directory separately, and only once per directory + get_filename_component(_libdir ${_lib} DIRECTORY) + get_filename_component(_libname ${_lib} NAME_WE) + if(_libname MATCHES "^lib") + list(FIND _sys_libdirs "${_libdir}" _libdir_index) + if(_libdir_index LESS 0) + list(APPEND _ldflags "-L${_libdir}") + endif() + string(REGEX REPLACE "^lib" "" _libname "${_libname}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE "-l${_libname}") + else() + list(APPEND LIBCURL_PC_LIBS_PRIVATE "${_lib}") + endif() + else() + list(APPEND LIBCURL_PC_LIBS_PRIVATE "-l${_lib}") + endif() + endforeach() + + if(LIBCURL_PC_REQUIRES_PRIVATE) + string(REPLACE ";" "," LIBCURL_PC_REQUIRES_PRIVATE "${LIBCURL_PC_REQUIRES_PRIVATE}") + endif() + if(LIBCURL_PC_LIBS_PRIVATE) + string(REPLACE ";" " " LIBCURL_PC_LIBS_PRIVATE "${LIBCURL_PC_LIBS_PRIVATE}") + endif() + if(_ldflags) + list(REMOVE_DUPLICATES _ldflags) + string(REPLACE ";" " " _ldflags "${_ldflags}") + set(LIBCURL_PC_LIBS_PRIVATE "${_ldflags} ${LIBCURL_PC_LIBS_PRIVATE}") + string(STRIP "${LIBCURL_PC_LIBS_PRIVATE}" LIBCURL_PC_LIBS_PRIVATE) + endif() + set(LIBCURL_PC_CFLAGS_PRIVATE "-DCURL_STATICLIB") + + # Merge pkg-config private fields into public ones when static-only + if(BUILD_SHARED_LIBS) + set(ENABLE_SHARED "yes") + set(LIBCURL_PC_REQUIRES "") + set(LIBCURL_PC_LIBS "") + set(LIBCURL_PC_CFLAGS "") + else() + set(ENABLE_SHARED "no") + set(LIBCURL_PC_REQUIRES "${LIBCURL_PC_REQUIRES_PRIVATE}") + set(LIBCURL_PC_LIBS "${LIBCURL_PC_LIBS_PRIVATE}") + set(LIBCURL_PC_CFLAGS "${LIBCURL_PC_CFLAGS_PRIVATE}") + endif() + if(BUILD_STATIC_LIBS) + set(ENABLE_STATIC "yes") + else() + set(ENABLE_STATIC "no") + endif() + + # Finally generate a "curl-config" matching this config. + # Consumed variables: + # CC + # CONFIGURE_OPTIONS + # CURLVERSION + # CURL_CA_BUNDLE + # ENABLE_SHARED + # ENABLE_STATIC + # exec_prefix + # includedir + # LDFLAGS + # LIBCURL_PC_CFLAGS + # LIBCURL_PC_LIBS_PRIVATE + # libdir + # libext + # prefix + # SSL_BACKENDS + # SUPPORT_FEATURES + # SUPPORT_PROTOCOLS + # VERSIONNUM + configure_file( + "${CURL_SOURCE_DIR}/curl-config.in" + "${CURL_BINARY_DIR}/curl-config" @ONLY) + install(FILES "${CURL_BINARY_DIR}/curl-config" + DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + + # Finally generate a pkg-config file matching this config + # Consumed variables: + # CURLVERSION + # exec_prefix + # includedir + # LIBCURL_PC_CFLAGS + # LIBCURL_PC_CFLAGS_PRIVATE + # LIBCURL_PC_LIBS + # LIBCURL_PC_LIBS_PRIVATE + # LIBCURL_PC_REQUIRES + # LIBCURL_PC_REQUIRES_PRIVATE + # libdir + # prefix + # SUPPORT_FEATURES + # SUPPORT_PROTOCOLS + configure_file( + "${CURL_SOURCE_DIR}/libcurl.pc.in" + "${CURL_BINARY_DIR}/libcurl.pc" @ONLY) + install(FILES "${CURL_BINARY_DIR}/libcurl.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + + # Install headers + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h") + + include(CMakePackageConfigHelpers) + write_basic_package_version_file( + "${_version_config}" + VERSION ${CURL_VERSION} + COMPATIBILITY SameMajorVersion) + file(READ "${_version_config}" _generated_version_config) + file(WRITE "${_version_config}" " + if(NOT PACKAGE_FIND_VERSION_RANGE AND PACKAGE_FIND_VERSION_MAJOR STREQUAL \"7\") + # Version 8 satisfies version 7... requirements + set(PACKAGE_FIND_VERSION_MAJOR 8) + set(PACKAGE_FIND_VERSION_COUNT 1) + endif() + ${_generated_version_config}") + + # Consumed custom variables: + # LIB_SELECTED + # TARGETS_EXPORT_NAME + # USE_OPENSSL + # USE_ZLIB + configure_package_config_file("CMake/curl-config.cmake.in" + "${_project_config}" + INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR} + PATH_VARS CMAKE_INSTALL_INCLUDEDIR) + + if(CURL_ENABLE_EXPORT_TARGET) + install(EXPORT "${TARGETS_EXPORT_NAME}" + NAMESPACE "${PROJECT_NAME}::" + DESTINATION ${CURL_INSTALL_CMAKE_DIR}) + endif() + + install(FILES ${_version_config} ${_project_config} + DESTINATION ${CURL_INSTALL_CMAKE_DIR}) + + # Workaround for MSVS10 to avoid the Dialog Hell + # FIXME: This could be removed with future version of CMake. + if(MSVC_VERSION EQUAL 1600) + set(_curl_sln_filename "${CMAKE_CURRENT_BINARY_DIR}/CURL.sln") + if(EXISTS "${_curl_sln_filename}") + file(APPEND "${_curl_sln_filename}" "\n# This should be regenerated!\n") + endif() + endif() + + if(NOT TARGET curl_uninstall) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + + add_custom_target(curl_uninstall + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake") + endif() + + install(FILES "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl" + DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) +endif() diff --git a/third_party/curl/curl-config.in b/third_party/curl/curl-config.in new file mode 100644 index 0000000000..5d40bc39f5 --- /dev/null +++ b/third_party/curl/curl-config.in @@ -0,0 +1,195 @@ +#!/bin/sh +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +# shellcheck disable=SC2006 + +prefix="@prefix@" +# Used in @libdir@ +# shellcheck disable=SC2034 +exec_prefix=@exec_prefix@ +# shellcheck disable=SC2034 +includedir=@includedir@ +cppflag_curl_staticlib=@CPPFLAG_CURL_STATICLIB@ + +usage() +{ + cat <&2 + exit 1 + fi + ;; + + --configure) + echo @CONFIGURE_OPTIONS@ + ;; + + *) + echo "unknown option: $1" + usage 1 + ;; + esac + shift +done + +exit 0 diff --git a/third_party/curl/include/Makefile.am b/third_party/curl/include/Makefile.am new file mode 100644 index 0000000000..d65bfeaa0f --- /dev/null +++ b/third_party/curl/include/Makefile.am @@ -0,0 +1,28 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +SUBDIRS = curl + +EXTRA_DIST = README.md + +AUTOMAKE_OPTIONS = foreign no-dependencies diff --git a/third_party/curl/include/Makefile.in b/third_party/curl/include/Makefile.in new file mode 100644 index 0000000000..1b95100606 --- /dev/null +++ b/third_party/curl/include/Makefile.in @@ -0,0 +1,775 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/curl-amissl.m4 \ + $(top_srcdir)/m4/curl-bearssl.m4 \ + $(top_srcdir)/m4/curl-compilers.m4 \ + $(top_srcdir)/m4/curl-confopts.m4 \ + $(top_srcdir)/m4/curl-functions.m4 \ + $(top_srcdir)/m4/curl-gnutls.m4 \ + $(top_srcdir)/m4/curl-mbedtls.m4 \ + $(top_srcdir)/m4/curl-openssl.m4 \ + $(top_srcdir)/m4/curl-override.m4 \ + $(top_srcdir)/m4/curl-reentrant.m4 \ + $(top_srcdir)/m4/curl-rustls.m4 \ + $(top_srcdir)/m4/curl-schannel.m4 \ + $(top_srcdir)/m4/curl-sectransp.m4 \ + $(top_srcdir)/m4/curl-sysconfig.m4 \ + $(top_srcdir)/m4/curl-wolfssl.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/xc-am-iface.m4 \ + $(top_srcdir)/m4/xc-cc-check.m4 \ + $(top_srcdir)/m4/xc-lt-iface.m4 \ + $(top_srcdir)/m4/xc-val-flgs.m4 \ + $(top_srcdir)/m4/zz40-xc-ovr.m4 \ + $(top_srcdir)/m4/zz50-xc-ovr.m4 \ + $(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/lib/curl_config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +depcomp = +am__maybe_remake_depfiles = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in README.md +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APACHECTL = @APACHECTL@ +APXS = @APXS@ +AR = @AR@ +AR_FLAGS = @AR_FLAGS@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@ +CADDY = @CADDY@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@ +CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURLVERSION = @CURLVERSION@ +CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ +CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@ +CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ +CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ +CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ +CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ +CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ +CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@ +CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ +CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@ +CURL_DISABLE_MQTT = @CURL_DISABLE_MQTT@ +CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@ +CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@ +CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@ +CURL_DISABLE_SMB = @CURL_DISABLE_SMB@ +CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@ +CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ +CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@ +CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@ +CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@ +CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@ +CURL_WITH_MULTI_SSL = @CURL_WITH_MULTI_SSL@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_SSL_BACKEND = @DEFAULT_SSL_BACKEND@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_SHARED = @ENABLE_SHARED@ +ENABLE_STATIC = @ENABLE_STATIC@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@ +GCOV = @GCOV@ +GREP = @GREP@ +HAVE_BROTLI = @HAVE_BROTLI@ +HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@ +HAVE_LDAP_SSL = @HAVE_LDAP_SSL@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_OPENSSL_QUIC = @HAVE_OPENSSL_QUIC@ +HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@ +HAVE_PROTO_BSDSOCKET_H = @HAVE_PROTO_BSDSOCKET_H@ +HAVE_ZSTD = @HAVE_ZSTD@ +HTTPD = @HTTPD@ +HTTPD_NGHTTPX = @HTTPD_NGHTTPX@ +IDN_ENABLED = @IDN_ENABLED@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPV6_ENABLED = @IPV6_ENABLED@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCURL_LIBS = @LIBCURL_LIBS@ +LIBCURL_NO_SHARED = @LIBCURL_NO_SHARED@ +LIBCURL_PC_REQUIRES = @LIBCURL_PC_REQUIRES@ +LIBCURL_PC_REQUIRES_PRIVATE = @LIBCURL_PC_REQUIRES_PRIVATE@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKGADD_NAME = @PKGADD_NAME@ +PKGADD_PKG = @PKGADD_PKG@ +PKGADD_VENDOR = @PKGADD_VENDOR@ +PKGCONFIG = @PKGCONFIG@ +RANDOM_FILE = @RANDOM_FILE@ +RANLIB = @RANLIB@ +RC = @RC@ +REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSL_BACKENDS = @SSL_BACKENDS@ +SSL_ENABLED = @SSL_ENABLED@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SUPPORT_FEATURES = @SUPPORT_FEATURES@ +SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@ +TEST_NGHTTPX = @TEST_NGHTTPX@ +USE_ARES = @USE_ARES@ +USE_BEARSSL = @USE_BEARSSL@ +USE_GNUTLS = @USE_GNUTLS@ +USE_HYPER = @USE_HYPER@ +USE_LIBPSL = @USE_LIBPSL@ +USE_LIBRTMP = @USE_LIBRTMP@ +USE_LIBSSH = @USE_LIBSSH@ +USE_LIBSSH2 = @USE_LIBSSH2@ +USE_MBEDTLS = @USE_MBEDTLS@ +USE_MSH3 = @USE_MSH3@ +USE_NGHTTP2 = @USE_NGHTTP2@ +USE_NGHTTP3 = @USE_NGHTTP3@ +USE_NGTCP2 = @USE_NGTCP2@ +USE_NGTCP2_CRYPTO_BORINGSSL = @USE_NGTCP2_CRYPTO_BORINGSSL@ +USE_NGTCP2_CRYPTO_GNUTLS = @USE_NGTCP2_CRYPTO_GNUTLS@ +USE_NGTCP2_CRYPTO_QUICTLS = @USE_NGTCP2_CRYPTO_QUICTLS@ +USE_NGTCP2_CRYPTO_WOLFSSL = @USE_NGTCP2_CRYPTO_WOLFSSL@ +USE_NGTCP2_H3 = @USE_NGTCP2_H3@ +USE_OPENLDAP = @USE_OPENLDAP@ +USE_OPENSSL_H3 = @USE_OPENSSL_H3@ +USE_OPENSSL_QUIC = @USE_OPENSSL_QUIC@ +USE_QUICHE = @USE_QUICHE@ +USE_RUSTLS = @USE_RUSTLS@ +USE_SCHANNEL = @USE_SCHANNEL@ +USE_SECTRANSP = @USE_SECTRANSP@ +USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@ +USE_WIN32_CRYPTO = @USE_WIN32_CRYPTO@ +USE_WIN32_LARGE_FILES = @USE_WIN32_LARGE_FILES@ +USE_WIN32_SMALL_FILES = @USE_WIN32_SMALL_FILES@ +USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@ +USE_WOLFSSH = @USE_WOLFSSH@ +USE_WOLFSSL = @USE_WOLFSSL@ +VERSION = @VERSION@ +VERSIONNUM = @VERSIONNUM@ +VSFTPD = @VSFTPD@ +ZLIB_LIBS = @ZLIB_LIBS@ +ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libext = @libext@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +SUBDIRS = curl +EXTRA_DIST = README.md +AUTOMAKE_OPTIONS = foreign no-dependencies +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/third_party/curl/include/README.md b/third_party/curl/include/README.md new file mode 100644 index 0000000000..b155d8c13b --- /dev/null +++ b/third_party/curl/include/README.md @@ -0,0 +1,20 @@ + + +# include + +Public include files for libcurl, external users. + +They are all placed in the curl subdirectory here for better fit in any kind of +environment. You must include files from here using... + + #include + +... style and point the compiler's include path to the directory holding the +curl subdirectory. It makes it more likely to survive future modifications. + +The public curl include files can be shared freely between different platforms +and different architectures. diff --git a/third_party/curl/include/curl/Makefile.am b/third_party/curl/include/curl/Makefile.am new file mode 100644 index 0000000000..8864d60e1f --- /dev/null +++ b/third_party/curl/include/curl/Makefile.am @@ -0,0 +1,41 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +pkginclude_HEADERS = \ + curl.h curlver.h easy.h mprintf.h stdcheaders.h multi.h \ + typecheck-gcc.h system.h urlapi.h options.h header.h websockets.h + +pkgincludedir= $(includedir)/curl + +CHECKSRC = $(CS_$(V)) +CS_0 = @echo " RUN " $@; +CS_1 = +CS_ = $(CS_0) + +checksrc: + $(CHECKSRC)@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(top_srcdir)/include/curl $(pkginclude_HEADERS) + +if DEBUGBUILD +# for debug builds, we scan the sources on all regular make invokes +all-local: checksrc +endif diff --git a/third_party/curl/include/curl/Makefile.in b/third_party/curl/include/curl/Makefile.in new file mode 100644 index 0000000000..5e534ac7fc --- /dev/null +++ b/third_party/curl/include/curl/Makefile.in @@ -0,0 +1,726 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include/curl +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/curl-amissl.m4 \ + $(top_srcdir)/m4/curl-bearssl.m4 \ + $(top_srcdir)/m4/curl-compilers.m4 \ + $(top_srcdir)/m4/curl-confopts.m4 \ + $(top_srcdir)/m4/curl-functions.m4 \ + $(top_srcdir)/m4/curl-gnutls.m4 \ + $(top_srcdir)/m4/curl-mbedtls.m4 \ + $(top_srcdir)/m4/curl-openssl.m4 \ + $(top_srcdir)/m4/curl-override.m4 \ + $(top_srcdir)/m4/curl-reentrant.m4 \ + $(top_srcdir)/m4/curl-rustls.m4 \ + $(top_srcdir)/m4/curl-schannel.m4 \ + $(top_srcdir)/m4/curl-sectransp.m4 \ + $(top_srcdir)/m4/curl-sysconfig.m4 \ + $(top_srcdir)/m4/curl-wolfssl.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/xc-am-iface.m4 \ + $(top_srcdir)/m4/xc-cc-check.m4 \ + $(top_srcdir)/m4/xc-lt-iface.m4 \ + $(top_srcdir)/m4/xc-val-flgs.m4 \ + $(top_srcdir)/m4/zz40-xc-ovr.m4 \ + $(top_srcdir)/m4/zz50-xc-ovr.m4 \ + $(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(pkginclude_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/lib/curl_config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkgincludedir)" +HEADERS = $(pkginclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +pkgincludedir = $(includedir)/curl +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APACHECTL = @APACHECTL@ +APXS = @APXS@ +AR = @AR@ +AR_FLAGS = @AR_FLAGS@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@ +CADDY = @CADDY@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@ +CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURLVERSION = @CURLVERSION@ +CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ +CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@ +CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ +CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ +CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ +CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ +CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ +CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@ +CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ +CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@ +CURL_DISABLE_MQTT = @CURL_DISABLE_MQTT@ +CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@ +CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@ +CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@ +CURL_DISABLE_SMB = @CURL_DISABLE_SMB@ +CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@ +CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ +CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@ +CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@ +CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@ +CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@ +CURL_WITH_MULTI_SSL = @CURL_WITH_MULTI_SSL@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_SSL_BACKEND = @DEFAULT_SSL_BACKEND@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_SHARED = @ENABLE_SHARED@ +ENABLE_STATIC = @ENABLE_STATIC@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@ +GCOV = @GCOV@ +GREP = @GREP@ +HAVE_BROTLI = @HAVE_BROTLI@ +HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@ +HAVE_LDAP_SSL = @HAVE_LDAP_SSL@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_OPENSSL_QUIC = @HAVE_OPENSSL_QUIC@ +HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@ +HAVE_PROTO_BSDSOCKET_H = @HAVE_PROTO_BSDSOCKET_H@ +HAVE_ZSTD = @HAVE_ZSTD@ +HTTPD = @HTTPD@ +HTTPD_NGHTTPX = @HTTPD_NGHTTPX@ +IDN_ENABLED = @IDN_ENABLED@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPV6_ENABLED = @IPV6_ENABLED@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCURL_LIBS = @LIBCURL_LIBS@ +LIBCURL_NO_SHARED = @LIBCURL_NO_SHARED@ +LIBCURL_PC_REQUIRES = @LIBCURL_PC_REQUIRES@ +LIBCURL_PC_REQUIRES_PRIVATE = @LIBCURL_PC_REQUIRES_PRIVATE@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKGADD_NAME = @PKGADD_NAME@ +PKGADD_PKG = @PKGADD_PKG@ +PKGADD_VENDOR = @PKGADD_VENDOR@ +PKGCONFIG = @PKGCONFIG@ +RANDOM_FILE = @RANDOM_FILE@ +RANLIB = @RANLIB@ +RC = @RC@ +REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSL_BACKENDS = @SSL_BACKENDS@ +SSL_ENABLED = @SSL_ENABLED@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SUPPORT_FEATURES = @SUPPORT_FEATURES@ +SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@ +TEST_NGHTTPX = @TEST_NGHTTPX@ +USE_ARES = @USE_ARES@ +USE_BEARSSL = @USE_BEARSSL@ +USE_GNUTLS = @USE_GNUTLS@ +USE_HYPER = @USE_HYPER@ +USE_LIBPSL = @USE_LIBPSL@ +USE_LIBRTMP = @USE_LIBRTMP@ +USE_LIBSSH = @USE_LIBSSH@ +USE_LIBSSH2 = @USE_LIBSSH2@ +USE_MBEDTLS = @USE_MBEDTLS@ +USE_MSH3 = @USE_MSH3@ +USE_NGHTTP2 = @USE_NGHTTP2@ +USE_NGHTTP3 = @USE_NGHTTP3@ +USE_NGTCP2 = @USE_NGTCP2@ +USE_NGTCP2_CRYPTO_BORINGSSL = @USE_NGTCP2_CRYPTO_BORINGSSL@ +USE_NGTCP2_CRYPTO_GNUTLS = @USE_NGTCP2_CRYPTO_GNUTLS@ +USE_NGTCP2_CRYPTO_QUICTLS = @USE_NGTCP2_CRYPTO_QUICTLS@ +USE_NGTCP2_CRYPTO_WOLFSSL = @USE_NGTCP2_CRYPTO_WOLFSSL@ +USE_NGTCP2_H3 = @USE_NGTCP2_H3@ +USE_OPENLDAP = @USE_OPENLDAP@ +USE_OPENSSL_H3 = @USE_OPENSSL_H3@ +USE_OPENSSL_QUIC = @USE_OPENSSL_QUIC@ +USE_QUICHE = @USE_QUICHE@ +USE_RUSTLS = @USE_RUSTLS@ +USE_SCHANNEL = @USE_SCHANNEL@ +USE_SECTRANSP = @USE_SECTRANSP@ +USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@ +USE_WIN32_CRYPTO = @USE_WIN32_CRYPTO@ +USE_WIN32_LARGE_FILES = @USE_WIN32_LARGE_FILES@ +USE_WIN32_SMALL_FILES = @USE_WIN32_SMALL_FILES@ +USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@ +USE_WOLFSSH = @USE_WOLFSSH@ +USE_WOLFSSL = @USE_WOLFSSL@ +VERSION = @VERSION@ +VERSIONNUM = @VERSIONNUM@ +VSFTPD = @VSFTPD@ +ZLIB_LIBS = @ZLIB_LIBS@ +ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libext = @libext@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +pkginclude_HEADERS = \ + curl.h curlver.h easy.h mprintf.h stdcheaders.h multi.h \ + typecheck-gcc.h system.h urlapi.h options.h header.h websockets.h + +CHECKSRC = $(CS_$(V)) +CS_0 = @echo " RUN " $@; +CS_1 = +CS_ = $(CS_0) +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/curl/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/curl/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgincludeHEADERS: $(pkginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +@DEBUGBUILD_FALSE@all-local: +all-am: Makefile $(HEADERS) all-local +installdirs: + for dir in "$(DESTDIR)$(pkgincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkgincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkgincludeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ + clean-generic clean-libtool cscopelist-am ctags ctags-am \ + distclean distclean-generic distclean-libtool distclean-tags \ + distdir dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkgincludeHEADERS \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkgincludeHEADERS + +.PRECIOUS: Makefile + + +checksrc: + $(CHECKSRC)@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(top_srcdir)/include/curl $(pkginclude_HEADERS) + +# for debug builds, we scan the sources on all regular make invokes +@DEBUGBUILD_TRUE@all-local: checksrc + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/third_party/curl/include/curl/curl.h b/third_party/curl/include/curl/curl.h new file mode 100644 index 0000000000..6da44ce9bf --- /dev/null +++ b/third_party/curl/include/curl/curl.h @@ -0,0 +1,3258 @@ +#ifndef CURLINC_CURL_H +#define CURLINC_CURL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * If you have libcurl problems, all docs and details are found here: + * https://curl.se/libcurl/ + */ + +#ifdef CURL_NO_OLDIES +#define CURL_STRICTER +#endif + +/* Compile-time deprecation macros. */ +#if (defined(__GNUC__) && \ + ((__GNUC__ > 12) || ((__GNUC__ == 12) && (__GNUC_MINOR__ >= 1 ))) || \ + defined(__IAR_SYSTEMS_ICC__)) && \ + !defined(__INTEL_COMPILER) && \ + !defined(CURL_DISABLE_DEPRECATION) && !defined(BUILDING_LIBCURL) +#define CURL_DEPRECATED(version, message) \ + __attribute__((deprecated("since " # version ". " message))) +#if defined(__IAR_SYSTEMS_ICC__) +#define CURL_IGNORE_DEPRECATION(statements) \ + _Pragma("diag_suppress=Pe1444") \ + statements \ + _Pragma("diag_default=Pe1444") +#else +#define CURL_IGNORE_DEPRECATION(statements) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \ + statements \ + _Pragma("GCC diagnostic pop") +#endif +#else +#define CURL_DEPRECATED(version, message) +#define CURL_IGNORE_DEPRECATION(statements) statements +#endif + +#include "curlver.h" /* libcurl version defines */ +#include "system.h" /* determine things runtime */ + +#include +#include + +#if defined(__FreeBSD__) || defined(__MidnightBSD__) +/* Needed for __FreeBSD_version or __MidnightBSD_version symbol definition */ +#include +#endif + +/* The include stuff here below is mainly for time_t! */ +#include +#include + +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \ + defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)) +/* The check above prevents the winsock2 inclusion if winsock.h already was + included, since they cannot co-exist without problems */ +#include +#include +#endif +#endif + +/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish + libc5-based Linux systems. Only include it on systems that are known to + require it! */ +#if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \ + defined(__minix) || defined(__INTEGRITY) || \ + defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \ + defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \ + (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \ + (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) || \ + defined(__sun__) || defined(__serenity__) || defined(__vxworks__) +#include +#endif + +#if !defined(_WIN32) && !defined(_WIN32_WCE) +#include +#endif + +#if !defined(_WIN32) +#include +#endif + +/* Compatibility for non-Clang compilers */ +#ifndef __has_declspec_attribute +# define __has_declspec_attribute(x) 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) +typedef struct Curl_easy CURL; +typedef struct Curl_share CURLSH; +#else +typedef void CURL; +typedef void CURLSH; +#endif + +/* + * libcurl external API function linkage decorations. + */ + +#ifdef CURL_STATICLIB +# define CURL_EXTERN +#elif defined(_WIN32) || \ + (__has_declspec_attribute(dllexport) && \ + __has_declspec_attribute(dllimport)) +# if defined(BUILDING_LIBCURL) +# define CURL_EXTERN __declspec(dllexport) +# else +# define CURL_EXTERN __declspec(dllimport) +# endif +#elif defined(BUILDING_LIBCURL) && defined(CURL_HIDDEN_SYMBOLS) +# define CURL_EXTERN CURL_EXTERN_SYMBOL +#else +# define CURL_EXTERN +#endif + +#ifndef curl_socket_typedef +/* socket typedef */ +#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) +typedef SOCKET curl_socket_t; +#define CURL_SOCKET_BAD INVALID_SOCKET +#else +typedef int curl_socket_t; +#define CURL_SOCKET_BAD -1 +#endif +#define curl_socket_typedef +#endif /* curl_socket_typedef */ + +/* enum for the different supported SSL backends */ +typedef enum { + CURLSSLBACKEND_NONE = 0, + CURLSSLBACKEND_OPENSSL = 1, + CURLSSLBACKEND_GNUTLS = 2, + CURLSSLBACKEND_NSS CURL_DEPRECATED(8.3.0, "") = 3, + CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ + CURLSSLBACKEND_GSKIT CURL_DEPRECATED(8.3.0, "") = 5, + CURLSSLBACKEND_POLARSSL CURL_DEPRECATED(7.69.0, "") = 6, + CURLSSLBACKEND_WOLFSSL = 7, + CURLSSLBACKEND_SCHANNEL = 8, + CURLSSLBACKEND_SECURETRANSPORT = 9, + CURLSSLBACKEND_AXTLS CURL_DEPRECATED(7.61.0, "") = 10, + CURLSSLBACKEND_MBEDTLS = 11, + CURLSSLBACKEND_MESALINK CURL_DEPRECATED(7.82.0, "") = 12, + CURLSSLBACKEND_BEARSSL = 13, + CURLSSLBACKEND_RUSTLS = 14 +} curl_sslbackend; + +/* aliases for library clones and renames */ +#define CURLSSLBACKEND_AWSLC CURLSSLBACKEND_OPENSSL +#define CURLSSLBACKEND_BORINGSSL CURLSSLBACKEND_OPENSSL +#define CURLSSLBACKEND_LIBRESSL CURLSSLBACKEND_OPENSSL + +/* deprecated names: */ +#define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL +#define CURLSSLBACKEND_DARWINSSL CURLSSLBACKEND_SECURETRANSPORT + +struct curl_httppost { + struct curl_httppost *next; /* next entry in the list */ + char *name; /* pointer to allocated name */ + long namelength; /* length of name length */ + char *contents; /* pointer to allocated data contents */ + long contentslength; /* length of contents field, see also + CURL_HTTPPOST_LARGE */ + char *buffer; /* pointer to allocated buffer contents */ + long bufferlength; /* length of buffer field */ + char *contenttype; /* Content-Type */ + struct curl_slist *contentheader; /* list of extra headers for this form */ + struct curl_httppost *more; /* if one field name has more than one + file, this link should link to following + files */ + long flags; /* as defined below */ + +/* specified content is a filename */ +#define CURL_HTTPPOST_FILENAME (1<<0) +/* specified content is a filename */ +#define CURL_HTTPPOST_READFILE (1<<1) +/* name is only stored pointer do not free in formfree */ +#define CURL_HTTPPOST_PTRNAME (1<<2) +/* contents is only stored pointer do not free in formfree */ +#define CURL_HTTPPOST_PTRCONTENTS (1<<3) +/* upload file from buffer */ +#define CURL_HTTPPOST_BUFFER (1<<4) +/* upload file from pointer contents */ +#define CURL_HTTPPOST_PTRBUFFER (1<<5) +/* upload file contents by using the regular read callback to get the data and + pass the given pointer as custom pointer */ +#define CURL_HTTPPOST_CALLBACK (1<<6) +/* use size in 'contentlen', added in 7.46.0 */ +#define CURL_HTTPPOST_LARGE (1<<7) + + char *showfilename; /* The filename to show. If not set, the + actual filename will be used (if this + is a file part) */ + void *userp; /* custom pointer used for + HTTPPOST_CALLBACK posts */ + curl_off_t contentlen; /* alternative length of contents + field. Used if CURL_HTTPPOST_LARGE is + set. Added in 7.46.0 */ +}; + + +/* This is a return code for the progress callback that, when returned, will + signal libcurl to continue executing the default progress function */ +#define CURL_PROGRESSFUNC_CONTINUE 0x10000001 + +/* This is the CURLOPT_PROGRESSFUNCTION callback prototype. It is now + considered deprecated but was the only choice up until 7.31.0 */ +typedef int (*curl_progress_callback)(void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow); + +/* This is the CURLOPT_XFERINFOFUNCTION callback prototype. It was introduced + in 7.32.0, avoids the use of floating point numbers and provides more + detailed information. */ +typedef int (*curl_xferinfo_callback)(void *clientp, + curl_off_t dltotal, + curl_off_t dlnow, + curl_off_t ultotal, + curl_off_t ulnow); + +#ifndef CURL_MAX_READ_SIZE + /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ +#define CURL_MAX_READ_SIZE (10*1024*1024) +#endif + +#ifndef CURL_MAX_WRITE_SIZE + /* Tests have proven that 20K is a very bad buffer size for uploads on + Windows, while 16K for some odd reason performed a lot better. + We do the ifndef check to allow this value to easier be changed at build + time for those who feel adventurous. The practical minimum is about + 400 bytes since libcurl uses a buffer of this size as a scratch area + (unrelated to network send operations). */ +#define CURL_MAX_WRITE_SIZE 16384 +#endif + +#ifndef CURL_MAX_HTTP_HEADER +/* The only reason to have a max limit for this is to avoid the risk of a bad + server feeding libcurl with a never-ending header that will cause reallocs + infinitely */ +#define CURL_MAX_HTTP_HEADER (100*1024) +#endif + +/* This is a magic return code for the write callback that, when returned, + will signal libcurl to pause receiving on the current transfer. */ +#define CURL_WRITEFUNC_PAUSE 0x10000001 + +/* This is a magic return code for the write callback that, when returned, + will signal an error from the callback. */ +#define CURL_WRITEFUNC_ERROR 0xFFFFFFFF + +typedef size_t (*curl_write_callback)(char *buffer, + size_t size, + size_t nitems, + void *outstream); + +/* This callback will be called when a new resolver request is made */ +typedef int (*curl_resolver_start_callback)(void *resolver_state, + void *reserved, void *userdata); + +/* enumeration of file types */ +typedef enum { + CURLFILETYPE_FILE = 0, + CURLFILETYPE_DIRECTORY, + CURLFILETYPE_SYMLINK, + CURLFILETYPE_DEVICE_BLOCK, + CURLFILETYPE_DEVICE_CHAR, + CURLFILETYPE_NAMEDPIPE, + CURLFILETYPE_SOCKET, + CURLFILETYPE_DOOR, /* is possible only on Sun Solaris now */ + + CURLFILETYPE_UNKNOWN /* should never occur */ +} curlfiletype; + +#define CURLFINFOFLAG_KNOWN_FILENAME (1<<0) +#define CURLFINFOFLAG_KNOWN_FILETYPE (1<<1) +#define CURLFINFOFLAG_KNOWN_TIME (1<<2) +#define CURLFINFOFLAG_KNOWN_PERM (1<<3) +#define CURLFINFOFLAG_KNOWN_UID (1<<4) +#define CURLFINFOFLAG_KNOWN_GID (1<<5) +#define CURLFINFOFLAG_KNOWN_SIZE (1<<6) +#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7) + +/* Information about a single file, used when doing FTP wildcard matching */ +struct curl_fileinfo { + char *filename; + curlfiletype filetype; + time_t time; /* always zero! */ + unsigned int perm; + int uid; + int gid; + curl_off_t size; + long int hardlinks; + + struct { + /* If some of these fields is not NULL, it is a pointer to b_data. */ + char *time; + char *perm; + char *user; + char *group; + char *target; /* pointer to the target filename of a symlink */ + } strings; + + unsigned int flags; + + /* These are libcurl private struct fields. Previously used by libcurl, so + they must never be interfered with. */ + char *b_data; + size_t b_size; + size_t b_used; +}; + +/* return codes for CURLOPT_CHUNK_BGN_FUNCTION */ +#define CURL_CHUNK_BGN_FUNC_OK 0 +#define CURL_CHUNK_BGN_FUNC_FAIL 1 /* tell the lib to end the task */ +#define CURL_CHUNK_BGN_FUNC_SKIP 2 /* skip this chunk over */ + +/* if splitting of data transfer is enabled, this callback is called before + download of an individual chunk started. Note that parameter "remains" works + only for FTP wildcard downloading (for now), otherwise is not used */ +typedef long (*curl_chunk_bgn_callback)(const void *transfer_info, + void *ptr, + int remains); + +/* return codes for CURLOPT_CHUNK_END_FUNCTION */ +#define CURL_CHUNK_END_FUNC_OK 0 +#define CURL_CHUNK_END_FUNC_FAIL 1 /* tell the lib to end the task */ + +/* If splitting of data transfer is enabled this callback is called after + download of an individual chunk finished. + Note! After this callback was set then it have to be called FOR ALL chunks. + Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC. + This is the reason why we do not need "transfer_info" parameter in this + callback and we are not interested in "remains" parameter too. */ +typedef long (*curl_chunk_end_callback)(void *ptr); + +/* return codes for FNMATCHFUNCTION */ +#define CURL_FNMATCHFUNC_MATCH 0 /* string corresponds to the pattern */ +#define CURL_FNMATCHFUNC_NOMATCH 1 /* pattern does not match the string */ +#define CURL_FNMATCHFUNC_FAIL 2 /* an error occurred */ + +/* callback type for wildcard downloading pattern matching. If the + string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */ +typedef int (*curl_fnmatch_callback)(void *ptr, + const char *pattern, + const char *string); + +/* These are the return codes for the seek callbacks */ +#define CURL_SEEKFUNC_OK 0 +#define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */ +#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking cannot be done, so + libcurl might try other means instead */ +typedef int (*curl_seek_callback)(void *instream, + curl_off_t offset, + int origin); /* 'whence' */ + +/* This is a return code for the read callback that, when returned, will + signal libcurl to immediately abort the current transfer. */ +#define CURL_READFUNC_ABORT 0x10000000 +/* This is a return code for the read callback that, when returned, will + signal libcurl to pause sending data on the current transfer. */ +#define CURL_READFUNC_PAUSE 0x10000001 + +/* Return code for when the trailing headers' callback has terminated + without any errors */ +#define CURL_TRAILERFUNC_OK 0 +/* Return code for when was an error in the trailing header's list and we + want to abort the request */ +#define CURL_TRAILERFUNC_ABORT 1 + +typedef size_t (*curl_read_callback)(char *buffer, + size_t size, + size_t nitems, + void *instream); + +typedef int (*curl_trailer_callback)(struct curl_slist **list, + void *userdata); + +typedef enum { + CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ + CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */ + CURLSOCKTYPE_LAST /* never use */ +} curlsocktype; + +/* The return code from the sockopt_callback can signal information back + to libcurl: */ +#define CURL_SOCKOPT_OK 0 +#define CURL_SOCKOPT_ERROR 1 /* causes libcurl to abort and return + CURLE_ABORTED_BY_CALLBACK */ +#define CURL_SOCKOPT_ALREADY_CONNECTED 2 + +typedef int (*curl_sockopt_callback)(void *clientp, + curl_socket_t curlfd, + curlsocktype purpose); + +struct curl_sockaddr { + int family; + int socktype; + int protocol; + unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it + turned really ugly and painful on the systems that + lack this type */ + struct sockaddr addr; +}; + +typedef curl_socket_t +(*curl_opensocket_callback)(void *clientp, + curlsocktype purpose, + struct curl_sockaddr *address); + +typedef int +(*curl_closesocket_callback)(void *clientp, curl_socket_t item); + +typedef enum { + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} curlioerr; + +typedef enum { + CURLIOCMD_NOP, /* no operation */ + CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ + CURLIOCMD_LAST /* never use */ +} curliocmd; + +typedef curlioerr (*curl_ioctl_callback)(CURL *handle, + int cmd, + void *clientp); + +#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS +/* + * The following typedef's are signatures of malloc, free, realloc, strdup and + * calloc respectively. Function pointers of these types can be passed to the + * curl_global_init_mem() function to set user defined memory management + * callback routines. + */ +typedef void *(*curl_malloc_callback)(size_t size); +typedef void (*curl_free_callback)(void *ptr); +typedef void *(*curl_realloc_callback)(void *ptr, size_t size); +typedef char *(*curl_strdup_callback)(const char *str); +typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); + +#define CURL_DID_MEMORY_FUNC_TYPEDEFS +#endif + +/* the kind of data that is passed to information_callback */ +typedef enum { + CURLINFO_TEXT = 0, + CURLINFO_HEADER_IN, /* 1 */ + CURLINFO_HEADER_OUT, /* 2 */ + CURLINFO_DATA_IN, /* 3 */ + CURLINFO_DATA_OUT, /* 4 */ + CURLINFO_SSL_DATA_IN, /* 5 */ + CURLINFO_SSL_DATA_OUT, /* 6 */ + CURLINFO_END +} curl_infotype; + +typedef int (*curl_debug_callback) + (CURL *handle, /* the handle/transfer this concerns */ + curl_infotype type, /* what kind of data */ + char *data, /* points to the data */ + size_t size, /* size of the data pointed to */ + void *userptr); /* whatever the user please */ + +/* This is the CURLOPT_PREREQFUNCTION callback prototype. */ +typedef int (*curl_prereq_callback)(void *clientp, + char *conn_primary_ip, + char *conn_local_ip, + int conn_primary_port, + int conn_local_port); + +/* Return code for when the pre-request callback has terminated without + any errors */ +#define CURL_PREREQFUNC_OK 0 +/* Return code for when the pre-request callback wants to abort the + request */ +#define CURL_PREREQFUNC_ABORT 1 + +/* All possible error codes from all sorts of curl functions. Future versions + may return other values, stay prepared. + + Always add new return codes last. Never *EVER* remove any. The return + codes must remain the same! + */ + +typedef enum { + CURLE_OK = 0, + CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ + CURLE_FAILED_INIT, /* 2 */ + CURLE_URL_MALFORMAT, /* 3 */ + CURLE_NOT_BUILT_IN, /* 4 - [was obsoleted in August 2007 for + 7.17.0, reused in April 2011 for 7.21.5] */ + CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ + CURLE_COULDNT_RESOLVE_HOST, /* 6 */ + CURLE_COULDNT_CONNECT, /* 7 */ + CURLE_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server + due to lack of access - when login fails + this is not returned. */ + CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for + 7.15.4, reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ + CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server + [was obsoleted in August 2007 for 7.17.0, + reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ + CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ + CURLE_FTP_CANT_GET_HOST, /* 15 */ + CURLE_HTTP2, /* 16 - A problem in the http2 framing layer. + [was obsoleted in August 2007 for 7.17.0, + reused in July 2014 for 7.38.0] */ + CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ + CURLE_PARTIAL_FILE, /* 18 */ + CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ + CURLE_OBSOLETE20, /* 20 - NOT USED */ + CURLE_QUOTE_ERROR, /* 21 - quote command failure */ + CURLE_HTTP_RETURNED_ERROR, /* 22 */ + CURLE_WRITE_ERROR, /* 23 */ + CURLE_OBSOLETE24, /* 24 - NOT USED */ + CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ + CURLE_READ_ERROR, /* 26 - could not open/read from file */ + CURLE_OUT_OF_MEMORY, /* 27 */ + CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ + CURLE_OBSOLETE29, /* 29 - NOT USED */ + CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ + CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ + CURLE_OBSOLETE32, /* 32 - NOT USED */ + CURLE_RANGE_ERROR, /* 33 - RANGE "command" did not work */ + CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ + CURLE_BAD_DOWNLOAD_RESUME, /* 36 - could not resume download */ + CURLE_FILE_COULDNT_READ_FILE, /* 37 */ + CURLE_LDAP_CANNOT_BIND, /* 38 */ + CURLE_LDAP_SEARCH_FAILED, /* 39 */ + CURLE_OBSOLETE40, /* 40 - NOT USED */ + CURLE_FUNCTION_NOT_FOUND, /* 41 - NOT USED starting with 7.53.0 */ + CURLE_ABORTED_BY_CALLBACK, /* 42 */ + CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ + CURLE_OBSOLETE44, /* 44 - NOT USED */ + CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ + CURLE_OBSOLETE46, /* 46 - NOT USED */ + CURLE_TOO_MANY_REDIRECTS, /* 47 - catch endless re-direct loops */ + CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ + CURLE_SETOPT_OPTION_SYNTAX, /* 49 - Malformed setopt option */ + CURLE_OBSOLETE50, /* 50 - NOT USED */ + CURLE_OBSOLETE51, /* 51 - NOT USED */ + CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ + CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ + CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as + default */ + CURLE_SEND_ERROR, /* 55 - failed sending network data */ + CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ + CURLE_OBSOLETE57, /* 57 - NOT IN USE */ + CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ + CURLE_SSL_CIPHER, /* 59 - could not use specified cipher */ + CURLE_PEER_FAILED_VERIFICATION, /* 60 - peer's certificate or fingerprint + was not verified fine */ + CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ + CURLE_OBSOLETE62, /* 62 - NOT IN USE since 7.82.0 */ + CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ + CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ + CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind + that failed */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not + accepted and we failed to login */ + CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ + CURLE_TFTP_PERM, /* 69 - permission problem on server */ + CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */ + CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ + CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ + CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */ + CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ + CURLE_OBSOLETE75, /* 75 - NOT IN USE since 7.82.0 */ + CURLE_OBSOLETE76, /* 76 - NOT IN USE since 7.82.0 */ + CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing + or wrong format */ + CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ + CURLE_SSH, /* 79 - error from the SSH layer, somewhat + generic so the error message will be of + interest when this has happened */ + + CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL + connection */ + CURLE_AGAIN, /* 81 - socket is not ready for send/recv, + wait till it is ready and try again (Added + in 7.18.2) */ + CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or + wrong format (Added in 7.19.0) */ + CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in + 7.19.0) */ + CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ + CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */ + CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */ + CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ + CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ + CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the + session will be queued */ + CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not + match */ + CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ + CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer + */ + CURLE_RECURSIVE_API_CALL, /* 93 - an api function was called from + inside a callback */ + CURLE_AUTH_ERROR, /* 94 - an authentication function returned an + error */ + CURLE_HTTP3, /* 95 - An HTTP/3 layer problem */ + CURLE_QUIC_CONNECT_ERROR, /* 96 - QUIC connection error */ + CURLE_PROXY, /* 97 - proxy handshake error */ + CURLE_SSL_CLIENTCERT, /* 98 - client-side certificate required */ + CURLE_UNRECOVERABLE_POLL, /* 99 - poll/select returned fatal error */ + CURLE_TOO_LARGE, /* 100 - a value/data met its maximum */ + CURLE_ECH_REQUIRED, /* 101 - ECH tried but failed */ + CURL_LAST /* never use! */ +} CURLcode; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Previously obsolete error code reused in 7.38.0 */ +#define CURLE_OBSOLETE16 CURLE_HTTP2 + +/* Previously obsolete error codes reused in 7.24.0 */ +#define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED +#define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT + +/* compatibility with older names */ +#define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING +#define CURLE_FTP_WEIRD_SERVER_REPLY CURLE_WEIRD_SERVER_REPLY + +/* The following were added in 7.62.0 */ +#define CURLE_SSL_CACERT CURLE_PEER_FAILED_VERIFICATION + +/* The following were added in 7.21.5, April 2011 */ +#define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION + +/* Added for 7.78.0 */ +#define CURLE_TELNET_OPTION_SYNTAX CURLE_SETOPT_OPTION_SYNTAX + +/* The following were added in 7.17.1 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION + +/* The following were added in 7.17.0 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_OBSOLETE CURLE_OBSOLETE50 /* no one should be using this! */ +#define CURLE_BAD_PASSWORD_ENTERED CURLE_OBSOLETE46 +#define CURLE_BAD_CALLING_ORDER CURLE_OBSOLETE44 +#define CURLE_FTP_USER_PASSWORD_INCORRECT CURLE_OBSOLETE10 +#define CURLE_FTP_CANT_RECONNECT CURLE_OBSOLETE16 +#define CURLE_FTP_COULDNT_GET_SIZE CURLE_OBSOLETE32 +#define CURLE_FTP_COULDNT_SET_ASCII CURLE_OBSOLETE29 +#define CURLE_FTP_WEIRD_USER_REPLY CURLE_OBSOLETE12 +#define CURLE_FTP_WRITE_ERROR CURLE_OBSOLETE20 +#define CURLE_LIBRARY_NOT_FOUND CURLE_OBSOLETE40 +#define CURLE_MALFORMAT_USER CURLE_OBSOLETE24 +#define CURLE_SHARE_IN_USE CURLE_OBSOLETE57 +#define CURLE_URL_MALFORMAT_USER CURLE_NOT_BUILT_IN + +#define CURLE_FTP_ACCESS_DENIED CURLE_REMOTE_ACCESS_DENIED +#define CURLE_FTP_COULDNT_SET_BINARY CURLE_FTP_COULDNT_SET_TYPE +#define CURLE_FTP_QUOTE_ERROR CURLE_QUOTE_ERROR +#define CURLE_TFTP_DISKFULL CURLE_REMOTE_DISK_FULL +#define CURLE_TFTP_EXISTS CURLE_REMOTE_FILE_EXISTS +#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR +#define CURLE_FTP_SSL_FAILED CURLE_USE_SSL_FAILED + +/* The following were added earlier */ + +#define CURLE_OPERATION_TIMEOUTED CURLE_OPERATION_TIMEDOUT +#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR +#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED +#define CURLE_FTP_COULDNT_STOR_FILE CURLE_UPLOAD_FAILED +#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE +#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME +#define CURLE_LDAP_INVALID_URL CURLE_OBSOLETE62 +#define CURLE_CONV_REQD CURLE_OBSOLETE76 +#define CURLE_CONV_FAILED CURLE_OBSOLETE75 + +/* This was the error code 50 in 7.7.3 and a few earlier versions, this + is no longer used by libcurl but is instead #defined here only to not + make programs break */ +#define CURLE_ALREADY_COMPLETE 99999 + +/* Provide defines for really old option names */ +#define CURLOPT_FILE CURLOPT_WRITEDATA /* name changed in 7.9.7 */ +#define CURLOPT_INFILE CURLOPT_READDATA /* name changed in 7.9.7 */ +#define CURLOPT_WRITEHEADER CURLOPT_HEADERDATA + +/* Since long deprecated options with no code in the lib that does anything + with them. */ +#define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40 +#define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72 + +#endif /* !CURL_NO_OLDIES */ + +/* + * Proxy error codes. Returned in CURLINFO_PROXY_ERROR if CURLE_PROXY was + * return for the transfers. + */ +typedef enum { + CURLPX_OK, + CURLPX_BAD_ADDRESS_TYPE, + CURLPX_BAD_VERSION, + CURLPX_CLOSED, + CURLPX_GSSAPI, + CURLPX_GSSAPI_PERMSG, + CURLPX_GSSAPI_PROTECTION, + CURLPX_IDENTD, + CURLPX_IDENTD_DIFFER, + CURLPX_LONG_HOSTNAME, + CURLPX_LONG_PASSWD, + CURLPX_LONG_USER, + CURLPX_NO_AUTH, + CURLPX_RECV_ADDRESS, + CURLPX_RECV_AUTH, + CURLPX_RECV_CONNECT, + CURLPX_RECV_REQACK, + CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED, + CURLPX_REPLY_COMMAND_NOT_SUPPORTED, + CURLPX_REPLY_CONNECTION_REFUSED, + CURLPX_REPLY_GENERAL_SERVER_FAILURE, + CURLPX_REPLY_HOST_UNREACHABLE, + CURLPX_REPLY_NETWORK_UNREACHABLE, + CURLPX_REPLY_NOT_ALLOWED, + CURLPX_REPLY_TTL_EXPIRED, + CURLPX_REPLY_UNASSIGNED, + CURLPX_REQUEST_FAILED, + CURLPX_RESOLVE_HOST, + CURLPX_SEND_AUTH, + CURLPX_SEND_CONNECT, + CURLPX_SEND_REQUEST, + CURLPX_UNKNOWN_FAIL, + CURLPX_UNKNOWN_MODE, + CURLPX_USER_REJECTED, + CURLPX_LAST /* never use */ +} CURLproxycode; + +/* This prototype applies to all conversion callbacks */ +typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); + +typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ + void *ssl_ctx, /* actually an OpenSSL + or wolfSSL SSL_CTX, + or an mbedTLS + mbedtls_ssl_config */ + void *userptr); + +typedef enum { + CURLPROXY_HTTP = 0, /* added in 7.10, new in 7.19.4 default is to use + CONNECT HTTP/1.1 */ + CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT + HTTP/1.0 */ + CURLPROXY_HTTPS = 2, /* HTTPS but stick to HTTP/1 added in 7.52.0 */ + CURLPROXY_HTTPS2 = 3, /* HTTPS and attempt HTTP/2 added in 8.2.0 */ + CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already + in 7.10 */ + CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ + CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */ + CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the + hostname rather than the IP address. added + in 7.18.0 */ +} curl_proxytype; /* this enum was added in 7.10 */ + +/* + * Bitmasks for CURLOPT_HTTPAUTH and CURLOPT_PROXYAUTH options: + * + * CURLAUTH_NONE - No HTTP authentication + * CURLAUTH_BASIC - HTTP Basic authentication (default) + * CURLAUTH_DIGEST - HTTP Digest authentication + * CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication + * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated) + * CURLAUTH_NTLM - HTTP NTLM authentication + * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour + * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper + * CURLAUTH_BEARER - HTTP Bearer token authentication + * CURLAUTH_ONLY - Use together with a single other type to force no + * authentication or just that single type + * CURLAUTH_ANY - All fine types set + * CURLAUTH_ANYSAFE - All fine types except Basic + */ + +#define CURLAUTH_NONE ((unsigned long)0) +#define CURLAUTH_BASIC (((unsigned long)1)<<0) +#define CURLAUTH_DIGEST (((unsigned long)1)<<1) +#define CURLAUTH_NEGOTIATE (((unsigned long)1)<<2) +/* Deprecated since the advent of CURLAUTH_NEGOTIATE */ +#define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE +/* Used for CURLOPT_SOCKS5_AUTH to stay terminologically correct */ +#define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE +#define CURLAUTH_NTLM (((unsigned long)1)<<3) +#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#ifndef CURL_NO_OLDIES + /* functionality removed since 8.8.0 */ +#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#endif +#define CURLAUTH_BEARER (((unsigned long)1)<<6) +#define CURLAUTH_AWS_SIGV4 (((unsigned long)1)<<7) +#define CURLAUTH_ONLY (((unsigned long)1)<<31) +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) + +#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ +#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ +#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */ +#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ +#define CURLSSH_AUTH_HOST (1<<2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */ +#define CURLSSH_AUTH_GSSAPI (1<<5) /* gssapi (kerberos, ...) */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY + +#define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ +#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */ +#define CURLGSSAPI_DELEGATION_FLAG (1<<1) /* delegate always */ + +#define CURL_ERROR_SIZE 256 + +enum curl_khtype { + CURLKHTYPE_UNKNOWN, + CURLKHTYPE_RSA1, + CURLKHTYPE_RSA, + CURLKHTYPE_DSS, + CURLKHTYPE_ECDSA, + CURLKHTYPE_ED25519 +}; + +struct curl_khkey { + const char *key; /* points to a null-terminated string encoded with base64 + if len is zero, otherwise to the "raw" data */ + size_t len; + enum curl_khtype keytype; +}; + +/* this is the set of return values expected from the curl_sshkeycallback + callback */ +enum curl_khstat { + CURLKHSTAT_FINE_ADD_TO_FILE, + CURLKHSTAT_FINE, + CURLKHSTAT_REJECT, /* reject the connection, return an error */ + CURLKHSTAT_DEFER, /* do not accept it, but we cannot answer right now. + Causes a CURLE_PEER_FAILED_VERIFICATION error but the + connection will be left intact etc */ + CURLKHSTAT_FINE_REPLACE, /* accept and replace the wrong key */ + CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ +}; + +/* this is the set of status codes pass in to the callback */ +enum curl_khmatch { + CURLKHMATCH_OK, /* match */ + CURLKHMATCH_MISMATCH, /* host found, key mismatch! */ + CURLKHMATCH_MISSING, /* no matching host/key found */ + CURLKHMATCH_LAST /* not for use, only a marker for last-in-list */ +}; + +typedef int + (*curl_sshkeycallback) (CURL *easy, /* easy handle */ + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch, /* libcurl's view on the keys */ + void *clientp); /* custom pointer passed with */ + /* CURLOPT_SSH_KEYDATA */ + +typedef int + (*curl_sshhostkeycallback) (void *clientp,/* custom pointer passed */ + /* with CURLOPT_SSH_HOSTKEYDATA */ + int keytype, /* CURLKHTYPE */ + const char *key, /* hostkey to check */ + size_t keylen); /* length of the key */ + /* return CURLE_OK to accept */ + /* or something else to refuse */ + + +/* parameter for the CURLOPT_USE_SSL option */ +typedef enum { + CURLUSESSL_NONE, /* do not attempt to use SSL */ + CURLUSESSL_TRY, /* try using SSL, proceed anyway otherwise */ + CURLUSESSL_CONTROL, /* SSL for the control connection or fail */ + CURLUSESSL_ALL, /* SSL for all communication or fail */ + CURLUSESSL_LAST /* not an option, never use */ +} curl_usessl; + +/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */ + +/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the + name of improving interoperability with older servers. Some SSL libraries + have introduced work-arounds for this flaw but those work-arounds sometimes + make the SSL communication fail. To regain functionality with those broken + servers, a user can this way allow the vulnerability back. */ +#define CURLSSLOPT_ALLOW_BEAST (1<<0) + +/* - NO_REVOKE tells libcurl to disable certificate revocation checks for those + SSL backends where such behavior is present. */ +#define CURLSSLOPT_NO_REVOKE (1<<1) + +/* - NO_PARTIALCHAIN tells libcurl to *NOT* accept a partial certificate chain + if possible. The OpenSSL backend has this ability. */ +#define CURLSSLOPT_NO_PARTIALCHAIN (1<<2) + +/* - REVOKE_BEST_EFFORT tells libcurl to ignore certificate revocation offline + checks and ignore missing revocation list for those SSL backends where such + behavior is present. */ +#define CURLSSLOPT_REVOKE_BEST_EFFORT (1<<3) + +/* - CURLSSLOPT_NATIVE_CA tells libcurl to use standard certificate store of + operating system. Currently implemented under MS-Windows. */ +#define CURLSSLOPT_NATIVE_CA (1<<4) + +/* - CURLSSLOPT_AUTO_CLIENT_CERT tells libcurl to automatically locate and use + a client certificate for authentication. (Schannel) */ +#define CURLSSLOPT_AUTO_CLIENT_CERT (1<<5) + +/* The default connection attempt delay in milliseconds for happy eyeballs. + CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document + this value, keep them in sync. */ +#define CURL_HET_DEFAULT 200L + +/* The default connection upkeep interval in milliseconds. */ +#define CURL_UPKEEP_INTERVAL_DEFAULT 60000L + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2009 */ + +#define CURLFTPSSL_NONE CURLUSESSL_NONE +#define CURLFTPSSL_TRY CURLUSESSL_TRY +#define CURLFTPSSL_CONTROL CURLUSESSL_CONTROL +#define CURLFTPSSL_ALL CURLUSESSL_ALL +#define CURLFTPSSL_LAST CURLUSESSL_LAST +#define curl_ftpssl curl_usessl +#endif /* !CURL_NO_OLDIES */ + +/* parameter for the CURLOPT_FTP_SSL_CCC option */ +typedef enum { + CURLFTPSSL_CCC_NONE, /* do not send CCC */ + CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */ + CURLFTPSSL_CCC_ACTIVE, /* Initiate the shutdown */ + CURLFTPSSL_CCC_LAST /* not an option, never use */ +} curl_ftpccc; + +/* parameter for the CURLOPT_FTPSSLAUTH option */ +typedef enum { + CURLFTPAUTH_DEFAULT, /* let libcurl decide */ + CURLFTPAUTH_SSL, /* use "AUTH SSL" */ + CURLFTPAUTH_TLS, /* use "AUTH TLS" */ + CURLFTPAUTH_LAST /* not an option, never use */ +} curl_ftpauth; + +/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ +typedef enum { + CURLFTP_CREATE_DIR_NONE, /* do NOT create missing dirs! */ + CURLFTP_CREATE_DIR, /* (FTP/SFTP) if CWD fails, try MKD and then CWD + again if MKD succeeded, for SFTP this does + similar magic */ + CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD + again even if MKD failed! */ + CURLFTP_CREATE_DIR_LAST /* not an option, never use */ +} curl_ftpcreatedir; + +/* parameter for the CURLOPT_FTP_FILEMETHOD option */ +typedef enum { + CURLFTPMETHOD_DEFAULT, /* let libcurl pick */ + CURLFTPMETHOD_MULTICWD, /* single CWD operation for each path part */ + CURLFTPMETHOD_NOCWD, /* no CWD at all */ + CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */ + CURLFTPMETHOD_LAST /* not an option, never use */ +} curl_ftpmethod; + +/* bitmask defines for CURLOPT_HEADEROPT */ +#define CURLHEADER_UNIFIED 0 +#define CURLHEADER_SEPARATE (1<<0) + +/* CURLALTSVC_* are bits for the CURLOPT_ALTSVC_CTRL option */ +#define CURLALTSVC_READONLYFILE (1<<2) +#define CURLALTSVC_H1 (1<<3) +#define CURLALTSVC_H2 (1<<4) +#define CURLALTSVC_H3 (1<<5) + + +struct curl_hstsentry { + char *name; + size_t namelen; + unsigned int includeSubDomains:1; + char expire[18]; /* YYYYMMDD HH:MM:SS [null-terminated] */ +}; + +struct curl_index { + size_t index; /* the provided entry's "index" or count */ + size_t total; /* total number of entries to save */ +}; + +typedef enum { + CURLSTS_OK, + CURLSTS_DONE, + CURLSTS_FAIL +} CURLSTScode; + +typedef CURLSTScode (*curl_hstsread_callback)(CURL *easy, + struct curl_hstsentry *e, + void *userp); +typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy, + struct curl_hstsentry *e, + struct curl_index *i, + void *userp); + +/* CURLHSTS_* are bits for the CURLOPT_HSTS option */ +#define CURLHSTS_ENABLE (long)(1<<0) +#define CURLHSTS_READONLYFILE (long)(1<<1) + +/* The CURLPROTO_ defines below are for the **deprecated** CURLOPT_*PROTOCOLS + options. Do not use. */ +#define CURLPROTO_HTTP (1<<0) +#define CURLPROTO_HTTPS (1<<1) +#define CURLPROTO_FTP (1<<2) +#define CURLPROTO_FTPS (1<<3) +#define CURLPROTO_SCP (1<<4) +#define CURLPROTO_SFTP (1<<5) +#define CURLPROTO_TELNET (1<<6) +#define CURLPROTO_LDAP (1<<7) +#define CURLPROTO_LDAPS (1<<8) +#define CURLPROTO_DICT (1<<9) +#define CURLPROTO_FILE (1<<10) +#define CURLPROTO_TFTP (1<<11) +#define CURLPROTO_IMAP (1<<12) +#define CURLPROTO_IMAPS (1<<13) +#define CURLPROTO_POP3 (1<<14) +#define CURLPROTO_POP3S (1<<15) +#define CURLPROTO_SMTP (1<<16) +#define CURLPROTO_SMTPS (1<<17) +#define CURLPROTO_RTSP (1<<18) +#define CURLPROTO_RTMP (1<<19) +#define CURLPROTO_RTMPT (1<<20) +#define CURLPROTO_RTMPE (1<<21) +#define CURLPROTO_RTMPTE (1<<22) +#define CURLPROTO_RTMPS (1<<23) +#define CURLPROTO_RTMPTS (1<<24) +#define CURLPROTO_GOPHER (1<<25) +#define CURLPROTO_SMB (1<<26) +#define CURLPROTO_SMBS (1<<27) +#define CURLPROTO_MQTT (1<<28) +#define CURLPROTO_GOPHERS (1<<29) +#define CURLPROTO_ALL (~0) /* enable everything */ + +/* long may be 32 or 64 bits, but we should never depend on anything else + but 32 */ +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 +#define CURLOPTTYPE_BLOB 40000 + +/* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the + string options from the header file */ + + +#define CURLOPT(na,t,nu) na = t + nu +#define CURLOPTDEPRECATED(na,t,nu,v,m) na CURL_DEPRECATED(v,m) = t + nu + +/* CURLOPT aliases that make no runtime difference */ + +/* 'char *' argument to a string with a trailing zero */ +#define CURLOPTTYPE_STRINGPOINT CURLOPTTYPE_OBJECTPOINT + +/* 'struct curl_slist *' argument */ +#define CURLOPTTYPE_SLISTPOINT CURLOPTTYPE_OBJECTPOINT + +/* 'void *' argument passed untouched to callback */ +#define CURLOPTTYPE_CBPOINT CURLOPTTYPE_OBJECTPOINT + +/* 'long' argument with a set of values/bitmask */ +#define CURLOPTTYPE_VALUES CURLOPTTYPE_LONG + +/* + * All CURLOPT_* values. + */ + +typedef enum { + /* This is the FILE * or void * the regular output should be written to. */ + CURLOPT(CURLOPT_WRITEDATA, CURLOPTTYPE_CBPOINT, 1), + + /* The full URL to get/put */ + CURLOPT(CURLOPT_URL, CURLOPTTYPE_STRINGPOINT, 2), + + /* Port number to connect to, if other than default. */ + CURLOPT(CURLOPT_PORT, CURLOPTTYPE_LONG, 3), + + /* Name of proxy to use. */ + CURLOPT(CURLOPT_PROXY, CURLOPTTYPE_STRINGPOINT, 4), + + /* "user:password;options" to use when fetching. */ + CURLOPT(CURLOPT_USERPWD, CURLOPTTYPE_STRINGPOINT, 5), + + /* "user:password" to use with proxy. */ + CURLOPT(CURLOPT_PROXYUSERPWD, CURLOPTTYPE_STRINGPOINT, 6), + + /* Range to get, specified as an ASCII string. */ + CURLOPT(CURLOPT_RANGE, CURLOPTTYPE_STRINGPOINT, 7), + + /* not used */ + + /* Specified file stream to upload from (use as input): */ + CURLOPT(CURLOPT_READDATA, CURLOPTTYPE_CBPOINT, 9), + + /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE + * bytes big. */ + CURLOPT(CURLOPT_ERRORBUFFER, CURLOPTTYPE_OBJECTPOINT, 10), + + /* Function that will be called to store the output (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CURLOPT(CURLOPT_WRITEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 11), + + /* Function that will be called to read the input (instead of fread). The + * parameters will use fread() syntax, make sure to follow them. */ + CURLOPT(CURLOPT_READFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 12), + + /* Time-out the read operation after this amount of seconds */ + CURLOPT(CURLOPT_TIMEOUT, CURLOPTTYPE_LONG, 13), + + /* If CURLOPT_READDATA is used, this can be used to inform libcurl about + * how large the file being sent really is. That allows better error + * checking and better verifies that the upload was successful. -1 means + * unknown size. + * + * For large file support, there is also a _LARGE version of the key + * which takes an off_t type, allowing platforms with larger off_t + * sizes to handle larger files. See below for INFILESIZE_LARGE. + */ + CURLOPT(CURLOPT_INFILESIZE, CURLOPTTYPE_LONG, 14), + + /* POST static input fields. */ + CURLOPT(CURLOPT_POSTFIELDS, CURLOPTTYPE_OBJECTPOINT, 15), + + /* Set the referrer page (needed by some CGIs) */ + CURLOPT(CURLOPT_REFERER, CURLOPTTYPE_STRINGPOINT, 16), + + /* Set the FTP PORT string (interface name, named or numerical IP address) + Use i.e '-' to use default address. */ + CURLOPT(CURLOPT_FTPPORT, CURLOPTTYPE_STRINGPOINT, 17), + + /* Set the User-Agent string (examined by some CGIs) */ + CURLOPT(CURLOPT_USERAGENT, CURLOPTTYPE_STRINGPOINT, 18), + + /* If the download receives less than "low speed limit" bytes/second + * during "low speed time" seconds, the operations is aborted. + * You could i.e if you have a pretty high speed connection, abort if + * it is less than 2000 bytes/sec during 20 seconds. + */ + + /* Set the "low speed limit" */ + CURLOPT(CURLOPT_LOW_SPEED_LIMIT, CURLOPTTYPE_LONG, 19), + + /* Set the "low speed time" */ + CURLOPT(CURLOPT_LOW_SPEED_TIME, CURLOPTTYPE_LONG, 20), + + /* Set the continuation offset. + * + * Note there is also a _LARGE version of this key which uses + * off_t types, allowing for large file offsets on platforms which + * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. + */ + CURLOPT(CURLOPT_RESUME_FROM, CURLOPTTYPE_LONG, 21), + + /* Set cookie in request: */ + CURLOPT(CURLOPT_COOKIE, CURLOPTTYPE_STRINGPOINT, 22), + + /* This points to a linked list of headers, struct curl_slist kind. This + list is also used for RTSP (in spite of its name) */ + CURLOPT(CURLOPT_HTTPHEADER, CURLOPTTYPE_SLISTPOINT, 23), + + /* This points to a linked list of post entries, struct curl_httppost */ + CURLOPTDEPRECATED(CURLOPT_HTTPPOST, CURLOPTTYPE_OBJECTPOINT, 24, + 7.56.0, "Use CURLOPT_MIMEPOST"), + + /* name of the file keeping your private SSL-certificate */ + CURLOPT(CURLOPT_SSLCERT, CURLOPTTYPE_STRINGPOINT, 25), + + /* password for the SSL or SSH private key */ + CURLOPT(CURLOPT_KEYPASSWD, CURLOPTTYPE_STRINGPOINT, 26), + + /* send TYPE parameter? */ + CURLOPT(CURLOPT_CRLF, CURLOPTTYPE_LONG, 27), + + /* send linked-list of QUOTE commands */ + CURLOPT(CURLOPT_QUOTE, CURLOPTTYPE_SLISTPOINT, 28), + + /* send FILE * or void * to store headers to, if you use a callback it + is simply passed to the callback unmodified */ + CURLOPT(CURLOPT_HEADERDATA, CURLOPTTYPE_CBPOINT, 29), + + /* point to a file to read the initial cookies from, also enables + "cookie awareness" */ + CURLOPT(CURLOPT_COOKIEFILE, CURLOPTTYPE_STRINGPOINT, 31), + + /* What version to specifically try to use. + See CURL_SSLVERSION defines below. */ + CURLOPT(CURLOPT_SSLVERSION, CURLOPTTYPE_VALUES, 32), + + /* What kind of HTTP time condition to use, see defines */ + CURLOPT(CURLOPT_TIMECONDITION, CURLOPTTYPE_VALUES, 33), + + /* Time to use with the above condition. Specified in number of seconds + since 1 Jan 1970 */ + CURLOPT(CURLOPT_TIMEVALUE, CURLOPTTYPE_LONG, 34), + + /* 35 = OBSOLETE */ + + /* Custom request, for customizing the get command like + HTTP: DELETE, TRACE and others + FTP: to use a different list command + */ + CURLOPT(CURLOPT_CUSTOMREQUEST, CURLOPTTYPE_STRINGPOINT, 36), + + /* FILE handle to use instead of stderr */ + CURLOPT(CURLOPT_STDERR, CURLOPTTYPE_OBJECTPOINT, 37), + + /* 38 is not used */ + + /* send linked-list of post-transfer QUOTE commands */ + CURLOPT(CURLOPT_POSTQUOTE, CURLOPTTYPE_SLISTPOINT, 39), + + /* OBSOLETE, do not use! */ + CURLOPT(CURLOPT_OBSOLETE40, CURLOPTTYPE_OBJECTPOINT, 40), + + /* talk a lot */ + CURLOPT(CURLOPT_VERBOSE, CURLOPTTYPE_LONG, 41), + + /* throw the header out too */ + CURLOPT(CURLOPT_HEADER, CURLOPTTYPE_LONG, 42), + + /* shut off the progress meter */ + CURLOPT(CURLOPT_NOPROGRESS, CURLOPTTYPE_LONG, 43), + + /* use HEAD to get http document */ + CURLOPT(CURLOPT_NOBODY, CURLOPTTYPE_LONG, 44), + + /* no output on http error codes >= 400 */ + CURLOPT(CURLOPT_FAILONERROR, CURLOPTTYPE_LONG, 45), + + /* this is an upload */ + CURLOPT(CURLOPT_UPLOAD, CURLOPTTYPE_LONG, 46), + + /* HTTP POST method */ + CURLOPT(CURLOPT_POST, CURLOPTTYPE_LONG, 47), + + /* bare names when listing directories */ + CURLOPT(CURLOPT_DIRLISTONLY, CURLOPTTYPE_LONG, 48), + + /* Append instead of overwrite on upload! */ + CURLOPT(CURLOPT_APPEND, CURLOPTTYPE_LONG, 50), + + /* Specify whether to read the user+password from the .netrc or the URL. + * This must be one of the CURL_NETRC_* enums below. */ + CURLOPT(CURLOPT_NETRC, CURLOPTTYPE_VALUES, 51), + + /* use Location: Luke! */ + CURLOPT(CURLOPT_FOLLOWLOCATION, CURLOPTTYPE_LONG, 52), + + /* transfer data in text/ASCII format */ + CURLOPT(CURLOPT_TRANSFERTEXT, CURLOPTTYPE_LONG, 53), + + /* HTTP PUT */ + CURLOPTDEPRECATED(CURLOPT_PUT, CURLOPTTYPE_LONG, 54, + 7.12.1, "Use CURLOPT_UPLOAD"), + + /* 55 = OBSOLETE */ + + /* DEPRECATED + * Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_progress_callback + * prototype defines. */ + CURLOPTDEPRECATED(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56, + 7.32.0, "Use CURLOPT_XFERINFOFUNCTION"), + + /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION + callbacks */ + CURLOPT(CURLOPT_XFERINFODATA, CURLOPTTYPE_CBPOINT, 57), +#define CURLOPT_PROGRESSDATA CURLOPT_XFERINFODATA + + /* We want the referrer field set automatically when following locations */ + CURLOPT(CURLOPT_AUTOREFERER, CURLOPTTYPE_LONG, 58), + + /* Port of the proxy, can be set in the proxy string as well with: + "[host]:[port]" */ + CURLOPT(CURLOPT_PROXYPORT, CURLOPTTYPE_LONG, 59), + + /* size of the POST input data, if strlen() is not good to use */ + CURLOPT(CURLOPT_POSTFIELDSIZE, CURLOPTTYPE_LONG, 60), + + /* tunnel non-http operations through an HTTP proxy */ + CURLOPT(CURLOPT_HTTPPROXYTUNNEL, CURLOPTTYPE_LONG, 61), + + /* Set the interface string to use as outgoing network interface */ + CURLOPT(CURLOPT_INTERFACE, CURLOPTTYPE_STRINGPOINT, 62), + + /* Set the krb4/5 security level, this also enables krb4/5 awareness. This + * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string + * is set but does not match one of these, 'private' will be used. */ + CURLOPT(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63), + + /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + CURLOPT(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64), + + /* The CApath or CAfile used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_CAINFO, CURLOPTTYPE_STRINGPOINT, 65), + + /* 66 = OBSOLETE */ + /* 67 = OBSOLETE */ + + /* Maximum number of http redirects to follow */ + CURLOPT(CURLOPT_MAXREDIRS, CURLOPTTYPE_LONG, 68), + + /* Pass a long set to 1 to get the date of the requested document (if + possible)! Pass a zero to shut it off. */ + CURLOPT(CURLOPT_FILETIME, CURLOPTTYPE_LONG, 69), + + /* This points to a linked list of telnet options */ + CURLOPT(CURLOPT_TELNETOPTIONS, CURLOPTTYPE_SLISTPOINT, 70), + + /* Max amount of cached alive connections */ + CURLOPT(CURLOPT_MAXCONNECTS, CURLOPTTYPE_LONG, 71), + + /* OBSOLETE, do not use! */ + CURLOPT(CURLOPT_OBSOLETE72, CURLOPTTYPE_LONG, 72), + + /* 73 = OBSOLETE */ + + /* Set to explicitly use a new connection for the upcoming transfer. + Do not use this unless you are absolutely sure of this, as it makes the + operation slower and is less friendly for the network. */ + CURLOPT(CURLOPT_FRESH_CONNECT, CURLOPTTYPE_LONG, 74), + + /* Set to explicitly forbid the upcoming transfer's connection to be reused + when done. Do not use this unless you are absolutely sure of this, as it + makes the operation slower and is less friendly for the network. */ + CURLOPT(CURLOPT_FORBID_REUSE, CURLOPTTYPE_LONG, 75), + + /* Set to a filename that contains random data for libcurl to use to + seed the random engine when doing SSL connects. */ + CURLOPTDEPRECATED(CURLOPT_RANDOM_FILE, CURLOPTTYPE_STRINGPOINT, 76, + 7.84.0, "Serves no purpose anymore"), + + /* Set to the Entropy Gathering Daemon socket pathname */ + CURLOPTDEPRECATED(CURLOPT_EGDSOCKET, CURLOPTTYPE_STRINGPOINT, 77, + 7.84.0, "Serves no purpose anymore"), + + /* Time-out connect operations after this amount of seconds, if connects are + OK within this time, then fine... This only aborts the connect phase. */ + CURLOPT(CURLOPT_CONNECTTIMEOUT, CURLOPTTYPE_LONG, 78), + + /* Function that will be called to store headers (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CURLOPT(CURLOPT_HEADERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 79), + + /* Set this to force the HTTP request to get back to GET. Only really usable + if POST, PUT or a custom request have been used first. + */ + CURLOPT(CURLOPT_HTTPGET, CURLOPTTYPE_LONG, 80), + + /* Set if we should verify the Common name from the peer certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches the + * provided hostname. */ + CURLOPT(CURLOPT_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 81), + + /* Specify which filename to write all known cookies in after completed + operation. Set filename to "-" (dash) to make it go to stdout. */ + CURLOPT(CURLOPT_COOKIEJAR, CURLOPTTYPE_STRINGPOINT, 82), + + /* Specify which SSL ciphers to use */ + CURLOPT(CURLOPT_SSL_CIPHER_LIST, CURLOPTTYPE_STRINGPOINT, 83), + + /* Specify which HTTP version to use! This must be set to one of the + CURL_HTTP_VERSION* enums set below. */ + CURLOPT(CURLOPT_HTTP_VERSION, CURLOPTTYPE_VALUES, 84), + + /* Specifically switch on or off the FTP engine's use of the EPSV command. By + default, that one will always be attempted before the more traditional + PASV command. */ + CURLOPT(CURLOPT_FTP_USE_EPSV, CURLOPTTYPE_LONG, 85), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ + CURLOPT(CURLOPT_SSLCERTTYPE, CURLOPTTYPE_STRINGPOINT, 86), + + /* name of the file keeping your private SSL-key */ + CURLOPT(CURLOPT_SSLKEY, CURLOPTTYPE_STRINGPOINT, 87), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ + CURLOPT(CURLOPT_SSLKEYTYPE, CURLOPTTYPE_STRINGPOINT, 88), + + /* crypto engine for the SSL-sub system */ + CURLOPT(CURLOPT_SSLENGINE, CURLOPTTYPE_STRINGPOINT, 89), + + /* set the crypto engine for the SSL-sub system as default + the param has no meaning... + */ + CURLOPT(CURLOPT_SSLENGINE_DEFAULT, CURLOPTTYPE_LONG, 90), + + /* Non-zero value means to use the global dns cache */ + /* DEPRECATED, do not use! */ + CURLOPTDEPRECATED(CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOPTTYPE_LONG, 91, + 7.11.1, "Use CURLOPT_SHARE"), + + /* DNS cache timeout */ + CURLOPT(CURLOPT_DNS_CACHE_TIMEOUT, CURLOPTTYPE_LONG, 92), + + /* send linked-list of pre-transfer QUOTE commands */ + CURLOPT(CURLOPT_PREQUOTE, CURLOPTTYPE_SLISTPOINT, 93), + + /* set the debug function */ + CURLOPT(CURLOPT_DEBUGFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 94), + + /* set the data for the debug function */ + CURLOPT(CURLOPT_DEBUGDATA, CURLOPTTYPE_CBPOINT, 95), + + /* mark this as start of a cookie session */ + CURLOPT(CURLOPT_COOKIESESSION, CURLOPTTYPE_LONG, 96), + + /* The CApath directory used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_CAPATH, CURLOPTTYPE_STRINGPOINT, 97), + + /* Instruct libcurl to use a smaller receive buffer */ + CURLOPT(CURLOPT_BUFFERSIZE, CURLOPTTYPE_LONG, 98), + + /* Instruct libcurl to not use any signal/alarm handlers, even when using + timeouts. This option is useful for multi-threaded applications. + See libcurl-the-guide for more background information. */ + CURLOPT(CURLOPT_NOSIGNAL, CURLOPTTYPE_LONG, 99), + + /* Provide a CURLShare for mutexing non-ts data */ + CURLOPT(CURLOPT_SHARE, CURLOPTTYPE_OBJECTPOINT, 100), + + /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), + CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and + CURLPROXY_SOCKS5. */ + CURLOPT(CURLOPT_PROXYTYPE, CURLOPTTYPE_VALUES, 101), + + /* Set the Accept-Encoding string. Use this to tell a server you would like + the response to be compressed. Before 7.21.6, this was known as + CURLOPT_ENCODING */ + CURLOPT(CURLOPT_ACCEPT_ENCODING, CURLOPTTYPE_STRINGPOINT, 102), + + /* Set pointer to private data */ + CURLOPT(CURLOPT_PRIVATE, CURLOPTTYPE_OBJECTPOINT, 103), + + /* Set aliases for HTTP 200 in the HTTP Response header */ + CURLOPT(CURLOPT_HTTP200ALIASES, CURLOPTTYPE_SLISTPOINT, 104), + + /* Continue to send authentication (user+password) when following locations, + even when hostname changed. This can potentially send off the name + and password to whatever host the server decides. */ + CURLOPT(CURLOPT_UNRESTRICTED_AUTH, CURLOPTTYPE_LONG, 105), + + /* Specifically switch on or off the FTP engine's use of the EPRT command ( + it also disables the LPRT attempt). By default, those ones will always be + attempted before the good old traditional PORT command. */ + CURLOPT(CURLOPT_FTP_USE_EPRT, CURLOPTTYPE_LONG, 106), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_USERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CURLOPT(CURLOPT_HTTPAUTH, CURLOPTTYPE_VALUES, 107), + + /* Set the ssl context callback function, currently only for OpenSSL or + wolfSSL ssl_ctx, or mbedTLS mbedtls_ssl_config in the second argument. + The function must match the curl_ssl_ctx_callback prototype. */ + CURLOPT(CURLOPT_SSL_CTX_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 108), + + /* Set the userdata for the ssl context callback function's third + argument */ + CURLOPT(CURLOPT_SSL_CTX_DATA, CURLOPTTYPE_CBPOINT, 109), + + /* FTP Option that causes missing dirs to be created on the remote server. + In 7.19.4 we introduced the convenience enums for this option using the + CURLFTP_CREATE_DIR prefix. + */ + CURLOPT(CURLOPT_FTP_CREATE_MISSING_DIRS, CURLOPTTYPE_LONG, 110), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CURLOPT(CURLOPT_PROXYAUTH, CURLOPTTYPE_VALUES, 111), + + /* Option that changes the timeout, in seconds, associated with getting a + response. This is different from transfer timeout time and essentially + places a demand on the server to acknowledge commands in a timely + manner. For FTP, SMTP, IMAP and POP3. */ + CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT, CURLOPTTYPE_LONG, 112), + + /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to + tell libcurl to use those IP versions only. This only has effect on + systems with support for more than one, i.e IPv4 _and_ IPv6. */ + CURLOPT(CURLOPT_IPRESOLVE, CURLOPTTYPE_VALUES, 113), + + /* Set this option to limit the size of a file that will be downloaded from + an HTTP or FTP server. + + Note there is also _LARGE version which adds large file support for + platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ + CURLOPT(CURLOPT_MAXFILESIZE, CURLOPTTYPE_LONG, 114), + + /* See the comment for INFILESIZE above, but in short, specifies + * the size of the file being uploaded. -1 means unknown. + */ + CURLOPT(CURLOPT_INFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 115), + + /* Sets the continuation offset. There is also a CURLOPTTYPE_LONG version + * of this; look above for RESUME_FROM. + */ + CURLOPT(CURLOPT_RESUME_FROM_LARGE, CURLOPTTYPE_OFF_T, 116), + + /* Sets the maximum size of data that will be downloaded from + * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. + */ + CURLOPT(CURLOPT_MAXFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 117), + + /* Set this option to the filename of your .netrc file you want libcurl + to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + a poor attempt to find the user's home directory and check for a .netrc + file in there. */ + CURLOPT(CURLOPT_NETRC_FILE, CURLOPTTYPE_STRINGPOINT, 118), + + /* Enable SSL/TLS for FTP, pick one of: + CURLUSESSL_TRY - try using SSL, proceed anyway otherwise + CURLUSESSL_CONTROL - SSL for the control connection or fail + CURLUSESSL_ALL - SSL for all communication or fail + */ + CURLOPT(CURLOPT_USE_SSL, CURLOPTTYPE_VALUES, 119), + + /* The _LARGE version of the standard POSTFIELDSIZE option */ + CURLOPT(CURLOPT_POSTFIELDSIZE_LARGE, CURLOPTTYPE_OFF_T, 120), + + /* Enable/disable the TCP Nagle algorithm */ + CURLOPT(CURLOPT_TCP_NODELAY, CURLOPTTYPE_LONG, 121), + + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 123 OBSOLETE. Gone in 7.16.0 */ + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 127 OBSOLETE. Gone in 7.16.0 */ + /* 128 OBSOLETE. Gone in 7.16.0 */ + + /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option + can be used to change libcurl's default action which is to first try + "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK + response has been received. + + Available parameters are: + CURLFTPAUTH_DEFAULT - let libcurl decide + CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS + CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL + */ + CURLOPT(CURLOPT_FTPSSLAUTH, CURLOPTTYPE_VALUES, 129), + + CURLOPTDEPRECATED(CURLOPT_IOCTLFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 130, + 7.18.0, "Use CURLOPT_SEEKFUNCTION"), + CURLOPTDEPRECATED(CURLOPT_IOCTLDATA, CURLOPTTYPE_CBPOINT, 131, + 7.18.0, "Use CURLOPT_SEEKDATA"), + + /* 132 OBSOLETE. Gone in 7.16.0 */ + /* 133 OBSOLETE. Gone in 7.16.0 */ + + /* null-terminated string for pass on to the FTP server when asked for + "account" info */ + CURLOPT(CURLOPT_FTP_ACCOUNT, CURLOPTTYPE_STRINGPOINT, 134), + + /* feed cookie into cookie engine */ + CURLOPT(CURLOPT_COOKIELIST, CURLOPTTYPE_STRINGPOINT, 135), + + /* ignore Content-Length */ + CURLOPT(CURLOPT_IGNORE_CONTENT_LENGTH, CURLOPTTYPE_LONG, 136), + + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CURLOPT(CURLOPT_FTP_SKIP_PASV_IP, CURLOPTTYPE_LONG, 137), + + /* Select "file method" to use when doing FTP, see the curl_ftpmethod + above. */ + CURLOPT(CURLOPT_FTP_FILEMETHOD, CURLOPTTYPE_VALUES, 138), + + /* Local port number to bind the socket to */ + CURLOPT(CURLOPT_LOCALPORT, CURLOPTTYPE_LONG, 139), + + /* Number of ports to try, including the first one set with LOCALPORT. + Thus, setting it to 1 will make no additional attempts but the first. + */ + CURLOPT(CURLOPT_LOCALPORTRANGE, CURLOPTTYPE_LONG, 140), + + /* no transfer, set up connection and let application use the socket by + extracting it with CURLINFO_LASTSOCKET */ + CURLOPT(CURLOPT_CONNECT_ONLY, CURLOPTTYPE_LONG, 141), + + /* Function that will be called to convert from the + network encoding (instead of using the iconv calls in libcurl) */ + CURLOPTDEPRECATED(CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOPTTYPE_FUNCTIONPOINT, 142, + 7.82.0, "Serves no purpose anymore"), + + /* Function that will be called to convert to the + network encoding (instead of using the iconv calls in libcurl) */ + CURLOPTDEPRECATED(CURLOPT_CONV_TO_NETWORK_FUNCTION, + CURLOPTTYPE_FUNCTIONPOINT, 143, + 7.82.0, "Serves no purpose anymore"), + + /* Function that will be called to convert from UTF8 + (instead of using the iconv calls in libcurl) + Note that this is used only for SSL certificate processing */ + CURLOPTDEPRECATED(CURLOPT_CONV_FROM_UTF8_FUNCTION, + CURLOPTTYPE_FUNCTIONPOINT, 144, + 7.82.0, "Serves no purpose anymore"), + + /* if the connection proceeds too quickly then need to slow it down */ + /* limit-rate: maximum number of bytes per second to send or receive */ + CURLOPT(CURLOPT_MAX_SEND_SPEED_LARGE, CURLOPTTYPE_OFF_T, 145), + CURLOPT(CURLOPT_MAX_RECV_SPEED_LARGE, CURLOPTTYPE_OFF_T, 146), + + /* Pointer to command string to send if USER/PASS fails. */ + CURLOPT(CURLOPT_FTP_ALTERNATIVE_TO_USER, CURLOPTTYPE_STRINGPOINT, 147), + + /* callback function for setting socket options */ + CURLOPT(CURLOPT_SOCKOPTFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 148), + CURLOPT(CURLOPT_SOCKOPTDATA, CURLOPTTYPE_CBPOINT, 149), + + /* set to 0 to disable session ID reuse for this transfer, default is + enabled (== 1) */ + CURLOPT(CURLOPT_SSL_SESSIONID_CACHE, CURLOPTTYPE_LONG, 150), + + /* allowed SSH authentication methods */ + CURLOPT(CURLOPT_SSH_AUTH_TYPES, CURLOPTTYPE_VALUES, 151), + + /* Used by scp/sftp to do public/private key authentication */ + CURLOPT(CURLOPT_SSH_PUBLIC_KEYFILE, CURLOPTTYPE_STRINGPOINT, 152), + CURLOPT(CURLOPT_SSH_PRIVATE_KEYFILE, CURLOPTTYPE_STRINGPOINT, 153), + + /* Send CCC (Clear Command Channel) after authentication */ + CURLOPT(CURLOPT_FTP_SSL_CCC, CURLOPTTYPE_LONG, 154), + + /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ + CURLOPT(CURLOPT_TIMEOUT_MS, CURLOPTTYPE_LONG, 155), + CURLOPT(CURLOPT_CONNECTTIMEOUT_MS, CURLOPTTYPE_LONG, 156), + + /* set to zero to disable the libcurl's decoding and thus pass the raw body + data to the application even when it is encoded/compressed */ + CURLOPT(CURLOPT_HTTP_TRANSFER_DECODING, CURLOPTTYPE_LONG, 157), + CURLOPT(CURLOPT_HTTP_CONTENT_DECODING, CURLOPTTYPE_LONG, 158), + + /* Permission used when creating new files and directories on the remote + server for protocols that support it, SFTP/SCP/FILE */ + CURLOPT(CURLOPT_NEW_FILE_PERMS, CURLOPTTYPE_LONG, 159), + CURLOPT(CURLOPT_NEW_DIRECTORY_PERMS, CURLOPTTYPE_LONG, 160), + + /* Set the behavior of POST when redirecting. Values must be set to one + of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ + CURLOPT(CURLOPT_POSTREDIR, CURLOPTTYPE_VALUES, 161), + + /* used by scp/sftp to verify the host's public key */ + CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, CURLOPTTYPE_STRINGPOINT, 162), + + /* Callback function for opening socket (instead of socket(2)). Optionally, + callback is able change the address or refuse to connect returning + CURL_SOCKET_BAD. The callback should have type + curl_opensocket_callback */ + CURLOPT(CURLOPT_OPENSOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 163), + CURLOPT(CURLOPT_OPENSOCKETDATA, CURLOPTTYPE_CBPOINT, 164), + + /* POST volatile input fields. */ + CURLOPT(CURLOPT_COPYPOSTFIELDS, CURLOPTTYPE_OBJECTPOINT, 165), + + /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ + CURLOPT(CURLOPT_PROXY_TRANSFER_MODE, CURLOPTTYPE_LONG, 166), + + /* Callback function for seeking in the input stream */ + CURLOPT(CURLOPT_SEEKFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 167), + CURLOPT(CURLOPT_SEEKDATA, CURLOPTTYPE_CBPOINT, 168), + + /* CRL file */ + CURLOPT(CURLOPT_CRLFILE, CURLOPTTYPE_STRINGPOINT, 169), + + /* Issuer certificate */ + CURLOPT(CURLOPT_ISSUERCERT, CURLOPTTYPE_STRINGPOINT, 170), + + /* (IPv6) Address scope */ + CURLOPT(CURLOPT_ADDRESS_SCOPE, CURLOPTTYPE_LONG, 171), + + /* Collect certificate chain info and allow it to get retrievable with + CURLINFO_CERTINFO after the transfer is complete. */ + CURLOPT(CURLOPT_CERTINFO, CURLOPTTYPE_LONG, 172), + + /* "name" and "pwd" to use when fetching. */ + CURLOPT(CURLOPT_USERNAME, CURLOPTTYPE_STRINGPOINT, 173), + CURLOPT(CURLOPT_PASSWORD, CURLOPTTYPE_STRINGPOINT, 174), + + /* "name" and "pwd" to use with Proxy when fetching. */ + CURLOPT(CURLOPT_PROXYUSERNAME, CURLOPTTYPE_STRINGPOINT, 175), + CURLOPT(CURLOPT_PROXYPASSWORD, CURLOPTTYPE_STRINGPOINT, 176), + + /* Comma separated list of hostnames defining no-proxy zones. These should + match both hostnames directly, and hostnames within a domain. For + example, local.com will match local.com and www.local.com, but NOT + notlocal.com or www.notlocal.com. For compatibility with other + implementations of this, .local.com will be considered to be the same as + local.com. A single * is the only valid wildcard, and effectively + disables the use of proxy. */ + CURLOPT(CURLOPT_NOPROXY, CURLOPTTYPE_STRINGPOINT, 177), + + /* block size for TFTP transfers */ + CURLOPT(CURLOPT_TFTP_BLKSIZE, CURLOPTTYPE_LONG, 178), + + /* Socks Service */ + /* DEPRECATED, do not use! */ + CURLOPTDEPRECATED(CURLOPT_SOCKS5_GSSAPI_SERVICE, + CURLOPTTYPE_STRINGPOINT, 179, + 7.49.0, "Use CURLOPT_PROXY_SERVICE_NAME"), + + /* Socks Service */ + CURLOPT(CURLOPT_SOCKS5_GSSAPI_NEC, CURLOPTTYPE_LONG, 180), + + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal + with. Defaults to CURLPROTO_ALL. */ + CURLOPTDEPRECATED(CURLOPT_PROTOCOLS, CURLOPTTYPE_LONG, 181, + 7.85.0, "Use CURLOPT_PROTOCOLS_STR"), + + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs + to be set in both bitmasks to be allowed to get redirected to. */ + CURLOPTDEPRECATED(CURLOPT_REDIR_PROTOCOLS, CURLOPTTYPE_LONG, 182, + 7.85.0, "Use CURLOPT_REDIR_PROTOCOLS_STR"), + + /* set the SSH knownhost filename to use */ + CURLOPT(CURLOPT_SSH_KNOWNHOSTS, CURLOPTTYPE_STRINGPOINT, 183), + + /* set the SSH host key callback, must point to a curl_sshkeycallback + function */ + CURLOPT(CURLOPT_SSH_KEYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 184), + + /* set the SSH host key callback custom pointer */ + CURLOPT(CURLOPT_SSH_KEYDATA, CURLOPTTYPE_CBPOINT, 185), + + /* set the SMTP mail originator */ + CURLOPT(CURLOPT_MAIL_FROM, CURLOPTTYPE_STRINGPOINT, 186), + + /* set the list of SMTP mail receiver(s) */ + CURLOPT(CURLOPT_MAIL_RCPT, CURLOPTTYPE_SLISTPOINT, 187), + + /* FTP: send PRET before PASV */ + CURLOPT(CURLOPT_FTP_USE_PRET, CURLOPTTYPE_LONG, 188), + + /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ + CURLOPT(CURLOPT_RTSP_REQUEST, CURLOPTTYPE_VALUES, 189), + + /* The RTSP session identifier */ + CURLOPT(CURLOPT_RTSP_SESSION_ID, CURLOPTTYPE_STRINGPOINT, 190), + + /* The RTSP stream URI */ + CURLOPT(CURLOPT_RTSP_STREAM_URI, CURLOPTTYPE_STRINGPOINT, 191), + + /* The Transport: header to use in RTSP requests */ + CURLOPT(CURLOPT_RTSP_TRANSPORT, CURLOPTTYPE_STRINGPOINT, 192), + + /* Manually initialize the client RTSP CSeq for this handle */ + CURLOPT(CURLOPT_RTSP_CLIENT_CSEQ, CURLOPTTYPE_LONG, 193), + + /* Manually initialize the server RTSP CSeq for this handle */ + CURLOPT(CURLOPT_RTSP_SERVER_CSEQ, CURLOPTTYPE_LONG, 194), + + /* The stream to pass to INTERLEAVEFUNCTION. */ + CURLOPT(CURLOPT_INTERLEAVEDATA, CURLOPTTYPE_CBPOINT, 195), + + /* Let the application define a custom write method for RTP data */ + CURLOPT(CURLOPT_INTERLEAVEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 196), + + /* Turn on wildcard matching */ + CURLOPT(CURLOPT_WILDCARDMATCH, CURLOPTTYPE_LONG, 197), + + /* Directory matching callback called before downloading of an + individual file (chunk) started */ + CURLOPT(CURLOPT_CHUNK_BGN_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 198), + + /* Directory matching callback called after the file (chunk) + was downloaded, or skipped */ + CURLOPT(CURLOPT_CHUNK_END_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 199), + + /* Change match (fnmatch-like) callback for wildcard matching */ + CURLOPT(CURLOPT_FNMATCH_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 200), + + /* Let the application define custom chunk data pointer */ + CURLOPT(CURLOPT_CHUNK_DATA, CURLOPTTYPE_CBPOINT, 201), + + /* FNMATCH_FUNCTION user pointer */ + CURLOPT(CURLOPT_FNMATCH_DATA, CURLOPTTYPE_CBPOINT, 202), + + /* send linked-list of name:port:address sets */ + CURLOPT(CURLOPT_RESOLVE, CURLOPTTYPE_SLISTPOINT, 203), + + /* Set a username for authenticated TLS */ + CURLOPT(CURLOPT_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 204), + + /* Set a password for authenticated TLS */ + CURLOPT(CURLOPT_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 205), + + /* Set authentication type for authenticated TLS */ + CURLOPT(CURLOPT_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 206), + + /* Set to 1 to enable the "TE:" header in HTTP requests to ask for + compressed transfer-encoded responses. Set to 0 to disable the use of TE: + in outgoing requests. The current default is 0, but it might change in a + future libcurl release. + + libcurl will ask for the compressed methods it knows of, and if that + is not any, it will not ask for transfer-encoding at all even if this + option is set to 1. + + */ + CURLOPT(CURLOPT_TRANSFER_ENCODING, CURLOPTTYPE_LONG, 207), + + /* Callback function for closing socket (instead of close(2)). The callback + should have type curl_closesocket_callback */ + CURLOPT(CURLOPT_CLOSESOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 208), + CURLOPT(CURLOPT_CLOSESOCKETDATA, CURLOPTTYPE_CBPOINT, 209), + + /* allow GSSAPI credential delegation */ + CURLOPT(CURLOPT_GSSAPI_DELEGATION, CURLOPTTYPE_VALUES, 210), + + /* Set the name servers to use for DNS resolution. + * Only supported by the c-ares DNS backend */ + CURLOPT(CURLOPT_DNS_SERVERS, CURLOPTTYPE_STRINGPOINT, 211), + + /* Time-out accept operations (currently for FTP only) after this amount + of milliseconds. */ + CURLOPT(CURLOPT_ACCEPTTIMEOUT_MS, CURLOPTTYPE_LONG, 212), + + /* Set TCP keepalive */ + CURLOPT(CURLOPT_TCP_KEEPALIVE, CURLOPTTYPE_LONG, 213), + + /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */ + CURLOPT(CURLOPT_TCP_KEEPIDLE, CURLOPTTYPE_LONG, 214), + CURLOPT(CURLOPT_TCP_KEEPINTVL, CURLOPTTYPE_LONG, 215), + + /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */ + CURLOPT(CURLOPT_SSL_OPTIONS, CURLOPTTYPE_VALUES, 216), + + /* Set the SMTP auth originator */ + CURLOPT(CURLOPT_MAIL_AUTH, CURLOPTTYPE_STRINGPOINT, 217), + + /* Enable/disable SASL initial response */ + CURLOPT(CURLOPT_SASL_IR, CURLOPTTYPE_LONG, 218), + + /* Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_xferinfo_callback + * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ + CURLOPT(CURLOPT_XFERINFOFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 219), + + /* The XOAUTH2 bearer token */ + CURLOPT(CURLOPT_XOAUTH2_BEARER, CURLOPTTYPE_STRINGPOINT, 220), + + /* Set the interface string to use as outgoing network + * interface for DNS requests. + * Only supported by the c-ares DNS backend */ + CURLOPT(CURLOPT_DNS_INTERFACE, CURLOPTTYPE_STRINGPOINT, 221), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CURLOPT(CURLOPT_DNS_LOCAL_IP4, CURLOPTTYPE_STRINGPOINT, 222), + + /* Set the local IPv6 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CURLOPT(CURLOPT_DNS_LOCAL_IP6, CURLOPTTYPE_STRINGPOINT, 223), + + /* Set authentication options directly */ + CURLOPT(CURLOPT_LOGIN_OPTIONS, CURLOPTTYPE_STRINGPOINT, 224), + + /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + CURLOPTDEPRECATED(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225, + 7.86.0, "Has no function"), + + /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + CURLOPT(CURLOPT_SSL_ENABLE_ALPN, CURLOPTTYPE_LONG, 226), + + /* Time to wait for a response to an HTTP request containing an + * Expect: 100-continue header before sending the data anyway. */ + CURLOPT(CURLOPT_EXPECT_100_TIMEOUT_MS, CURLOPTTYPE_LONG, 227), + + /* This points to a linked list of headers used for proxy requests only, + struct curl_slist kind */ + CURLOPT(CURLOPT_PROXYHEADER, CURLOPTTYPE_SLISTPOINT, 228), + + /* Pass in a bitmask of "header options" */ + CURLOPT(CURLOPT_HEADEROPT, CURLOPTTYPE_VALUES, 229), + + /* The public key in DER form used to validate the peer public key + this option is used only if SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_PINNEDPUBLICKEY, CURLOPTTYPE_STRINGPOINT, 230), + + /* Path to Unix domain socket */ + CURLOPT(CURLOPT_UNIX_SOCKET_PATH, CURLOPTTYPE_STRINGPOINT, 231), + + /* Set if we should verify the certificate status. */ + CURLOPT(CURLOPT_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 232), + + /* Set if we should enable TLS false start. */ + CURLOPT(CURLOPT_SSL_FALSESTART, CURLOPTTYPE_LONG, 233), + + /* Do not squash dot-dot sequences */ + CURLOPT(CURLOPT_PATH_AS_IS, CURLOPTTYPE_LONG, 234), + + /* Proxy Service Name */ + CURLOPT(CURLOPT_PROXY_SERVICE_NAME, CURLOPTTYPE_STRINGPOINT, 235), + + /* Service Name */ + CURLOPT(CURLOPT_SERVICE_NAME, CURLOPTTYPE_STRINGPOINT, 236), + + /* Wait/do not wait for pipe/mutex to clarify */ + CURLOPT(CURLOPT_PIPEWAIT, CURLOPTTYPE_LONG, 237), + + /* Set the protocol used when curl is given a URL without a protocol */ + CURLOPT(CURLOPT_DEFAULT_PROTOCOL, CURLOPTTYPE_STRINGPOINT, 238), + + /* Set stream weight, 1 - 256 (default is 16) */ + CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239), + + /* Set stream dependency on another CURL handle */ + CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240), + + /* Set E-xclusive stream dependency on another CURL handle */ + CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241), + + /* Do not send any tftp option requests to the server */ + CURLOPT(CURLOPT_TFTP_NO_OPTIONS, CURLOPTTYPE_LONG, 242), + + /* Linked-list of host:port:connect-to-host:connect-to-port, + overrides the URL's host:port (only for the network layer) */ + CURLOPT(CURLOPT_CONNECT_TO, CURLOPTTYPE_SLISTPOINT, 243), + + /* Set TCP Fast Open */ + CURLOPT(CURLOPT_TCP_FASTOPEN, CURLOPTTYPE_LONG, 244), + + /* Continue to send data if the server responds early with an + * HTTP status code >= 300 */ + CURLOPT(CURLOPT_KEEP_SENDING_ON_ERROR, CURLOPTTYPE_LONG, 245), + + /* The CApath or CAfile used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_PROXY_CAINFO, CURLOPTTYPE_STRINGPOINT, 246), + + /* The CApath directory used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_PROXY_CAPATH, CURLOPTTYPE_STRINGPOINT, 247), + + /* Set if we should verify the proxy in ssl handshake, + set 1 to verify. */ + CURLOPT(CURLOPT_PROXY_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 248), + + /* Set if we should verify the Common name from the proxy certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches + * the provided hostname. */ + CURLOPT(CURLOPT_PROXY_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 249), + + /* What version to specifically try to use for proxy. + See CURL_SSLVERSION defines below. */ + CURLOPT(CURLOPT_PROXY_SSLVERSION, CURLOPTTYPE_VALUES, 250), + + /* Set a username for authenticated TLS for proxy */ + CURLOPT(CURLOPT_PROXY_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 251), + + /* Set a password for authenticated TLS for proxy */ + CURLOPT(CURLOPT_PROXY_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 252), + + /* Set authentication type for authenticated TLS for proxy */ + CURLOPT(CURLOPT_PROXY_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 253), + + /* name of the file keeping your private SSL-certificate for proxy */ + CURLOPT(CURLOPT_PROXY_SSLCERT, CURLOPTTYPE_STRINGPOINT, 254), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") for + proxy */ + CURLOPT(CURLOPT_PROXY_SSLCERTTYPE, CURLOPTTYPE_STRINGPOINT, 255), + + /* name of the file keeping your private SSL-key for proxy */ + CURLOPT(CURLOPT_PROXY_SSLKEY, CURLOPTTYPE_STRINGPOINT, 256), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") for + proxy */ + CURLOPT(CURLOPT_PROXY_SSLKEYTYPE, CURLOPTTYPE_STRINGPOINT, 257), + + /* password for the SSL private key for proxy */ + CURLOPT(CURLOPT_PROXY_KEYPASSWD, CURLOPTTYPE_STRINGPOINT, 258), + + /* Specify which SSL ciphers to use for proxy */ + CURLOPT(CURLOPT_PROXY_SSL_CIPHER_LIST, CURLOPTTYPE_STRINGPOINT, 259), + + /* CRL file for proxy */ + CURLOPT(CURLOPT_PROXY_CRLFILE, CURLOPTTYPE_STRINGPOINT, 260), + + /* Enable/disable specific SSL features with a bitmask for proxy, see + CURLSSLOPT_* */ + CURLOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLOPTTYPE_LONG, 261), + + /* Name of pre proxy to use. */ + CURLOPT(CURLOPT_PRE_PROXY, CURLOPTTYPE_STRINGPOINT, 262), + + /* The public key in DER form used to validate the proxy public key + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_PROXY_PINNEDPUBLICKEY, CURLOPTTYPE_STRINGPOINT, 263), + + /* Path to an abstract Unix domain socket */ + CURLOPT(CURLOPT_ABSTRACT_UNIX_SOCKET, CURLOPTTYPE_STRINGPOINT, 264), + + /* Suppress proxy CONNECT response headers from user callbacks */ + CURLOPT(CURLOPT_SUPPRESS_CONNECT_HEADERS, CURLOPTTYPE_LONG, 265), + + /* The request target, instead of extracted from the URL */ + CURLOPT(CURLOPT_REQUEST_TARGET, CURLOPTTYPE_STRINGPOINT, 266), + + /* bitmask of allowed auth methods for connections to SOCKS5 proxies */ + CURLOPT(CURLOPT_SOCKS5_AUTH, CURLOPTTYPE_LONG, 267), + + /* Enable/disable SSH compression */ + CURLOPT(CURLOPT_SSH_COMPRESSION, CURLOPTTYPE_LONG, 268), + + /* Post MIME data. */ + CURLOPT(CURLOPT_MIMEPOST, CURLOPTTYPE_OBJECTPOINT, 269), + + /* Time to use with the CURLOPT_TIMECONDITION. Specified in number of + seconds since 1 Jan 1970. */ + CURLOPT(CURLOPT_TIMEVALUE_LARGE, CURLOPTTYPE_OFF_T, 270), + + /* Head start in milliseconds to give happy eyeballs. */ + CURLOPT(CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CURLOPTTYPE_LONG, 271), + + /* Function that will be called before a resolver request is made */ + CURLOPT(CURLOPT_RESOLVER_START_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 272), + + /* User data to pass to the resolver start callback. */ + CURLOPT(CURLOPT_RESOLVER_START_DATA, CURLOPTTYPE_CBPOINT, 273), + + /* send HAProxy PROXY protocol header? */ + CURLOPT(CURLOPT_HAPROXYPROTOCOL, CURLOPTTYPE_LONG, 274), + + /* shuffle addresses before use when DNS returns multiple */ + CURLOPT(CURLOPT_DNS_SHUFFLE_ADDRESSES, CURLOPTTYPE_LONG, 275), + + /* Specify which TLS 1.3 ciphers suites to use */ + CURLOPT(CURLOPT_TLS13_CIPHERS, CURLOPTTYPE_STRINGPOINT, 276), + CURLOPT(CURLOPT_PROXY_TLS13_CIPHERS, CURLOPTTYPE_STRINGPOINT, 277), + + /* Disallow specifying username/login in URL. */ + CURLOPT(CURLOPT_DISALLOW_USERNAME_IN_URL, CURLOPTTYPE_LONG, 278), + + /* DNS-over-HTTPS URL */ + CURLOPT(CURLOPT_DOH_URL, CURLOPTTYPE_STRINGPOINT, 279), + + /* Preferred buffer size to use for uploads */ + CURLOPT(CURLOPT_UPLOAD_BUFFERSIZE, CURLOPTTYPE_LONG, 280), + + /* Time in ms between connection upkeep calls for long-lived connections. */ + CURLOPT(CURLOPT_UPKEEP_INTERVAL_MS, CURLOPTTYPE_LONG, 281), + + /* Specify URL using CURL URL API. */ + CURLOPT(CURLOPT_CURLU, CURLOPTTYPE_OBJECTPOINT, 282), + + /* add trailing data just after no more data is available */ + CURLOPT(CURLOPT_TRAILERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 283), + + /* pointer to be passed to HTTP_TRAILER_FUNCTION */ + CURLOPT(CURLOPT_TRAILERDATA, CURLOPTTYPE_CBPOINT, 284), + + /* set this to 1L to allow HTTP/0.9 responses or 0L to disallow */ + CURLOPT(CURLOPT_HTTP09_ALLOWED, CURLOPTTYPE_LONG, 285), + + /* alt-svc control bitmask */ + CURLOPT(CURLOPT_ALTSVC_CTRL, CURLOPTTYPE_LONG, 286), + + /* alt-svc cache filename to possibly read from/write to */ + CURLOPT(CURLOPT_ALTSVC, CURLOPTTYPE_STRINGPOINT, 287), + + /* maximum age (idle time) of a connection to consider it for reuse + * (in seconds) */ + CURLOPT(CURLOPT_MAXAGE_CONN, CURLOPTTYPE_LONG, 288), + + /* SASL authorization identity */ + CURLOPT(CURLOPT_SASL_AUTHZID, CURLOPTTYPE_STRINGPOINT, 289), + + /* allow RCPT TO command to fail for some recipients */ + CURLOPT(CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOPTTYPE_LONG, 290), + + /* the private SSL-certificate as a "blob" */ + CURLOPT(CURLOPT_SSLCERT_BLOB, CURLOPTTYPE_BLOB, 291), + CURLOPT(CURLOPT_SSLKEY_BLOB, CURLOPTTYPE_BLOB, 292), + CURLOPT(CURLOPT_PROXY_SSLCERT_BLOB, CURLOPTTYPE_BLOB, 293), + CURLOPT(CURLOPT_PROXY_SSLKEY_BLOB, CURLOPTTYPE_BLOB, 294), + CURLOPT(CURLOPT_ISSUERCERT_BLOB, CURLOPTTYPE_BLOB, 295), + + /* Issuer certificate for proxy */ + CURLOPT(CURLOPT_PROXY_ISSUERCERT, CURLOPTTYPE_STRINGPOINT, 296), + CURLOPT(CURLOPT_PROXY_ISSUERCERT_BLOB, CURLOPTTYPE_BLOB, 297), + + /* the EC curves requested by the TLS client (RFC 8422, 5.1); + * OpenSSL support via 'set_groups'/'set_curves': + * https://docs.openssl.org/master/man3/SSL_CTX_set1_curves/ + */ + CURLOPT(CURLOPT_SSL_EC_CURVES, CURLOPTTYPE_STRINGPOINT, 298), + + /* HSTS bitmask */ + CURLOPT(CURLOPT_HSTS_CTRL, CURLOPTTYPE_LONG, 299), + /* HSTS filename */ + CURLOPT(CURLOPT_HSTS, CURLOPTTYPE_STRINGPOINT, 300), + + /* HSTS read callback */ + CURLOPT(CURLOPT_HSTSREADFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 301), + CURLOPT(CURLOPT_HSTSREADDATA, CURLOPTTYPE_CBPOINT, 302), + + /* HSTS write callback */ + CURLOPT(CURLOPT_HSTSWRITEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 303), + CURLOPT(CURLOPT_HSTSWRITEDATA, CURLOPTTYPE_CBPOINT, 304), + + /* Parameters for V4 signature */ + CURLOPT(CURLOPT_AWS_SIGV4, CURLOPTTYPE_STRINGPOINT, 305), + + /* Same as CURLOPT_SSL_VERIFYPEER but for DoH (DNS-over-HTTPS) servers. */ + CURLOPT(CURLOPT_DOH_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 306), + + /* Same as CURLOPT_SSL_VERIFYHOST but for DoH (DNS-over-HTTPS) servers. */ + CURLOPT(CURLOPT_DOH_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 307), + + /* Same as CURLOPT_SSL_VERIFYSTATUS but for DoH (DNS-over-HTTPS) servers. */ + CURLOPT(CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 308), + + /* The CA certificates as "blob" used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_CAINFO_BLOB, CURLOPTTYPE_BLOB, 309), + + /* The CA certificates as "blob" used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_PROXY_CAINFO_BLOB, CURLOPTTYPE_BLOB, 310), + + /* used by scp/sftp to verify the host's public key */ + CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPTTYPE_STRINGPOINT, 311), + + /* Function that will be called immediately before the initial request + is made on a connection (after any protocol negotiation step). */ + CURLOPT(CURLOPT_PREREQFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 312), + + /* Data passed to the CURLOPT_PREREQFUNCTION callback */ + CURLOPT(CURLOPT_PREREQDATA, CURLOPTTYPE_CBPOINT, 313), + + /* maximum age (since creation) of a connection to consider it for reuse + * (in seconds) */ + CURLOPT(CURLOPT_MAXLIFETIME_CONN, CURLOPTTYPE_LONG, 314), + + /* Set MIME option flags. */ + CURLOPT(CURLOPT_MIME_OPTIONS, CURLOPTTYPE_LONG, 315), + + /* set the SSH host key callback, must point to a curl_sshkeycallback + function */ + CURLOPT(CURLOPT_SSH_HOSTKEYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 316), + + /* set the SSH host key callback custom pointer */ + CURLOPT(CURLOPT_SSH_HOSTKEYDATA, CURLOPTTYPE_CBPOINT, 317), + + /* specify which protocols that are allowed to be used for the transfer, + which thus helps the app which takes URLs from users or other external + inputs and want to restrict what protocol(s) to deal with. Defaults to + all built-in protocols. */ + CURLOPT(CURLOPT_PROTOCOLS_STR, CURLOPTTYPE_STRINGPOINT, 318), + + /* specify which protocols that libcurl is allowed to follow directs to */ + CURLOPT(CURLOPT_REDIR_PROTOCOLS_STR, CURLOPTTYPE_STRINGPOINT, 319), + + /* websockets options */ + CURLOPT(CURLOPT_WS_OPTIONS, CURLOPTTYPE_LONG, 320), + + /* CA cache timeout */ + CURLOPT(CURLOPT_CA_CACHE_TIMEOUT, CURLOPTTYPE_LONG, 321), + + /* Can leak things, gonna exit() soon */ + CURLOPT(CURLOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 322), + + /* set a specific client IP for HAProxy PROXY protocol header? */ + CURLOPT(CURLOPT_HAPROXY_CLIENT_IP, CURLOPTTYPE_STRINGPOINT, 323), + + /* millisecond version */ + CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, CURLOPTTYPE_LONG, 324), + + /* set ECH configuration */ + CURLOPT(CURLOPT_ECH, CURLOPTTYPE_STRINGPOINT, 325), + + /* maximum number of keepalive probes (Linux, *BSD, macOS, etc.) */ + CURLOPT(CURLOPT_TCP_KEEPCNT, CURLOPTTYPE_LONG, 326), + + CURLOPT_LASTENTRY /* the last unused */ +} CURLoption; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2011 */ + +/* This was added in version 7.19.1 */ +#define CURLOPT_POST301 CURLOPT_POSTREDIR + +/* These are scheduled to disappear by 2009 */ + +/* The following were added in 7.17.0 */ +#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_FTPAPPEND CURLOPT_APPEND +#define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY +#define CURLOPT_FTP_SSL CURLOPT_USE_SSL + +/* The following were added earlier */ + +#define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL + +/* */ +#define CURLOPT_FTP_RESPONSE_TIMEOUT CURLOPT_SERVER_RESPONSE_TIMEOUT + +/* Added in 8.2.0 */ +#define CURLOPT_MAIL_RCPT_ALLLOWFAILS CURLOPT_MAIL_RCPT_ALLOWFAILS + +#else +/* This is set if CURL_NO_OLDIES is defined at compile-time */ +#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ +#endif + + + /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ +#define CURL_IPRESOLVE_WHATEVER 0 /* default, uses addresses to all IP + versions that your system allows */ +#define CURL_IPRESOLVE_V4 1 /* uses only IPv4 addresses/connections */ +#define CURL_IPRESOLVE_V6 2 /* uses only IPv6 addresses/connections */ + + /* Convenient "aliases" */ +#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER + + /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ +enum { + CURL_HTTP_VERSION_NONE, /* setting this means we do not care, and that we + would like the library to choose the best + possible for us! */ + CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ + CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ + CURL_HTTP_VERSION_2_0, /* please use HTTP 2 in the request */ + CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */ + CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1 + Upgrade */ + CURL_HTTP_VERSION_3 = 30, /* Use HTTP/3, fallback to HTTP/2 or HTTP/1 if + needed. For HTTPS only. For HTTP, this option + makes libcurl return error. */ + CURL_HTTP_VERSION_3ONLY = 31, /* Use HTTP/3 without fallback. For HTTPS + only. For HTTP, this makes libcurl + return error. */ + + CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ +}; + +/* Convenience definition simple because the name of the version is HTTP/2 and + not 2.0. The 2_0 version of the enum name was set while the version was + still planned to be 2.0 and we stick to it for compatibility. */ +#define CURL_HTTP_VERSION_2 CURL_HTTP_VERSION_2_0 + +/* + * Public API enums for RTSP requests + */ +enum { + CURL_RTSPREQ_NONE, /* first in list */ + CURL_RTSPREQ_OPTIONS, + CURL_RTSPREQ_DESCRIBE, + CURL_RTSPREQ_ANNOUNCE, + CURL_RTSPREQ_SETUP, + CURL_RTSPREQ_PLAY, + CURL_RTSPREQ_PAUSE, + CURL_RTSPREQ_TEARDOWN, + CURL_RTSPREQ_GET_PARAMETER, + CURL_RTSPREQ_SET_PARAMETER, + CURL_RTSPREQ_RECORD, + CURL_RTSPREQ_RECEIVE, + CURL_RTSPREQ_LAST /* last in list */ +}; + + /* These enums are for use with the CURLOPT_NETRC option. */ +enum CURL_NETRC_OPTION { + CURL_NETRC_IGNORED, /* The .netrc will never be read. + * This is the default. */ + CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred + * to one in the .netrc. */ + CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored. + * Unless one is set programmatically, the .netrc + * will be queried. */ + CURL_NETRC_LAST +}; + +#define CURL_SSLVERSION_DEFAULT 0 +#define CURL_SSLVERSION_TLSv1 1 /* TLS 1.x */ +#define CURL_SSLVERSION_SSLv2 2 +#define CURL_SSLVERSION_SSLv3 3 +#define CURL_SSLVERSION_TLSv1_0 4 +#define CURL_SSLVERSION_TLSv1_1 5 +#define CURL_SSLVERSION_TLSv1_2 6 +#define CURL_SSLVERSION_TLSv1_3 7 + +#define CURL_SSLVERSION_LAST 8 /* never use, keep last */ + +#define CURL_SSLVERSION_MAX_NONE 0 +#define CURL_SSLVERSION_MAX_DEFAULT (CURL_SSLVERSION_TLSv1 << 16) +#define CURL_SSLVERSION_MAX_TLSv1_0 (CURL_SSLVERSION_TLSv1_0 << 16) +#define CURL_SSLVERSION_MAX_TLSv1_1 (CURL_SSLVERSION_TLSv1_1 << 16) +#define CURL_SSLVERSION_MAX_TLSv1_2 (CURL_SSLVERSION_TLSv1_2 << 16) +#define CURL_SSLVERSION_MAX_TLSv1_3 (CURL_SSLVERSION_TLSv1_3 << 16) + + /* never use, keep last */ +#define CURL_SSLVERSION_MAX_LAST (CURL_SSLVERSION_LAST << 16) + +enum CURL_TLSAUTH { + CURL_TLSAUTH_NONE, + CURL_TLSAUTH_SRP, + CURL_TLSAUTH_LAST /* never use, keep last */ +}; + +/* symbols to use with CURLOPT_POSTREDIR. + CURL_REDIR_POST_301, CURL_REDIR_POST_302 and CURL_REDIR_POST_303 + can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302 + | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */ + +#define CURL_REDIR_GET_ALL 0 +#define CURL_REDIR_POST_301 1 +#define CURL_REDIR_POST_302 2 +#define CURL_REDIR_POST_303 4 +#define CURL_REDIR_POST_ALL \ + (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303) + +typedef enum { + CURL_TIMECOND_NONE, + + CURL_TIMECOND_IFMODSINCE, + CURL_TIMECOND_IFUNMODSINCE, + CURL_TIMECOND_LASTMOD, + + CURL_TIMECOND_LAST +} curl_TimeCond; + +/* Special size_t value signaling a null-terminated string. */ +#define CURL_ZERO_TERMINATED ((size_t) -1) + +/* curl_strequal() and curl_strnequal() are subject for removal in a future + release */ +CURL_EXTERN int curl_strequal(const char *s1, const char *s2); +CURL_EXTERN int curl_strnequal(const char *s1, const char *s2, size_t n); + +/* Mime/form handling support. */ +typedef struct curl_mime curl_mime; /* Mime context. */ +typedef struct curl_mimepart curl_mimepart; /* Mime part context. */ + +/* CURLMIMEOPT_ defines are for the CURLOPT_MIME_OPTIONS option. */ +#define CURLMIMEOPT_FORMESCAPE (1<<0) /* Use backslash-escaping for forms. */ + +/* + * NAME curl_mime_init() + * + * DESCRIPTION + * + * Create a mime context and return its handle. The easy parameter is the + * target handle. + */ +CURL_EXTERN curl_mime *curl_mime_init(CURL *easy); + +/* + * NAME curl_mime_free() + * + * DESCRIPTION + * + * release a mime handle and its substructures. + */ +CURL_EXTERN void curl_mime_free(curl_mime *mime); + +/* + * NAME curl_mime_addpart() + * + * DESCRIPTION + * + * Append a new empty part to the given mime context and return a handle to + * the created part. + */ +CURL_EXTERN curl_mimepart *curl_mime_addpart(curl_mime *mime); + +/* + * NAME curl_mime_name() + * + * DESCRIPTION + * + * Set mime/form part name. + */ +CURL_EXTERN CURLcode curl_mime_name(curl_mimepart *part, const char *name); + +/* + * NAME curl_mime_filename() + * + * DESCRIPTION + * + * Set mime part remote filename. + */ +CURL_EXTERN CURLcode curl_mime_filename(curl_mimepart *part, + const char *filename); + +/* + * NAME curl_mime_type() + * + * DESCRIPTION + * + * Set mime part type. + */ +CURL_EXTERN CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype); + +/* + * NAME curl_mime_encoder() + * + * DESCRIPTION + * + * Set mime data transfer encoder. + */ +CURL_EXTERN CURLcode curl_mime_encoder(curl_mimepart *part, + const char *encoding); + +/* + * NAME curl_mime_data() + * + * DESCRIPTION + * + * Set mime part data source from memory data, + */ +CURL_EXTERN CURLcode curl_mime_data(curl_mimepart *part, + const char *data, size_t datasize); + +/* + * NAME curl_mime_filedata() + * + * DESCRIPTION + * + * Set mime part data source from named file. + */ +CURL_EXTERN CURLcode curl_mime_filedata(curl_mimepart *part, + const char *filename); + +/* + * NAME curl_mime_data_cb() + * + * DESCRIPTION + * + * Set mime part data source from callback function. + */ +CURL_EXTERN CURLcode curl_mime_data_cb(curl_mimepart *part, + curl_off_t datasize, + curl_read_callback readfunc, + curl_seek_callback seekfunc, + curl_free_callback freefunc, + void *arg); + +/* + * NAME curl_mime_subparts() + * + * DESCRIPTION + * + * Set mime part data source from subparts. + */ +CURL_EXTERN CURLcode curl_mime_subparts(curl_mimepart *part, + curl_mime *subparts); +/* + * NAME curl_mime_headers() + * + * DESCRIPTION + * + * Set mime part headers. + */ +CURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part, + struct curl_slist *headers, + int take_ownership); + +typedef enum { + /********* the first one is unused ************/ + CURLFORM_NOTHING CURL_DEPRECATED(7.56.0, ""), + CURLFORM_COPYNAME CURL_DEPRECATED(7.56.0, "Use curl_mime_name()"), + CURLFORM_PTRNAME CURL_DEPRECATED(7.56.0, "Use curl_mime_name()"), + CURLFORM_NAMELENGTH CURL_DEPRECATED(7.56.0, ""), + CURLFORM_COPYCONTENTS CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), + CURLFORM_PTRCONTENTS CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), + CURLFORM_CONTENTSLENGTH CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), + CURLFORM_FILECONTENT CURL_DEPRECATED(7.56.0, "Use curl_mime_data_cb()"), + CURLFORM_ARRAY CURL_DEPRECATED(7.56.0, ""), + CURLFORM_OBSOLETE, + CURLFORM_FILE CURL_DEPRECATED(7.56.0, "Use curl_mime_filedata()"), + + CURLFORM_BUFFER CURL_DEPRECATED(7.56.0, "Use curl_mime_filename()"), + CURLFORM_BUFFERPTR CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), + CURLFORM_BUFFERLENGTH CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), + + CURLFORM_CONTENTTYPE CURL_DEPRECATED(7.56.0, "Use curl_mime_type()"), + CURLFORM_CONTENTHEADER CURL_DEPRECATED(7.56.0, "Use curl_mime_headers()"), + CURLFORM_FILENAME CURL_DEPRECATED(7.56.0, "Use curl_mime_filename()"), + CURLFORM_END, + CURLFORM_OBSOLETE2, + + CURLFORM_STREAM CURL_DEPRECATED(7.56.0, "Use curl_mime_data_cb()"), + CURLFORM_CONTENTLEN /* added in 7.46.0, provide a curl_off_t length */ + CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), + + CURLFORM_LASTENTRY /* the last unused */ +} CURLformoption; + +/* structure to be used as parameter for CURLFORM_ARRAY */ +struct curl_forms { + CURLformoption option; + const char *value; +}; + +/* use this for multipart formpost building */ +/* Returns code for curl_formadd() + * + * Returns: + * CURL_FORMADD_OK on success + * CURL_FORMADD_MEMORY if the FormInfo allocation fails + * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form + * CURL_FORMADD_NULL if a null pointer was given for a char + * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed + * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used + * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) + * CURL_FORMADD_MEMORY if a curl_httppost struct cannot be allocated + * CURL_FORMADD_MEMORY if some allocation for string copying failed. + * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + ***************************************************************************/ +typedef enum { + CURL_FORMADD_OK CURL_DEPRECATED(7.56.0, ""), /* 1st, no error */ + + CURL_FORMADD_MEMORY CURL_DEPRECATED(7.56.0, ""), + CURL_FORMADD_OPTION_TWICE CURL_DEPRECATED(7.56.0, ""), + CURL_FORMADD_NULL CURL_DEPRECATED(7.56.0, ""), + CURL_FORMADD_UNKNOWN_OPTION CURL_DEPRECATED(7.56.0, ""), + CURL_FORMADD_INCOMPLETE CURL_DEPRECATED(7.56.0, ""), + CURL_FORMADD_ILLEGAL_ARRAY CURL_DEPRECATED(7.56.0, ""), + /* libcurl was built with form api disabled */ + CURL_FORMADD_DISABLED CURL_DEPRECATED(7.56.0, ""), + + CURL_FORMADD_LAST /* last */ +} CURLFORMcode; + +/* + * NAME curl_formadd() + * + * DESCRIPTION + * + * Pretty advanced function for building multi-part formposts. Each invoke + * adds one part that together construct a full post. Then use + * CURLOPT_HTTPPOST to send it off to libcurl. + */ +CURL_EXTERN CURLFORMcode CURL_DEPRECATED(7.56.0, "Use curl_mime_init()") +curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...); + +/* + * callback function for curl_formget() + * The void *arg pointer will be the one passed as second argument to + * curl_formget(). + * The character buffer passed to it must not be freed. + * Should return the buffer length passed to it as the argument "len" on + * success. + */ +typedef size_t (*curl_formget_callback)(void *arg, const char *buf, + size_t len); + +/* + * NAME curl_formget() + * + * DESCRIPTION + * + * Serialize a curl_httppost struct built with curl_formadd(). + * Accepts a void pointer as second argument which will be passed to + * the curl_formget_callback function. + * Returns 0 on success. + */ +CURL_EXTERN int CURL_DEPRECATED(7.56.0, "") +curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append); +/* + * NAME curl_formfree() + * + * DESCRIPTION + * + * Free a multipart formpost previously built with curl_formadd(). + */ +CURL_EXTERN void CURL_DEPRECATED(7.56.0, "Use curl_mime_free()") +curl_formfree(struct curl_httppost *form); + +/* + * NAME curl_getenv() + * + * DESCRIPTION + * + * Returns a malloc()'ed string that MUST be curl_free()ed after usage is + * complete. DEPRECATED - see lib/README.curlx + */ +CURL_EXTERN char *curl_getenv(const char *variable); + +/* + * NAME curl_version() + * + * DESCRIPTION + * + * Returns a static ascii string of the libcurl version. + */ +CURL_EXTERN char *curl_version(void); + +/* + * NAME curl_easy_escape() + * + * DESCRIPTION + * + * Escapes URL strings (converts all letters consider illegal in URLs to their + * %XX versions). This function returns a new allocated string or NULL if an + * error occurred. + */ +CURL_EXTERN char *curl_easy_escape(CURL *handle, + const char *string, + int length); + +/* the previous version: */ +CURL_EXTERN char *curl_escape(const char *string, + int length); + + +/* + * NAME curl_easy_unescape() + * + * DESCRIPTION + * + * Unescapes URL encoding in strings (converts all %XX codes to their 8bit + * versions). This function returns a new allocated string or NULL if an error + * occurred. + * Conversion Note: On non-ASCII platforms the ASCII %XX codes are + * converted into the host encoding. + */ +CURL_EXTERN char *curl_easy_unescape(CURL *handle, + const char *string, + int length, + int *outlength); + +/* the previous version */ +CURL_EXTERN char *curl_unescape(const char *string, + int length); + +/* + * NAME curl_free() + * + * DESCRIPTION + * + * Provided for de-allocation in the same translation unit that did the + * allocation. Added in libcurl 7.10 + */ +CURL_EXTERN void curl_free(void *p); + +/* + * NAME curl_global_init() + * + * DESCRIPTION + * + * curl_global_init() should be invoked exactly once for each application that + * uses libcurl and before any call of other libcurl functions. + + * This function is thread-safe if CURL_VERSION_THREADSAFE is set in the + * curl_version_info_data.features flag (fetch by curl_version_info()). + + */ +CURL_EXTERN CURLcode curl_global_init(long flags); + +/* + * NAME curl_global_init_mem() + * + * DESCRIPTION + * + * curl_global_init() or curl_global_init_mem() should be invoked exactly once + * for each application that uses libcurl. This function can be used to + * initialize libcurl and set user defined memory management callback + * functions. Users can implement memory management routines to check for + * memory leaks, check for mis-use of the curl library etc. User registered + * callback routines will be invoked by this library instead of the system + * memory management routines like malloc, free etc. + */ +CURL_EXTERN CURLcode curl_global_init_mem(long flags, + curl_malloc_callback m, + curl_free_callback f, + curl_realloc_callback r, + curl_strdup_callback s, + curl_calloc_callback c); + +/* + * NAME curl_global_cleanup() + * + * DESCRIPTION + * + * curl_global_cleanup() should be invoked exactly once for each application + * that uses libcurl + */ +CURL_EXTERN void curl_global_cleanup(void); + +/* + * NAME curl_global_trace() + * + * DESCRIPTION + * + * curl_global_trace() can be invoked at application start to + * configure which components in curl should participate in tracing. + + * This function is thread-safe if CURL_VERSION_THREADSAFE is set in the + * curl_version_info_data.features flag (fetch by curl_version_info()). + + */ +CURL_EXTERN CURLcode curl_global_trace(const char *config); + +/* linked-list structure for the CURLOPT_QUOTE option (and other) */ +struct curl_slist { + char *data; + struct curl_slist *next; +}; + +/* + * NAME curl_global_sslset() + * + * DESCRIPTION + * + * When built with multiple SSL backends, curl_global_sslset() allows to + * choose one. This function can only be called once, and it must be called + * *before* curl_global_init(). + * + * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The + * backend can also be specified via the name parameter (passing -1 as id). + * If both id and name are specified, the name will be ignored. If neither id + * nor name are specified, the function will fail with + * CURLSSLSET_UNKNOWN_BACKEND and set the "avail" pointer to the + * NULL-terminated list of available backends. + * + * Upon success, the function returns CURLSSLSET_OK. + * + * If the specified SSL backend is not available, the function returns + * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a NULL-terminated + * list of available SSL backends. + * + * The SSL backend can be set only once. If it has already been set, a + * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE. + */ + +struct curl_ssl_backend { + curl_sslbackend id; + const char *name; +}; +typedef struct curl_ssl_backend curl_ssl_backend; + +typedef enum { + CURLSSLSET_OK = 0, + CURLSSLSET_UNKNOWN_BACKEND, + CURLSSLSET_TOO_LATE, + CURLSSLSET_NO_BACKENDS /* libcurl was built without any SSL support */ +} CURLsslset; + +CURL_EXTERN CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, + const curl_ssl_backend ***avail); + +/* + * NAME curl_slist_append() + * + * DESCRIPTION + * + * Appends a string to a linked list. If no list exists, it will be created + * first. Returns the new list, after appending. + */ +CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *list, + const char *data); + +/* + * NAME curl_slist_free_all() + * + * DESCRIPTION + * + * free a previously built curl_slist. + */ +CURL_EXTERN void curl_slist_free_all(struct curl_slist *list); + +/* + * NAME curl_getdate() + * + * DESCRIPTION + * + * Returns the time, in seconds since 1 Jan 1970 of the time string given in + * the first argument. The time argument in the second parameter is unused + * and should be set to NULL. + */ +CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); + +/* info about the certificate chain, for SSL backends that support it. Asked + for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +struct curl_certinfo { + int num_of_certs; /* number of certificates with information */ + struct curl_slist **certinfo; /* for each index in this array, there is a + linked list with textual information for a + certificate in the format "name:content". + eg "Subject:foo", "Issuer:bar", etc. */ +}; + +/* Information about the SSL library used and the respective internal SSL + handle, which can be used to obtain further information regarding the + connection. Asked for with CURLINFO_TLS_SSL_PTR or CURLINFO_TLS_SESSION. */ +struct curl_tlssessioninfo { + curl_sslbackend backend; + void *internals; +}; + +#define CURLINFO_STRING 0x100000 +#define CURLINFO_LONG 0x200000 +#define CURLINFO_DOUBLE 0x300000 +#define CURLINFO_SLIST 0x400000 +#define CURLINFO_PTR 0x400000 /* same as SLIST */ +#define CURLINFO_SOCKET 0x500000 +#define CURLINFO_OFF_T 0x600000 +#define CURLINFO_MASK 0x0fffff +#define CURLINFO_TYPEMASK 0xf00000 + +typedef enum { + CURLINFO_NONE, /* first, never use this */ + CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3, + CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4, + CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, + CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, + CURLINFO_SIZE_UPLOAD CURL_DEPRECATED(7.55.0, "Use CURLINFO_SIZE_UPLOAD_T") + = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_UPLOAD_T = CURLINFO_OFF_T + 7, + CURLINFO_SIZE_DOWNLOAD + CURL_DEPRECATED(7.55.0, "Use CURLINFO_SIZE_DOWNLOAD_T") + = CURLINFO_DOUBLE + 8, + CURLINFO_SIZE_DOWNLOAD_T = CURLINFO_OFF_T + 8, + CURLINFO_SPEED_DOWNLOAD + CURL_DEPRECATED(7.55.0, "Use CURLINFO_SPEED_DOWNLOAD_T") + = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_DOWNLOAD_T = CURLINFO_OFF_T + 9, + CURLINFO_SPEED_UPLOAD + CURL_DEPRECATED(7.55.0, "Use CURLINFO_SPEED_UPLOAD_T") + = CURLINFO_DOUBLE + 10, + CURLINFO_SPEED_UPLOAD_T = CURLINFO_OFF_T + 10, + CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, + CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, + CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, + CURLINFO_FILETIME = CURLINFO_LONG + 14, + CURLINFO_FILETIME_T = CURLINFO_OFF_T + 14, + CURLINFO_CONTENT_LENGTH_DOWNLOAD + CURL_DEPRECATED(7.55.0, + "Use CURLINFO_CONTENT_LENGTH_DOWNLOAD_T") + = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LENGTH_DOWNLOAD_T = CURLINFO_OFF_T + 15, + CURLINFO_CONTENT_LENGTH_UPLOAD + CURL_DEPRECATED(7.55.0, + "Use CURLINFO_CONTENT_LENGTH_UPLOAD_T") + = CURLINFO_DOUBLE + 16, + CURLINFO_CONTENT_LENGTH_UPLOAD_T = CURLINFO_OFF_T + 16, + CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, + CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, + CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19, + CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20, + CURLINFO_PRIVATE = CURLINFO_STRING + 21, + CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, + CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, + CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, + CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, + CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, + CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, + CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + CURLINFO_LASTSOCKET CURL_DEPRECATED(7.45.0, "Use CURLINFO_ACTIVESOCKET") + = CURLINFO_LONG + 29, + CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30, + CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, + CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, + CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, + CURLINFO_CERTINFO = CURLINFO_PTR + 34, + CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35, + CURLINFO_RTSP_SESSION_ID = CURLINFO_STRING + 36, + CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG + 37, + CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG + 38, + CURLINFO_RTSP_CSEQ_RECV = CURLINFO_LONG + 39, + CURLINFO_PRIMARY_PORT = CURLINFO_LONG + 40, + CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, + CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, + CURLINFO_TLS_SESSION CURL_DEPRECATED(7.48.0, "Use CURLINFO_TLS_SSL_PTR") + = CURLINFO_PTR + 43, + CURLINFO_ACTIVESOCKET = CURLINFO_SOCKET + 44, + CURLINFO_TLS_SSL_PTR = CURLINFO_PTR + 45, + CURLINFO_HTTP_VERSION = CURLINFO_LONG + 46, + CURLINFO_PROXY_SSL_VERIFYRESULT = CURLINFO_LONG + 47, + CURLINFO_PROTOCOL CURL_DEPRECATED(7.85.0, "Use CURLINFO_SCHEME") + = CURLINFO_LONG + 48, + CURLINFO_SCHEME = CURLINFO_STRING + 49, + CURLINFO_TOTAL_TIME_T = CURLINFO_OFF_T + 50, + CURLINFO_NAMELOOKUP_TIME_T = CURLINFO_OFF_T + 51, + CURLINFO_CONNECT_TIME_T = CURLINFO_OFF_T + 52, + CURLINFO_PRETRANSFER_TIME_T = CURLINFO_OFF_T + 53, + CURLINFO_STARTTRANSFER_TIME_T = CURLINFO_OFF_T + 54, + CURLINFO_REDIRECT_TIME_T = CURLINFO_OFF_T + 55, + CURLINFO_APPCONNECT_TIME_T = CURLINFO_OFF_T + 56, + CURLINFO_RETRY_AFTER = CURLINFO_OFF_T + 57, + CURLINFO_EFFECTIVE_METHOD = CURLINFO_STRING + 58, + CURLINFO_PROXY_ERROR = CURLINFO_LONG + 59, + CURLINFO_REFERER = CURLINFO_STRING + 60, + CURLINFO_CAINFO = CURLINFO_STRING + 61, + CURLINFO_CAPATH = CURLINFO_STRING + 62, + CURLINFO_XFER_ID = CURLINFO_OFF_T + 63, + CURLINFO_CONN_ID = CURLINFO_OFF_T + 64, + CURLINFO_QUEUE_TIME_T = CURLINFO_OFF_T + 65, + CURLINFO_USED_PROXY = CURLINFO_LONG + 66, + CURLINFO_LASTONE = 66 +} CURLINFO; + +/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as + CURLINFO_HTTP_CODE */ +#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE + +typedef enum { + CURLCLOSEPOLICY_NONE, /* first, never use this */ + + CURLCLOSEPOLICY_OLDEST, + CURLCLOSEPOLICY_LEAST_RECENTLY_USED, + CURLCLOSEPOLICY_LEAST_TRAFFIC, + CURLCLOSEPOLICY_SLOWEST, + CURLCLOSEPOLICY_CALLBACK, + + CURLCLOSEPOLICY_LAST /* last, never use this */ +} curl_closepolicy; + +#define CURL_GLOBAL_SSL (1<<0) /* no purpose since 7.57.0 */ +#define CURL_GLOBAL_WIN32 (1<<1) +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_NOTHING 0 +#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL +#define CURL_GLOBAL_ACK_EINTR (1<<2) + + +/***************************************************************************** + * Setup defines, protos etc for the sharing stuff. + */ + +/* Different data locks for a single share */ +typedef enum { + CURL_LOCK_DATA_NONE = 0, + /* CURL_LOCK_DATA_SHARE is used internally to say that + * the locking is just made to change the internal state of the share + * itself. + */ + CURL_LOCK_DATA_SHARE, + CURL_LOCK_DATA_COOKIE, + CURL_LOCK_DATA_DNS, + CURL_LOCK_DATA_SSL_SESSION, + CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_PSL, + CURL_LOCK_DATA_HSTS, + CURL_LOCK_DATA_LAST +} curl_lock_data; + +/* Different lock access types */ +typedef enum { + CURL_LOCK_ACCESS_NONE = 0, /* unspecified action */ + CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */ + CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */ + CURL_LOCK_ACCESS_LAST /* never use */ +} curl_lock_access; + +typedef void (*curl_lock_function)(CURL *handle, + curl_lock_data data, + curl_lock_access locktype, + void *userptr); +typedef void (*curl_unlock_function)(CURL *handle, + curl_lock_data data, + void *userptr); + + +typedef enum { + CURLSHE_OK, /* all is fine */ + CURLSHE_BAD_OPTION, /* 1 */ + CURLSHE_IN_USE, /* 2 */ + CURLSHE_INVALID, /* 3 */ + CURLSHE_NOMEM, /* 4 out of memory */ + CURLSHE_NOT_BUILT_IN, /* 5 feature not present in lib */ + CURLSHE_LAST /* never use */ +} CURLSHcode; + +typedef enum { + CURLSHOPT_NONE, /* do not use */ + CURLSHOPT_SHARE, /* specify a data type to share */ + CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */ + CURLSHOPT_LOCKFUNC, /* pass in a 'curl_lock_function' pointer */ + CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */ + CURLSHOPT_USERDATA, /* pass in a user data pointer used in the lock/unlock + callback functions */ + CURLSHOPT_LAST /* never use */ +} CURLSHoption; + +CURL_EXTERN CURLSH *curl_share_init(void); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *share, CURLSHoption option, + ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *share); + +/**************************************************************************** + * Structures for querying information about the curl library at runtime. + */ + +typedef enum { + CURLVERSION_FIRST, /* 7.10 */ + CURLVERSION_SECOND, /* 7.11.1 */ + CURLVERSION_THIRD, /* 7.12.0 */ + CURLVERSION_FOURTH, /* 7.16.1 */ + CURLVERSION_FIFTH, /* 7.57.0 */ + CURLVERSION_SIXTH, /* 7.66.0 */ + CURLVERSION_SEVENTH, /* 7.70.0 */ + CURLVERSION_EIGHTH, /* 7.72.0 */ + CURLVERSION_NINTH, /* 7.75.0 */ + CURLVERSION_TENTH, /* 7.77.0 */ + CURLVERSION_ELEVENTH, /* 7.87.0 */ + CURLVERSION_TWELFTH, /* 8.8.0 */ + CURLVERSION_LAST /* never actually use this */ +} CURLversion; + +/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by + basically all programs ever that want to get version information. It is + meant to be a built-in version number for what kind of struct the caller + expects. If the struct ever changes, we redefine the NOW to another enum + from above. */ +#define CURLVERSION_NOW CURLVERSION_TWELFTH + +struct curl_version_info_data { + CURLversion age; /* age of the returned struct */ + const char *version; /* LIBCURL_VERSION */ + unsigned int version_num; /* LIBCURL_VERSION_NUM */ + const char *host; /* OS/host/cpu/machine when configured */ + int features; /* bitmask, see defines below */ + const char *ssl_version; /* human readable string */ + long ssl_version_num; /* not used anymore, always 0 */ + const char *libz_version; /* human readable string */ + /* protocols is terminated by an entry with a NULL protoname */ + const char * const *protocols; + + /* The fields below this were added in CURLVERSION_SECOND */ + const char *ares; + int ares_num; + + /* This field was added in CURLVERSION_THIRD */ + const char *libidn; + + /* These field were added in CURLVERSION_FOURTH */ + + /* Same as '_libiconv_version' if built with HAVE_ICONV */ + int iconv_ver_num; + + const char *libssh_version; /* human readable string */ + + /* These fields were added in CURLVERSION_FIFTH */ + unsigned int brotli_ver_num; /* Numeric Brotli version + (MAJOR << 24) | (MINOR << 12) | PATCH */ + const char *brotli_version; /* human readable string. */ + + /* These fields were added in CURLVERSION_SIXTH */ + unsigned int nghttp2_ver_num; /* Numeric nghttp2 version + (MAJOR << 16) | (MINOR << 8) | PATCH */ + const char *nghttp2_version; /* human readable string. */ + const char *quic_version; /* human readable quic (+ HTTP/3) library + + version or NULL */ + + /* These fields were added in CURLVERSION_SEVENTH */ + const char *cainfo; /* the built-in default CURLOPT_CAINFO, might + be NULL */ + const char *capath; /* the built-in default CURLOPT_CAPATH, might + be NULL */ + + /* These fields were added in CURLVERSION_EIGHTH */ + unsigned int zstd_ver_num; /* Numeric Zstd version + (MAJOR << 24) | (MINOR << 12) | PATCH */ + const char *zstd_version; /* human readable string. */ + + /* These fields were added in CURLVERSION_NINTH */ + const char *hyper_version; /* human readable string. */ + + /* These fields were added in CURLVERSION_TENTH */ + const char *gsasl_version; /* human readable string. */ + + /* These fields were added in CURLVERSION_ELEVENTH */ + /* feature_names is terminated by an entry with a NULL feature name */ + const char * const *feature_names; + + /* These fields were added in CURLVERSION_TWELFTH */ + const char *rtmp_version; /* human readable string. */ +}; +typedef struct curl_version_info_data curl_version_info_data; + +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* Kerberos V4 auth is supported + (deprecated) */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth is supported + (deprecated) */ +#define CURL_VERSION_DEBUG (1<<6) /* Built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* Asynchronous DNS resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth is supported */ +#define CURL_VERSION_LARGEFILE (1<<9) /* Supports files larger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* Internationized Domain Names are + supported */ +#define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ +#define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ +#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper + is supported */ +#define CURL_VERSION_HTTP2 (1<<16) /* HTTP2 support built-in */ +#define CURL_VERSION_GSSAPI (1<<17) /* Built against a GSS-API library */ +#define CURL_VERSION_KERBEROS5 (1<<18) /* Kerberos V5 auth is supported */ +#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */ +#define CURL_VERSION_PSL (1<<20) /* Mozilla's Public Suffix List, used + for cookie domain verification */ +#define CURL_VERSION_HTTPS_PROXY (1<<21) /* HTTPS-proxy support built-in */ +#define CURL_VERSION_MULTI_SSL (1<<22) /* Multiple SSL backends available */ +#define CURL_VERSION_BROTLI (1<<23) /* Brotli features are present. */ +#define CURL_VERSION_ALTSVC (1<<24) /* Alt-Svc handling built-in */ +#define CURL_VERSION_HTTP3 (1<<25) /* HTTP3 support built-in */ +#define CURL_VERSION_ZSTD (1<<26) /* zstd features are present */ +#define CURL_VERSION_UNICODE (1<<27) /* Unicode support on Windows */ +#define CURL_VERSION_HSTS (1<<28) /* HSTS is supported */ +#define CURL_VERSION_GSASL (1<<29) /* libgsasl is supported */ +#define CURL_VERSION_THREADSAFE (1<<30) /* libcurl API is thread-safe */ + +/* + * NAME curl_version_info() + * + * DESCRIPTION + * + * This function returns a pointer to a static copy of the version info + * struct. See above. + */ +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); + +/* + * NAME curl_easy_strerror() + * + * DESCRIPTION + * + * The curl_easy_strerror function may be used to turn a CURLcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_easy_strerror(CURLcode); + +/* + * NAME curl_share_strerror() + * + * DESCRIPTION + * + * The curl_share_strerror function may be used to turn a CURLSHcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_share_strerror(CURLSHcode); + +/* + * NAME curl_easy_pause() + * + * DESCRIPTION + * + * The curl_easy_pause function pauses or unpauses transfers. Select the new + * state by setting the bitmask, use the convenience defines below. + * + */ +CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); + +#define CURLPAUSE_RECV (1<<0) +#define CURLPAUSE_RECV_CONT (0) + +#define CURLPAUSE_SEND (1<<2) +#define CURLPAUSE_SEND_CONT (0) + +#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND) +#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +/* unfortunately, the easy.h and multi.h include files need options and info + stuff before they can be included! */ +#include "easy.h" /* nothing in curl is fun without the easy stuff */ +#include "multi.h" +#include "urlapi.h" +#include "options.h" +#include "header.h" +#include "websockets.h" +#include "mprintf.h" + +/* the typechecker does not work in C++ (yet) */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ + ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \ + !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK) +#include "typecheck-gcc.h" +#else +#if defined(__STDC__) && (__STDC__ >= 1) +/* This preprocessor magic that replaces a call with the exact same call is + only done to make sure application authors pass exactly three arguments + to these functions. */ +#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param) +#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg) +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) +#endif /* __STDC__ >= 1 */ +#endif /* gcc >= 4.3 && !__cplusplus && !CURL_DISABLE_TYPECHECK */ + +#endif /* CURLINC_CURL_H */ diff --git a/third_party/curl/include/curl/curlver.h b/third_party/curl/include/curl/curlver.h new file mode 100644 index 0000000000..dbee9bd4dc --- /dev/null +++ b/third_party/curl/include/curl/curlver.h @@ -0,0 +1,79 @@ +#ifndef CURLINC_CURLVER_H +#define CURLINC_CURLVER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* This header file contains nothing but libcurl version info, generated by + a script at release-time. This was made its own header file in 7.11.2 */ + +/* This is the global package copyright */ +#define LIBCURL_COPYRIGHT "Daniel Stenberg, ." + +/* This is the version number of the libcurl package from which this header + file origins: */ +#define LIBCURL_VERSION "8.9.1" + +/* The numeric version number is also available "in parts" by using these + defines: */ +#define LIBCURL_VERSION_MAJOR 8 +#define LIBCURL_VERSION_MINOR 9 +#define LIBCURL_VERSION_PATCH 1 + +/* This is the numeric version of the libcurl version number, meant for easier + parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will + always follow this syntax: + + 0xXXYYZZ + + Where XX, YY and ZZ are the main version, release and patch numbers in + hexadecimal (using 8 bits each). All three numbers are always represented + using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + appears as "0x090b07". + + This 6-digit (24 bits) hexadecimal number does not show pre-release number, + and it is always a greater number in a more recent release. It makes + comparisons with greater than and less than work. + + Note: This define is the full hex number and _does not_ use the + CURL_VERSION_BITS() macro since curl's own configure script greps for it + and needs it to contain the full number. +*/ +#define LIBCURL_VERSION_NUM 0x080901 + +/* + * This is the date and time when the full source package was created. The + * timestamp is not stored in git, as the timestamp is properly set in the + * tarballs by the maketgz script. + * + * The format of the date follows this template: + * + * "2007-11-23" + */ +#define LIBCURL_TIMESTAMP "2024-07-31" + +#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z)) +#define CURL_AT_LEAST_VERSION(x,y,z) \ + (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) + +#endif /* CURLINC_CURLVER_H */ diff --git a/third_party/curl/include/curl/easy.h b/third_party/curl/include/curl/easy.h new file mode 100644 index 0000000000..71b8dd4674 --- /dev/null +++ b/third_party/curl/include/curl/easy.h @@ -0,0 +1,125 @@ +#ifndef CURLINC_EASY_H +#define CURLINC_EASY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/* Flag bits in the curl_blob struct: */ +#define CURL_BLOB_COPY 1 /* tell libcurl to copy the data */ +#define CURL_BLOB_NOCOPY 0 /* tell libcurl to NOT copy the data */ + +struct curl_blob { + void *data; + size_t len; + unsigned int flags; /* bit 0 is defined, the rest are reserved and should be + left zeroes */ +}; + +CURL_EXTERN CURL *curl_easy_init(void); +CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); +CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); +CURL_EXTERN void curl_easy_cleanup(CURL *curl); + +/* + * NAME curl_easy_getinfo() + * + * DESCRIPTION + * + * Request internal information from the curl session with this function. + * The third argument MUST be pointing to the specific type of the used option + * which is documented in each manpage of the option. The data pointed to + * will be filled in accordingly and can be relied upon only if the function + * returns CURLE_OK. This function is intended to get used *AFTER* a performed + * transfer, all results from this function are undefined until the transfer + * is completed. + */ +CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); + + +/* + * NAME curl_easy_duphandle() + * + * DESCRIPTION + * + * Creates a new curl session handle with the same options set for the handle + * passed in. Duplicating a handle could only be a matter of cloning data and + * options, internal state info and things like persistent connections cannot + * be transferred. It is useful in multithreaded applications when you can run + * curl_easy_duphandle() for each new thread to avoid a series of identical + * curl_easy_setopt() invokes in every thread. + */ +CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl); + +/* + * NAME curl_easy_reset() + * + * DESCRIPTION + * + * Re-initializes a CURL handle to the default values. This puts back the + * handle to the same state as it was in when it was just created. + * + * It does keep: live connections, the Session ID cache, the DNS cache and the + * cookies. + */ +CURL_EXTERN void curl_easy_reset(CURL *curl); + +/* + * NAME curl_easy_recv() + * + * DESCRIPTION + * + * Receives data from the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, + size_t *n); + +/* + * NAME curl_easy_send() + * + * DESCRIPTION + * + * Sends data over the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, + size_t buflen, size_t *n); + + +/* + * NAME curl_easy_upkeep() + * + * DESCRIPTION + * + * Performs connection upkeep for the given session handle. + */ +CURL_EXTERN CURLcode curl_easy_upkeep(CURL *curl); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/third_party/curl/include/curl/header.h b/third_party/curl/include/curl/header.h new file mode 100644 index 0000000000..8df11e1e42 --- /dev/null +++ b/third_party/curl/include/curl/header.h @@ -0,0 +1,74 @@ +#ifndef CURLINC_HEADER_H +#define CURLINC_HEADER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +struct curl_header { + char *name; /* this might not use the same case */ + char *value; + size_t amount; /* number of headers using this name */ + size_t index; /* ... of this instance, 0 or higher */ + unsigned int origin; /* see bits below */ + void *anchor; /* handle privately used by libcurl */ +}; + +/* 'origin' bits */ +#define CURLH_HEADER (1<<0) /* plain server header */ +#define CURLH_TRAILER (1<<1) /* trailers */ +#define CURLH_CONNECT (1<<2) /* CONNECT headers */ +#define CURLH_1XX (1<<3) /* 1xx headers */ +#define CURLH_PSEUDO (1<<4) /* pseudo headers */ + +typedef enum { + CURLHE_OK, + CURLHE_BADINDEX, /* header exists but not with this index */ + CURLHE_MISSING, /* no such header exists */ + CURLHE_NOHEADERS, /* no headers at all exist (yet) */ + CURLHE_NOREQUEST, /* no request with this number was used */ + CURLHE_OUT_OF_MEMORY, /* out of memory while processing */ + CURLHE_BAD_ARGUMENT, /* a function argument was not okay */ + CURLHE_NOT_BUILT_IN /* if API was disabled in the build */ +} CURLHcode; + +CURL_EXTERN CURLHcode curl_easy_header(CURL *easy, + const char *name, + size_t index, + unsigned int origin, + int request, + struct curl_header **hout); + +CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *easy, + unsigned int origin, + int request, + struct curl_header *prev); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* CURLINC_HEADER_H */ diff --git a/third_party/curl/include/curl/mprintf.h b/third_party/curl/include/curl/mprintf.h new file mode 100644 index 0000000000..e6bc5bca47 --- /dev/null +++ b/third_party/curl/include/curl/mprintf.h @@ -0,0 +1,79 @@ +#ifndef CURLINC_MPRINTF_H +#define CURLINC_MPRINTF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include +#include /* needed for FILE */ +#include "curl.h" /* for CURL_EXTERN */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(__GNUC__) || defined(__clang__) || \ + defined(__IAR_SYSTEMS_ICC__)) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(CURL_NO_FMT_CHECKS) +#if defined(__MINGW32__) && !defined(__clang__) +#define CURL_TEMP_PRINTF(fmt, arg) \ + __attribute__((format(gnu_printf, fmt, arg))) +#else +#define CURL_TEMP_PRINTF(fmt, arg) \ + __attribute__((format(printf, fmt, arg))) +#endif +#else +#define CURL_TEMP_PRINTF(fmt, arg) +#endif + +CURL_EXTERN int curl_mprintf(const char *format, ...) + CURL_TEMP_PRINTF(1, 2); +CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...) + CURL_TEMP_PRINTF(2, 3); +CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...) + CURL_TEMP_PRINTF(2, 3); +CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, + const char *format, ...) + CURL_TEMP_PRINTF(3, 4); +CURL_EXTERN int curl_mvprintf(const char *format, va_list args) + CURL_TEMP_PRINTF(1, 0); +CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args) + CURL_TEMP_PRINTF(2, 0); +CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args) + CURL_TEMP_PRINTF(2, 0); +CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, + const char *format, va_list args) + CURL_TEMP_PRINTF(3, 0); +CURL_EXTERN char *curl_maprintf(const char *format, ...) + CURL_TEMP_PRINTF(1, 2); +CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args) + CURL_TEMP_PRINTF(1, 0); + +#undef CURL_TEMP_PRINTF + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* CURLINC_MPRINTF_H */ diff --git a/third_party/curl/include/curl/multi.h b/third_party/curl/include/curl/multi.h new file mode 100644 index 0000000000..7b6c351ada --- /dev/null +++ b/third_party/curl/include/curl/multi.h @@ -0,0 +1,485 @@ +#ifndef CURLINC_MULTI_H +#define CURLINC_MULTI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* + This is an "external" header file. Do not give away any internals here! + + GOALS + + o Enable a "pull" interface. The application that uses libcurl decides where + and when to ask libcurl to get/send data. + + o Enable multiple simultaneous transfers in the same thread without making it + complicated for the application. + + o Enable the application to select() on its own file descriptors and curl's + file descriptors simultaneous easily. + +*/ + +/* + * This header file should not really need to include "curl.h" since curl.h + * itself includes this file and we expect user applications to do #include + * without the need for especially including multi.h. + * + * For some reason we added this include here at one point, and rather than to + * break existing (wrongly written) libcurl applications, we leave it as-is + * but with this warning attached. + */ +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) +typedef struct Curl_multi CURLM; +#else +typedef void CURLM; +#endif + +typedef enum { + CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or + curl_multi_socket*() soon */ + CURLM_OK, + CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ + CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ + CURLM_OUT_OF_MEMORY, /* if you ever get this, you are in deep sh*t */ + CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ + CURLM_BAD_SOCKET, /* the passed in socket argument did not match */ + CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */ + CURLM_ADDED_ALREADY, /* an easy handle already added to a multi handle was + attempted to get added - again */ + CURLM_RECURSIVE_API_CALL, /* an api function was called from inside a + callback */ + CURLM_WAKEUP_FAILURE, /* wakeup is unavailable or failed */ + CURLM_BAD_FUNCTION_ARGUMENT, /* function called with a bad parameter */ + CURLM_ABORTED_BY_CALLBACK, + CURLM_UNRECOVERABLE_POLL, + CURLM_LAST +} CURLMcode; + +/* just to make code nicer when using curl_multi_socket() you can now check + for CURLM_CALL_MULTI_SOCKET too in the same style it works for + curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ +#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM + +/* bitmask bits for CURLMOPT_PIPELINING */ +#define CURLPIPE_NOTHING 0L +#define CURLPIPE_HTTP1 1L +#define CURLPIPE_MULTIPLEX 2L + +typedef enum { + CURLMSG_NONE, /* first, not used */ + CURLMSG_DONE, /* This easy handle has completed. 'result' contains + the CURLcode of the transfer */ + CURLMSG_LAST /* last, not used */ +} CURLMSG; + +struct CURLMsg { + CURLMSG msg; /* what this message means */ + CURL *easy_handle; /* the handle it concerns */ + union { + void *whatever; /* message-specific data */ + CURLcode result; /* return code for transfer */ + } data; +}; +typedef struct CURLMsg CURLMsg; + +/* Based on poll(2) structure and values. + * We do not use pollfd and POLL* constants explicitly + * to cover platforms without poll(). */ +#define CURL_WAIT_POLLIN 0x0001 +#define CURL_WAIT_POLLPRI 0x0002 +#define CURL_WAIT_POLLOUT 0x0004 + +struct curl_waitfd { + curl_socket_t fd; + short events; + short revents; +}; + +/* + * Name: curl_multi_init() + * + * Desc: initialize multi-style curl usage + * + * Returns: a new CURLM handle to use in all 'curl_multi' functions. + */ +CURL_EXTERN CURLM *curl_multi_init(void); + +/* + * Name: curl_multi_add_handle() + * + * Desc: add a standard curl handle to the multi stack + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); + +/* + * Name: curl_multi_wait() + * + * Desc: Poll on all fds within a CURLM set as well as any + * additional fds passed to the function. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret); + +/* + * Name: curl_multi_poll() + * + * Desc: Poll on all fds within a CURLM set as well as any + * additional fds passed to the function. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret); + +/* + * Name: curl_multi_wakeup() + * + * Desc: wakes up a sleeping curl_multi_poll call. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle); + + /* + * Name: curl_multi_perform() + * + * Desc: When the app thinks there is data available for curl it calls this + * function to read/write whatever there is right now. This returns + * as soon as the reads and writes are done. This function does not + * require that there actually is data available for reading or that + * data can be written, it can be called just in case. It returns + * the number of handles that still transfer data in the second + * argument's integer-pointer. + * + * Returns: CURLMcode type, general multi error code. *NOTE* that this only + * returns errors etc regarding the whole multi stack. There might + * still have occurred problems on individual transfers even when + * this returns OK. + */ +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, + int *running_handles); + + /* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles will be if this function is called + * in the middle of a transfer. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); + +/* + * Name: curl_multi_info_read() + * + * Desc: Ask the multi handle if there is any messages/informationals from + * the individual transfers. Messages include informationals such as + * error code from the transfer or just the fact that a transfer is + * completed. More details on these should be written down as well. + * + * Repeated calls to this function will return a new struct each + * time, until a special "end of msgs" struct is returned as a signal + * that there is no more to get at this point. + * + * The data the returned pointer points to will not survive calling + * curl_multi_cleanup(). + * + * The 'CURLMsg' struct is meant to be very simple and only contain + * very basic information. If more involved information is wanted, + * we will provide the particular "transfer handle" in that struct + * and that should/could/would be used in subsequent + * curl_easy_getinfo() calls (or similar). The point being that we + * must never expose complex structs to applications, as then we will + * undoubtably get backwards compatibility problems in the future. + * + * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out + * of structs. It also writes the number of messages left in the + * queue (after this read) in the integer the second argument points + * to. + */ +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, + int *msgs_in_queue); + +/* + * Name: curl_multi_strerror() + * + * Desc: The curl_multi_strerror function may be used to turn a CURLMcode + * value into the equivalent human readable error string. This is + * useful for printing meaningful error messages. + * + * Returns: A pointer to a null-terminated error message. + */ +CURL_EXTERN const char *curl_multi_strerror(CURLMcode); + +/* + * Name: curl_multi_socket() and + * curl_multi_socket_all() + * + * Desc: An alternative version of curl_multi_perform() that allows the + * application to pass in one of the file descriptors that have been + * detected to have "action" on them and let libcurl perform. + * See manpage for details. + */ +#define CURL_POLL_NONE 0 +#define CURL_POLL_IN 1 +#define CURL_POLL_OUT 2 +#define CURL_POLL_INOUT 3 +#define CURL_POLL_REMOVE 4 + +#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD + +#define CURL_CSELECT_IN 0x01 +#define CURL_CSELECT_OUT 0x02 +#define CURL_CSELECT_ERR 0x04 + +typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp); /* private socket + pointer */ +/* + * Name: curl_multi_timer_callback + * + * Desc: Called by libcurl whenever the library detects a change in the + * maximum number of milliseconds the app is allowed to wait before + * curl_multi_socket() or curl_multi_perform() must be called + * (to allow libcurl's timed events to take place). + * + * Returns: The callback should return zero. + */ +typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp); /* private callback + pointer */ + +CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") +curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, + curl_socket_t s, + int ev_bitmask, + int *running_handles); + +CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") +curl_multi_socket_all(CURLM *multi_handle, int *running_handles); + +#ifndef CURL_ALLOW_OLD_MULTI_SOCKET +/* This macro below was added in 7.16.3 to push users who recompile to use + the new curl_multi_socket_action() instead of the old curl_multi_socket() +*/ +#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z) +#endif + +/* + * Name: curl_multi_timeout() + * + * Desc: Returns the maximum number of milliseconds the app is allowed to + * wait before curl_multi_socket() or curl_multi_perform() must be + * called (to allow libcurl's timed events to take place). + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, + long *milliseconds); + +typedef enum { + /* This is the socket callback function pointer */ + CURLOPT(CURLMOPT_SOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 1), + + /* This is the argument passed to the socket callback */ + CURLOPT(CURLMOPT_SOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 2), + + /* set to 1 to enable pipelining for this multi handle */ + CURLOPT(CURLMOPT_PIPELINING, CURLOPTTYPE_LONG, 3), + + /* This is the timer callback function pointer */ + CURLOPT(CURLMOPT_TIMERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 4), + + /* This is the argument passed to the timer callback */ + CURLOPT(CURLMOPT_TIMERDATA, CURLOPTTYPE_OBJECTPOINT, 5), + + /* maximum number of entries in the connection cache */ + CURLOPT(CURLMOPT_MAXCONNECTS, CURLOPTTYPE_LONG, 6), + + /* maximum number of (pipelining) connections to one host */ + CURLOPT(CURLMOPT_MAX_HOST_CONNECTIONS, CURLOPTTYPE_LONG, 7), + + /* maximum number of requests in a pipeline */ + CURLOPT(CURLMOPT_MAX_PIPELINE_LENGTH, CURLOPTTYPE_LONG, 8), + + /* a connection with a content-length longer than this + will not be considered for pipelining */ + CURLOPT(CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 9), + + /* a connection with a chunk length longer than this + will not be considered for pipelining */ + CURLOPT(CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 10), + + /* a list of site names(+port) that are blocked from pipelining */ + CURLOPT(CURLMOPT_PIPELINING_SITE_BL, CURLOPTTYPE_OBJECTPOINT, 11), + + /* a list of server types that are blocked from pipelining */ + CURLOPT(CURLMOPT_PIPELINING_SERVER_BL, CURLOPTTYPE_OBJECTPOINT, 12), + + /* maximum number of open connections in total */ + CURLOPT(CURLMOPT_MAX_TOTAL_CONNECTIONS, CURLOPTTYPE_LONG, 13), + + /* This is the server push callback function pointer */ + CURLOPT(CURLMOPT_PUSHFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 14), + + /* This is the argument passed to the server push callback */ + CURLOPT(CURLMOPT_PUSHDATA, CURLOPTTYPE_OBJECTPOINT, 15), + + /* maximum number of concurrent streams to support on a connection */ + CURLOPT(CURLMOPT_MAX_CONCURRENT_STREAMS, CURLOPTTYPE_LONG, 16), + + CURLMOPT_LASTENTRY /* the last unused */ +} CURLMoption; + + +/* + * Name: curl_multi_setopt() + * + * Desc: Sets options for the multi handle. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, + CURLMoption option, ...); + + +/* + * Name: curl_multi_assign() + * + * Desc: This function sets an association in the multi handle between the + * given socket and a private pointer of the application. This is + * (only) useful for curl_multi_socket uses. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, + curl_socket_t sockfd, void *sockp); + +/* + * Name: curl_multi_get_handles() + * + * Desc: Returns an allocated array holding all handles currently added to + * the multi handle. Marks the final entry with a NULL pointer. If + * there is no easy handle added to the multi handle, this function + * returns an array with the first entry as a NULL pointer. + * + * Returns: NULL on failure, otherwise a CURL **array pointer + */ +CURL_EXTERN CURL **curl_multi_get_handles(CURLM *multi_handle); + +/* + * Name: curl_push_callback + * + * Desc: This callback gets called when a new stream is being pushed by the + * server. It approves or denies the new stream. It can also decide + * to completely fail the connection. + * + * Returns: CURL_PUSH_OK, CURL_PUSH_DENY or CURL_PUSH_ERROROUT + */ +#define CURL_PUSH_OK 0 +#define CURL_PUSH_DENY 1 +#define CURL_PUSH_ERROROUT 2 /* added in 7.72.0 */ + +struct curl_pushheaders; /* forward declaration only */ + +CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h, + size_t num); +CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h, + const char *name); + +typedef int (*curl_push_callback)(CURL *parent, + CURL *easy, + size_t num_headers, + struct curl_pushheaders *headers, + void *userp); + +/* + * Name: curl_multi_waitfds() + * + * Desc: Ask curl for fds for polling. The app can use these to poll on. + * We want curl_multi_perform() called as soon as one of them are + * ready. Passing zero size allows to get just a number of fds. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *multi, + struct curl_waitfd *ufds, + unsigned int size, + unsigned int *fd_count); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/third_party/curl/include/curl/options.h b/third_party/curl/include/curl/options.h new file mode 100644 index 0000000000..1ed76a95c6 --- /dev/null +++ b/third_party/curl/include/curl/options.h @@ -0,0 +1,70 @@ +#ifndef CURLINC_OPTIONS_H +#define CURLINC_OPTIONS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + CURLOT_LONG, /* long (a range of values) */ + CURLOT_VALUES, /* (a defined set or bitmask) */ + CURLOT_OFF_T, /* curl_off_t (a range of values) */ + CURLOT_OBJECT, /* pointer (void *) */ + CURLOT_STRING, /* (char * to null-terminated buffer) */ + CURLOT_SLIST, /* (struct curl_slist *) */ + CURLOT_CBPTR, /* (void * passed as-is to a callback) */ + CURLOT_BLOB, /* blob (struct curl_blob *) */ + CURLOT_FUNCTION /* function pointer */ +} curl_easytype; + +/* Flag bits */ + +/* "alias" means it is provided for old programs to remain functional, + we prefer another name */ +#define CURLOT_FLAG_ALIAS (1<<0) + +/* The CURLOPTTYPE_* id ranges can still be used to figure out what type/size + to use for curl_easy_setopt() for the given id */ +struct curl_easyoption { + const char *name; + CURLoption id; + curl_easytype type; + unsigned int flags; +}; + +CURL_EXTERN const struct curl_easyoption * +curl_easy_option_by_name(const char *name); + +CURL_EXTERN const struct curl_easyoption * +curl_easy_option_by_id(CURLoption id); + +CURL_EXTERN const struct curl_easyoption * +curl_easy_option_next(const struct curl_easyoption *prev); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif +#endif /* CURLINC_OPTIONS_H */ diff --git a/third_party/curl/include/curl/stdcheaders.h b/third_party/curl/include/curl/stdcheaders.h new file mode 100644 index 0000000000..7451aa3052 --- /dev/null +++ b/third_party/curl/include/curl/stdcheaders.h @@ -0,0 +1,35 @@ +#ifndef CURLINC_STDCHEADERS_H +#define CURLINC_STDCHEADERS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include + +size_t fread(void *, size_t, size_t, FILE *); +size_t fwrite(const void *, size_t, size_t, FILE *); + +int strcasecmp(const char *, const char *); +int strncasecmp(const char *, const char *, size_t); + +#endif /* CURLINC_STDCHEADERS_H */ diff --git a/third_party/curl/include/curl/system.h b/third_party/curl/include/curl/system.h new file mode 100644 index 0000000000..363914f20d --- /dev/null +++ b/third_party/curl/include/curl/system.h @@ -0,0 +1,496 @@ +#ifndef CURLINC_SYSTEM_H +#define CURLINC_SYSTEM_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Try to keep one section per platform, compiler and architecture, otherwise, + * if an existing section is reused for a different one and later on the + * original is adjusted, probably the piggybacking one can be adversely + * changed. + * + * In order to differentiate between platforms/compilers/architectures use + * only compiler built in predefined preprocessor symbols. + * + * curl_off_t + * ---------- + * + * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit + * wide signed integral data type. The width of this data type must remain + * constant and independent of any possible large file support settings. + * + * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit + * wide signed integral data type if there is no 64-bit type. + * + * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall + * only be violated if off_t is the only 64-bit data type available and the + * size of off_t is independent of large file support settings. Keep your + * build on the safe side avoiding an off_t gating. If you have a 64-bit + * off_t then take for sure that another 64-bit data type exists, dig deeper + * and you will find it. + * + */ + +#if defined(__DJGPP__) || defined(__GO32__) +# if defined(__DJGPP__) && (__DJGPP__ > 1) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__SALFORDC__) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__BORLANDC__) +# if (__BORLANDC__ < 0x520) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__TURBOC__) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__POCC__) +# if (__POCC__ < 280) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# elif defined(_MSC_VER) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__LCC__) +# if defined(__MCST__) /* MCST eLbrus Compiler Collection */ +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# else /* Local (or Little) C Compiler */ +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# endif + +#elif defined(macintosh) +# include +# if TYPE_LONGLONG +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int + +#elif defined(__TANDEM) +# if ! defined(__LP64) + /* Required for 32-bit NonStop builds only. */ +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# endif + +#elif defined(_WIN32_WCE) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__MINGW32__) +# include +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T PRId64 +# define CURL_FORMAT_CURL_OFF_TU PRIu64 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_PULL_SYS_TYPES_H 1 + +#elif defined(__VMS) +# if defined(__VAX) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int + +#elif defined(__OS400__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#elif defined(__MVS__) +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#elif defined(__370__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# elif defined(_LP64) +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(TPF) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__TINYC__) /* also known as tcc */ +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) /* Oracle Solaris Studio */ +# if !defined(__LP64) && (defined(__ILP32) || \ + defined(__i386) || \ + defined(__sparcv8) || \ + defined(__sparcv8plus)) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64) || \ + defined(__amd64) || defined(__sparcv9) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#elif defined(__xlc__) /* IBM xlc compiler */ +# if !defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#elif defined(__hpux) /* HP aCC compiler */ +# if !defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +/* ===================================== */ +/* KEEP MSVC THE PENULTIMATE ENTRY */ +/* ===================================== */ + +#elif defined(_MSC_VER) +# if (_MSC_VER >= 1800) +# include +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T PRId64 +# define CURL_FORMAT_CURL_OFF_TU PRIu64 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# elif (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +/* ===================================== */ +/* KEEP GENERIC GCC THE LAST ENTRY */ +/* ===================================== */ + +#elif defined(__GNUC__) && !defined(_SCO_DS) +# if !defined(__LP64__) && \ + (defined(__ILP32__) || defined(__i386__) || defined(__hppa__) || \ + defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || \ + defined(__sparc__) || defined(__mips__) || defined(__sh__) || \ + defined(__XTENSA__) || \ + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) || \ + (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L)) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64__) || \ + defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \ + defined(__e2k__) || \ + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) || \ + (defined(__LONG_MAX__) && __LONG_MAX__ == 9223372036854775807L) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#else +/* generic "safe guess" on old 32-bit style */ +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +#endif + +#ifdef _AIX +/* AIX needs */ +#define CURL_PULL_SYS_POLL_H +#endif + +/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */ +/* sys/types.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file */ +/* sys/socket.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file */ +/* sys/poll.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_POLL_H +# include +#endif + +/* Data type definition of curl_socklen_t. */ +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T + typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; +#endif + +/* Data type definition of curl_off_t. */ + +#ifdef CURL_TYPEOF_CURL_OFF_T + typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; +#endif + +/* + * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow + * these to be visible and exported by the external libcurl interface API, + * while also making them visible to the library internals, simply including + * curl_setup.h, without actually needing to include curl.h internally. + * If some day this section would grow big enough, all this should be moved + * to its own header file. + */ + +/* + * Figure out if we can use the ## preprocessor operator, which is supported + * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__ + * or __cplusplus so we need to carefully check for them too. + */ + +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ + defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ + defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \ + defined(__ILEC400__) + /* This compiler is believed to have an ISO compatible preprocessor */ +#define CURL_ISOCPP +#else + /* This compiler is believed NOT to have an ISO compatible preprocessor */ +#undef CURL_ISOCPP +#endif + +/* + * Macros for minimum-width signed and unsigned curl_off_t integer constants. + */ + +#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551) +# define CURLINC_OFF_T_C_HLPR2(x) x +# define CURLINC_OFF_T_C_HLPR1(x) CURLINC_OFF_T_C_HLPR2(x) +# define CURL_OFF_T_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \ + CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \ + CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU) +#else +# ifdef CURL_ISOCPP +# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix +# else +# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix +# endif +# define CURLINC_OFF_T_C_HLPR1(Val,Suffix) CURLINC_OFF_T_C_HLPR2(Val,Suffix) +# define CURL_OFF_T_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU) +#endif + +#endif /* CURLINC_SYSTEM_H */ diff --git a/third_party/curl/include/curl/typecheck-gcc.h b/third_party/curl/include/curl/typecheck-gcc.h new file mode 100644 index 0000000000..e532e6997d --- /dev/null +++ b/third_party/curl/include/curl/typecheck-gcc.h @@ -0,0 +1,718 @@ +#ifndef CURLINC_TYPECHECK_GCC_H +#define CURLINC_TYPECHECK_GCC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* wraps curl_easy_setopt() with typechecking */ + +/* To add a new kind of warning, add an + * if(curlcheck_sometype_option(_curl_opt)) + * if(!curlcheck_sometype(value)) + * _curl_easy_setopt_err_sometype(); + * block and define curlcheck_sometype_option, curlcheck_sometype and + * _curl_easy_setopt_err_sometype below + * + * NOTE: We use two nested 'if' statements here instead of the && operator, in + * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x + * when compiling with -Wlogical-op. + * + * To add an option that uses the same type as an existing option, you will + * just need to extend the appropriate _curl_*_option macro + */ +#define curl_easy_setopt(handle, option, value) \ + __extension__({ \ + CURLoption _curl_opt = (option); \ + if(__builtin_constant_p(_curl_opt)) { \ + CURL_IGNORE_DEPRECATION( \ + if(curlcheck_long_option(_curl_opt)) \ + if(!curlcheck_long(value)) \ + _curl_easy_setopt_err_long(); \ + if(curlcheck_off_t_option(_curl_opt)) \ + if(!curlcheck_off_t(value)) \ + _curl_easy_setopt_err_curl_off_t(); \ + if(curlcheck_string_option(_curl_opt)) \ + if(!curlcheck_string(value)) \ + _curl_easy_setopt_err_string(); \ + if(curlcheck_write_cb_option(_curl_opt)) \ + if(!curlcheck_write_cb(value)) \ + _curl_easy_setopt_err_write_callback(); \ + if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION) \ + if(!curlcheck_resolver_start_callback(value)) \ + _curl_easy_setopt_err_resolver_start_callback(); \ + if((_curl_opt) == CURLOPT_READFUNCTION) \ + if(!curlcheck_read_cb(value)) \ + _curl_easy_setopt_err_read_cb(); \ + if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ + if(!curlcheck_ioctl_cb(value)) \ + _curl_easy_setopt_err_ioctl_cb(); \ + if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ + if(!curlcheck_sockopt_cb(value)) \ + _curl_easy_setopt_err_sockopt_cb(); \ + if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!curlcheck_opensocket_cb(value)) \ + _curl_easy_setopt_err_opensocket_cb(); \ + if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ + if(!curlcheck_progress_cb(value)) \ + _curl_easy_setopt_err_progress_cb(); \ + if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ + if(!curlcheck_debug_cb(value)) \ + _curl_easy_setopt_err_debug_cb(); \ + if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!curlcheck_ssl_ctx_cb(value)) \ + _curl_easy_setopt_err_ssl_ctx_cb(); \ + if(curlcheck_conv_cb_option(_curl_opt)) \ + if(!curlcheck_conv_cb(value)) \ + _curl_easy_setopt_err_conv_cb(); \ + if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ + if(!curlcheck_seek_cb(value)) \ + _curl_easy_setopt_err_seek_cb(); \ + if(curlcheck_cb_data_option(_curl_opt)) \ + if(!curlcheck_cb_data(value)) \ + _curl_easy_setopt_err_cb_data(); \ + if((_curl_opt) == CURLOPT_ERRORBUFFER) \ + if(!curlcheck_error_buffer(value)) \ + _curl_easy_setopt_err_error_buffer(); \ + if((_curl_opt) == CURLOPT_STDERR) \ + if(!curlcheck_FILE(value)) \ + _curl_easy_setopt_err_FILE(); \ + if(curlcheck_postfields_option(_curl_opt)) \ + if(!curlcheck_postfields(value)) \ + _curl_easy_setopt_err_postfields(); \ + if((_curl_opt) == CURLOPT_HTTPPOST) \ + if(!curlcheck_arr((value), struct curl_httppost)) \ + _curl_easy_setopt_err_curl_httpost(); \ + if((_curl_opt) == CURLOPT_MIMEPOST) \ + if(!curlcheck_ptr((value), curl_mime)) \ + _curl_easy_setopt_err_curl_mimepost(); \ + if(curlcheck_slist_option(_curl_opt)) \ + if(!curlcheck_arr((value), struct curl_slist)) \ + _curl_easy_setopt_err_curl_slist(); \ + if((_curl_opt) == CURLOPT_SHARE) \ + if(!curlcheck_ptr((value), CURLSH)) \ + _curl_easy_setopt_err_CURLSH(); \ + ) \ + } \ + curl_easy_setopt(handle, _curl_opt, value); \ + }) + +/* wraps curl_easy_getinfo() with typechecking */ +#define curl_easy_getinfo(handle, info, arg) \ + __extension__({ \ + CURLINFO _curl_info = (info); \ + if(__builtin_constant_p(_curl_info)) { \ + CURL_IGNORE_DEPRECATION( \ + if(curlcheck_string_info(_curl_info)) \ + if(!curlcheck_arr((arg), char *)) \ + _curl_easy_getinfo_err_string(); \ + if(curlcheck_long_info(_curl_info)) \ + if(!curlcheck_arr((arg), long)) \ + _curl_easy_getinfo_err_long(); \ + if(curlcheck_double_info(_curl_info)) \ + if(!curlcheck_arr((arg), double)) \ + _curl_easy_getinfo_err_double(); \ + if(curlcheck_slist_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_slist *)) \ + _curl_easy_getinfo_err_curl_slist(); \ + if(curlcheck_tlssessioninfo_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ + _curl_easy_getinfo_err_curl_tlssesssioninfo(); \ + if(curlcheck_certinfo_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_certinfo *)) \ + _curl_easy_getinfo_err_curl_certinfo(); \ + if(curlcheck_socket_info(_curl_info)) \ + if(!curlcheck_arr((arg), curl_socket_t)) \ + _curl_easy_getinfo_err_curl_socket(); \ + if(curlcheck_off_t_info(_curl_info)) \ + if(!curlcheck_arr((arg), curl_off_t)) \ + _curl_easy_getinfo_err_curl_off_t(); \ + ) \ + } \ + curl_easy_getinfo(handle, _curl_info, arg); \ + }) + +/* + * For now, just make sure that the functions are called with three arguments + */ +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) + + +/* the actual warnings, triggered by calling the _curl_easy_setopt_err* + * functions */ + +/* To define a new warning, use _CURL_WARNING(identifier, "message") */ +#define CURLWARNING(id, message) \ + static void __attribute__((__warning__(message))) \ + __attribute__((__unused__)) __attribute__((__noinline__)) \ + id(void) { __asm__(""); } + +CURLWARNING(_curl_easy_setopt_err_long, + "curl_easy_setopt expects a long argument for this option") +CURLWARNING(_curl_easy_setopt_err_curl_off_t, + "curl_easy_setopt expects a curl_off_t argument for this option") +CURLWARNING(_curl_easy_setopt_err_string, + "curl_easy_setopt expects a " + "string ('char *' or char[]) argument for this option" + ) +CURLWARNING(_curl_easy_setopt_err_write_callback, + "curl_easy_setopt expects a curl_write_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_resolver_start_callback, + "curl_easy_setopt expects a " + "curl_resolver_start_callback argument for this option" + ) +CURLWARNING(_curl_easy_setopt_err_read_cb, + "curl_easy_setopt expects a curl_read_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_ioctl_cb, + "curl_easy_setopt expects a curl_ioctl_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_sockopt_cb, + "curl_easy_setopt expects a curl_sockopt_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_opensocket_cb, + "curl_easy_setopt expects a " + "curl_opensocket_callback argument for this option" + ) +CURLWARNING(_curl_easy_setopt_err_progress_cb, + "curl_easy_setopt expects a curl_progress_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_debug_cb, + "curl_easy_setopt expects a curl_debug_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_ssl_ctx_cb, + "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_conv_cb, + "curl_easy_setopt expects a curl_conv_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_seek_cb, + "curl_easy_setopt expects a curl_seek_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_cb_data, + "curl_easy_setopt expects a " + "private data pointer as argument for this option") +CURLWARNING(_curl_easy_setopt_err_error_buffer, + "curl_easy_setopt expects a " + "char buffer of CURL_ERROR_SIZE as argument for this option") +CURLWARNING(_curl_easy_setopt_err_FILE, + "curl_easy_setopt expects a 'FILE *' argument for this option") +CURLWARNING(_curl_easy_setopt_err_postfields, + "curl_easy_setopt expects a 'void *' or 'char *' argument for this option") +CURLWARNING(_curl_easy_setopt_err_curl_httpost, + "curl_easy_setopt expects a 'struct curl_httppost *' " + "argument for this option") +CURLWARNING(_curl_easy_setopt_err_curl_mimepost, + "curl_easy_setopt expects a 'curl_mime *' " + "argument for this option") +CURLWARNING(_curl_easy_setopt_err_curl_slist, + "curl_easy_setopt expects a 'struct curl_slist *' argument for this option") +CURLWARNING(_curl_easy_setopt_err_CURLSH, + "curl_easy_setopt expects a CURLSH* argument for this option") + +CURLWARNING(_curl_easy_getinfo_err_string, + "curl_easy_getinfo expects a pointer to 'char *' for this info") +CURLWARNING(_curl_easy_getinfo_err_long, + "curl_easy_getinfo expects a pointer to long for this info") +CURLWARNING(_curl_easy_getinfo_err_double, + "curl_easy_getinfo expects a pointer to double for this info") +CURLWARNING(_curl_easy_getinfo_err_curl_slist, + "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info") +CURLWARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo, + "curl_easy_getinfo expects a pointer to " + "'struct curl_tlssessioninfo *' for this info") +CURLWARNING(_curl_easy_getinfo_err_curl_certinfo, + "curl_easy_getinfo expects a pointer to " + "'struct curl_certinfo *' for this info") +CURLWARNING(_curl_easy_getinfo_err_curl_socket, + "curl_easy_getinfo expects a pointer to curl_socket_t for this info") +CURLWARNING(_curl_easy_getinfo_err_curl_off_t, + "curl_easy_getinfo expects a pointer to curl_off_t for this info") + +/* groups of curl_easy_setops options that take the same type of argument */ + +/* To add a new option to one of the groups, just add + * (option) == CURLOPT_SOMETHING + * to the or-expression. If the option takes a long or curl_off_t, you do not + * have to do anything + */ + +/* evaluates to true if option takes a long argument */ +#define curlcheck_long_option(option) \ + (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT) + +#define curlcheck_off_t_option(option) \ + (((option) > CURLOPTTYPE_OFF_T) && ((option) < CURLOPTTYPE_BLOB)) + +/* evaluates to true if option takes a char* argument */ +#define curlcheck_string_option(option) \ + ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \ + (option) == CURLOPT_ACCEPT_ENCODING || \ + (option) == CURLOPT_ALTSVC || \ + (option) == CURLOPT_CAINFO || \ + (option) == CURLOPT_CAPATH || \ + (option) == CURLOPT_COOKIE || \ + (option) == CURLOPT_COOKIEFILE || \ + (option) == CURLOPT_COOKIEJAR || \ + (option) == CURLOPT_COOKIELIST || \ + (option) == CURLOPT_CRLFILE || \ + (option) == CURLOPT_CUSTOMREQUEST || \ + (option) == CURLOPT_DEFAULT_PROTOCOL || \ + (option) == CURLOPT_DNS_INTERFACE || \ + (option) == CURLOPT_DNS_LOCAL_IP4 || \ + (option) == CURLOPT_DNS_LOCAL_IP6 || \ + (option) == CURLOPT_DNS_SERVERS || \ + (option) == CURLOPT_DOH_URL || \ + (option) == CURLOPT_ECH || \ + (option) == CURLOPT_EGDSOCKET || \ + (option) == CURLOPT_FTP_ACCOUNT || \ + (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ + (option) == CURLOPT_FTPPORT || \ + (option) == CURLOPT_HSTS || \ + (option) == CURLOPT_HAPROXY_CLIENT_IP || \ + (option) == CURLOPT_INTERFACE || \ + (option) == CURLOPT_ISSUERCERT || \ + (option) == CURLOPT_KEYPASSWD || \ + (option) == CURLOPT_KRBLEVEL || \ + (option) == CURLOPT_LOGIN_OPTIONS || \ + (option) == CURLOPT_MAIL_AUTH || \ + (option) == CURLOPT_MAIL_FROM || \ + (option) == CURLOPT_NETRC_FILE || \ + (option) == CURLOPT_NOPROXY || \ + (option) == CURLOPT_PASSWORD || \ + (option) == CURLOPT_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PRE_PROXY || \ + (option) == CURLOPT_PROTOCOLS_STR || \ + (option) == CURLOPT_PROXY || \ + (option) == CURLOPT_PROXY_CAINFO || \ + (option) == CURLOPT_PROXY_CAPATH || \ + (option) == CURLOPT_PROXY_CRLFILE || \ + (option) == CURLOPT_PROXY_ISSUERCERT || \ + (option) == CURLOPT_PROXY_KEYPASSWD || \ + (option) == CURLOPT_PROXY_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PROXY_SERVICE_NAME || \ + (option) == CURLOPT_PROXY_SSL_CIPHER_LIST || \ + (option) == CURLOPT_PROXY_SSLCERT || \ + (option) == CURLOPT_PROXY_SSLCERTTYPE || \ + (option) == CURLOPT_PROXY_SSLKEY || \ + (option) == CURLOPT_PROXY_SSLKEYTYPE || \ + (option) == CURLOPT_PROXY_TLS13_CIPHERS || \ + (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_PROXY_TLSAUTH_TYPE || \ + (option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \ + (option) == CURLOPT_PROXYPASSWORD || \ + (option) == CURLOPT_PROXYUSERNAME || \ + (option) == CURLOPT_PROXYUSERPWD || \ + (option) == CURLOPT_RANDOM_FILE || \ + (option) == CURLOPT_RANGE || \ + (option) == CURLOPT_REDIR_PROTOCOLS_STR || \ + (option) == CURLOPT_REFERER || \ + (option) == CURLOPT_REQUEST_TARGET || \ + (option) == CURLOPT_RTSP_SESSION_ID || \ + (option) == CURLOPT_RTSP_STREAM_URI || \ + (option) == CURLOPT_RTSP_TRANSPORT || \ + (option) == CURLOPT_SASL_AUTHZID || \ + (option) == CURLOPT_SERVICE_NAME || \ + (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 || \ + (option) == CURLOPT_SSH_KNOWNHOSTS || \ + (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ + (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ + (option) == CURLOPT_SSLCERT || \ + (option) == CURLOPT_SSLCERTTYPE || \ + (option) == CURLOPT_SSLENGINE || \ + (option) == CURLOPT_SSLKEY || \ + (option) == CURLOPT_SSLKEYTYPE || \ + (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_TLS13_CIPHERS || \ + (option) == CURLOPT_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_TLSAUTH_TYPE || \ + (option) == CURLOPT_TLSAUTH_USERNAME || \ + (option) == CURLOPT_UNIX_SOCKET_PATH || \ + (option) == CURLOPT_URL || \ + (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_USERNAME || \ + (option) == CURLOPT_AWS_SIGV4 || \ + (option) == CURLOPT_USERPWD || \ + (option) == CURLOPT_XOAUTH2_BEARER || \ + (option) == CURLOPT_SSL_EC_CURVES || \ + 0) + +/* evaluates to true if option takes a curl_write_callback argument */ +#define curlcheck_write_cb_option(option) \ + ((option) == CURLOPT_HEADERFUNCTION || \ + (option) == CURLOPT_WRITEFUNCTION) + +/* evaluates to true if option takes a curl_conv_callback argument */ +#define curlcheck_conv_cb_option(option) \ + ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) + +/* evaluates to true if option takes a data argument to pass to a callback */ +#define curlcheck_cb_data_option(option) \ + ((option) == CURLOPT_CHUNK_DATA || \ + (option) == CURLOPT_CLOSESOCKETDATA || \ + (option) == CURLOPT_DEBUGDATA || \ + (option) == CURLOPT_FNMATCH_DATA || \ + (option) == CURLOPT_HEADERDATA || \ + (option) == CURLOPT_HSTSREADDATA || \ + (option) == CURLOPT_HSTSWRITEDATA || \ + (option) == CURLOPT_INTERLEAVEDATA || \ + (option) == CURLOPT_IOCTLDATA || \ + (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PREREQDATA || \ + (option) == CURLOPT_PROGRESSDATA || \ + (option) == CURLOPT_READDATA || \ + (option) == CURLOPT_SEEKDATA || \ + (option) == CURLOPT_SOCKOPTDATA || \ + (option) == CURLOPT_SSH_KEYDATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_WRITEDATA || \ + (option) == CURLOPT_RESOLVER_START_DATA || \ + (option) == CURLOPT_TRAILERDATA || \ + (option) == CURLOPT_SSH_HOSTKEYDATA || \ + 0) + +/* evaluates to true if option takes a POST data argument (void* or char*) */ +#define curlcheck_postfields_option(option) \ + ((option) == CURLOPT_POSTFIELDS || \ + (option) == CURLOPT_COPYPOSTFIELDS || \ + 0) + +/* evaluates to true if option takes a struct curl_slist * argument */ +#define curlcheck_slist_option(option) \ + ((option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_MAIL_RCPT || \ + (option) == CURLOPT_POSTQUOTE || \ + (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_PROXYHEADER || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_RESOLVE || \ + (option) == CURLOPT_TELNETOPTIONS || \ + (option) == CURLOPT_CONNECT_TO || \ + 0) + +/* groups of curl_easy_getinfo infos that take the same type of argument */ + +/* evaluates to true if info expects a pointer to char * argument */ +#define curlcheck_string_info(info) \ + (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG && \ + (info) != CURLINFO_PRIVATE) + +/* evaluates to true if info expects a pointer to long argument */ +#define curlcheck_long_info(info) \ + (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) + +/* evaluates to true if info expects a pointer to double argument */ +#define curlcheck_double_info(info) \ + (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) + +/* true if info expects a pointer to struct curl_slist * argument */ +#define curlcheck_slist_info(info) \ + (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST)) + +/* true if info expects a pointer to struct curl_tlssessioninfo * argument */ +#define curlcheck_tlssessioninfo_info(info) \ + (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION)) + +/* true if info expects a pointer to struct curl_certinfo * argument */ +#define curlcheck_certinfo_info(info) ((info) == CURLINFO_CERTINFO) + +/* true if info expects a pointer to struct curl_socket_t argument */ +#define curlcheck_socket_info(info) \ + (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T) + +/* true if info expects a pointer to curl_off_t argument */ +#define curlcheck_off_t_info(info) \ + (CURLINFO_OFF_T < (info)) + + +/* typecheck helpers -- check whether given expression has requested type */ + +/* For pointers, you can use the curlcheck_ptr/curlcheck_arr macros, + * otherwise define a new macro. Search for __builtin_types_compatible_p + * in the GCC manual. + * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is + * the actual expression passed to the curl_easy_setopt macro. This + * means that you can only apply the sizeof and __typeof__ operators, no + * == or whatsoever. + */ + +/* XXX: should evaluate to true if expr is a pointer */ +#define curlcheck_any_ptr(expr) \ + (sizeof(expr) == sizeof(void *)) + +/* evaluates to true if expr is NULL */ +/* XXX: must not evaluate expr, so this check is not accurate */ +#define curlcheck_NULL(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL))) + +/* evaluates to true if expr is type*, const type* or NULL */ +#define curlcheck_ptr(expr, type) \ + (curlcheck_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), type *) || \ + __builtin_types_compatible_p(__typeof__(expr), const type *)) + +/* evaluates to true if expr is one of type[], type*, NULL or const type* */ +#define curlcheck_arr(expr, type) \ + (curlcheck_ptr((expr), type) || \ + __builtin_types_compatible_p(__typeof__(expr), type [])) + +/* evaluates to true if expr is a string */ +#define curlcheck_string(expr) \ + (curlcheck_arr((expr), char) || \ + curlcheck_arr((expr), signed char) || \ + curlcheck_arr((expr), unsigned char)) + +/* evaluates to true if expr is a long (no matter the signedness) + * XXX: for now, int is also accepted (and therefore short and char, which + * are promoted to int when passed to a variadic function) */ +#define curlcheck_long(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), long) || \ + __builtin_types_compatible_p(__typeof__(expr), signed long) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \ + __builtin_types_compatible_p(__typeof__(expr), int) || \ + __builtin_types_compatible_p(__typeof__(expr), signed int) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned int) || \ + __builtin_types_compatible_p(__typeof__(expr), short) || \ + __builtin_types_compatible_p(__typeof__(expr), signed short) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned short) || \ + __builtin_types_compatible_p(__typeof__(expr), char) || \ + __builtin_types_compatible_p(__typeof__(expr), signed char) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned char)) + +/* evaluates to true if expr is of type curl_off_t */ +#define curlcheck_off_t(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) + +/* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ +/* XXX: also check size of an char[] array? */ +#define curlcheck_error_buffer(expr) \ + (curlcheck_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), char *) || \ + __builtin_types_compatible_p(__typeof__(expr), char[])) + +/* evaluates to true if expr is of type (const) void* or (const) FILE* */ +#if 0 +#define curlcheck_cb_data(expr) \ + (curlcheck_ptr((expr), void) || \ + curlcheck_ptr((expr), FILE)) +#else /* be less strict */ +#define curlcheck_cb_data(expr) \ + curlcheck_any_ptr(expr) +#endif + +/* evaluates to true if expr is of type FILE* */ +#define curlcheck_FILE(expr) \ + (curlcheck_NULL(expr) || \ + (__builtin_types_compatible_p(__typeof__(expr), FILE *))) + +/* evaluates to true if expr can be passed as POST data (void* or char*) */ +#define curlcheck_postfields(expr) \ + (curlcheck_ptr((expr), void) || \ + curlcheck_arr((expr), char) || \ + curlcheck_arr((expr), unsigned char)) + +/* helper: __builtin_types_compatible_p distinguishes between functions and + * function pointers, hide it */ +#define curlcheck_cb_compatible(func, type) \ + (__builtin_types_compatible_p(__typeof__(func), type) || \ + __builtin_types_compatible_p(__typeof__(func) *, type)) + +/* evaluates to true if expr is of type curl_resolver_start_callback */ +#define curlcheck_resolver_start_callback(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_resolver_start_callback)) + +/* evaluates to true if expr is of type curl_read_callback or "similar" */ +#define curlcheck_read_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), __typeof__(fread) *) || \ + curlcheck_cb_compatible((expr), curl_read_callback) || \ + curlcheck_cb_compatible((expr), _curl_read_callback1) || \ + curlcheck_cb_compatible((expr), _curl_read_callback2) || \ + curlcheck_cb_compatible((expr), _curl_read_callback3) || \ + curlcheck_cb_compatible((expr), _curl_read_callback4) || \ + curlcheck_cb_compatible((expr), _curl_read_callback5) || \ + curlcheck_cb_compatible((expr), _curl_read_callback6)) +typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *); +typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *); +typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *); +typedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *); +typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *); +typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *); + +/* evaluates to true if expr is of type curl_write_callback or "similar" */ +#define curlcheck_write_cb(expr) \ + (curlcheck_read_cb(expr) || \ + curlcheck_cb_compatible((expr), __typeof__(fwrite) *) || \ + curlcheck_cb_compatible((expr), curl_write_callback) || \ + curlcheck_cb_compatible((expr), _curl_write_callback1) || \ + curlcheck_cb_compatible((expr), _curl_write_callback2) || \ + curlcheck_cb_compatible((expr), _curl_write_callback3) || \ + curlcheck_cb_compatible((expr), _curl_write_callback4) || \ + curlcheck_cb_compatible((expr), _curl_write_callback5) || \ + curlcheck_cb_compatible((expr), _curl_write_callback6)) +typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *); +typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t, + const void *); +typedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *); +typedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *); +typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t, + const void *); +typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *); + +/* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ +#define curlcheck_ioctl_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_ioctl_callback) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback1) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback2) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback3) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback4)) +typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *); +typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *); +typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *); +typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *); + +/* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ +#define curlcheck_sockopt_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_sockopt_callback) || \ + curlcheck_cb_compatible((expr), _curl_sockopt_callback1) || \ + curlcheck_cb_compatible((expr), _curl_sockopt_callback2)) +typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); +typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t, + curlsocktype); + +/* evaluates to true if expr is of type curl_opensocket_callback or + "similar" */ +#define curlcheck_opensocket_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_opensocket_callback) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback1) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback2) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback3) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback4)) +typedef curl_socket_t (*_curl_opensocket_callback1) + (void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (*_curl_opensocket_callback2) + (void *, curlsocktype, const struct curl_sockaddr *); +typedef curl_socket_t (*_curl_opensocket_callback3) + (const void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (*_curl_opensocket_callback4) + (const void *, curlsocktype, const struct curl_sockaddr *); + +/* evaluates to true if expr is of type curl_progress_callback or "similar" */ +#define curlcheck_progress_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_progress_callback) || \ + curlcheck_cb_compatible((expr), _curl_progress_callback1) || \ + curlcheck_cb_compatible((expr), _curl_progress_callback2)) +typedef int (*_curl_progress_callback1)(void *, + double, double, double, double); +typedef int (*_curl_progress_callback2)(const void *, + double, double, double, double); + +/* evaluates to true if expr is of type curl_debug_callback or "similar" */ +#define curlcheck_debug_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_debug_callback) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback1) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback2) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback3) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback4) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback5) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback6) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback7) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback8)) +typedef int (*_curl_debug_callback1) (CURL *, + curl_infotype, char *, size_t, void *); +typedef int (*_curl_debug_callback2) (CURL *, + curl_infotype, char *, size_t, const void *); +typedef int (*_curl_debug_callback3) (CURL *, + curl_infotype, const char *, size_t, void *); +typedef int (*_curl_debug_callback4) (CURL *, + curl_infotype, const char *, size_t, const void *); +typedef int (*_curl_debug_callback5) (CURL *, + curl_infotype, unsigned char *, size_t, void *); +typedef int (*_curl_debug_callback6) (CURL *, + curl_infotype, unsigned char *, size_t, const void *); +typedef int (*_curl_debug_callback7) (CURL *, + curl_infotype, const unsigned char *, size_t, void *); +typedef int (*_curl_debug_callback8) (CURL *, + curl_infotype, const unsigned char *, size_t, const void *); + +/* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ +/* this is getting even messier... */ +#define curlcheck_ssl_ctx_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_ssl_ctx_callback) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback1) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback2) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback3) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback4) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback5) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback6) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback7) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback8)) +typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *); +typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *); +typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *); +typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *, + const void *); +#ifdef HEADER_SSL_H +/* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX + * this will of course break if we are included before OpenSSL headers... + */ +typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *); +typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *); +typedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX *, void *); +typedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX *, + const void *); +#else +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8; +#endif + +/* evaluates to true if expr is of type curl_conv_callback or "similar" */ +#define curlcheck_conv_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_conv_callback) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback1) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback2) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback3) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback4)) +typedef CURLcode (*_curl_conv_callback1)(char *, size_t length); +typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length); +typedef CURLcode (*_curl_conv_callback3)(void *, size_t length); +typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length); + +/* evaluates to true if expr is of type curl_seek_callback or "similar" */ +#define curlcheck_seek_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_seek_callback) || \ + curlcheck_cb_compatible((expr), _curl_seek_callback1) || \ + curlcheck_cb_compatible((expr), _curl_seek_callback2)) +typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int); +typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int); + + +#endif /* CURLINC_TYPECHECK_GCC_H */ diff --git a/third_party/curl/include/curl/urlapi.h b/third_party/curl/include/curl/urlapi.h new file mode 100644 index 0000000000..b4a6e5d567 --- /dev/null +++ b/third_party/curl/include/curl/urlapi.h @@ -0,0 +1,155 @@ +#ifndef CURLINC_URLAPI_H +#define CURLINC_URLAPI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* the error codes for the URL API */ +typedef enum { + CURLUE_OK, + CURLUE_BAD_HANDLE, /* 1 */ + CURLUE_BAD_PARTPOINTER, /* 2 */ + CURLUE_MALFORMED_INPUT, /* 3 */ + CURLUE_BAD_PORT_NUMBER, /* 4 */ + CURLUE_UNSUPPORTED_SCHEME, /* 5 */ + CURLUE_URLDECODE, /* 6 */ + CURLUE_OUT_OF_MEMORY, /* 7 */ + CURLUE_USER_NOT_ALLOWED, /* 8 */ + CURLUE_UNKNOWN_PART, /* 9 */ + CURLUE_NO_SCHEME, /* 10 */ + CURLUE_NO_USER, /* 11 */ + CURLUE_NO_PASSWORD, /* 12 */ + CURLUE_NO_OPTIONS, /* 13 */ + CURLUE_NO_HOST, /* 14 */ + CURLUE_NO_PORT, /* 15 */ + CURLUE_NO_QUERY, /* 16 */ + CURLUE_NO_FRAGMENT, /* 17 */ + CURLUE_NO_ZONEID, /* 18 */ + CURLUE_BAD_FILE_URL, /* 19 */ + CURLUE_BAD_FRAGMENT, /* 20 */ + CURLUE_BAD_HOSTNAME, /* 21 */ + CURLUE_BAD_IPV6, /* 22 */ + CURLUE_BAD_LOGIN, /* 23 */ + CURLUE_BAD_PASSWORD, /* 24 */ + CURLUE_BAD_PATH, /* 25 */ + CURLUE_BAD_QUERY, /* 26 */ + CURLUE_BAD_SCHEME, /* 27 */ + CURLUE_BAD_SLASHES, /* 28 */ + CURLUE_BAD_USER, /* 29 */ + CURLUE_LACKS_IDN, /* 30 */ + CURLUE_TOO_LARGE, /* 31 */ + CURLUE_LAST +} CURLUcode; + +typedef enum { + CURLUPART_URL, + CURLUPART_SCHEME, + CURLUPART_USER, + CURLUPART_PASSWORD, + CURLUPART_OPTIONS, + CURLUPART_HOST, + CURLUPART_PORT, + CURLUPART_PATH, + CURLUPART_QUERY, + CURLUPART_FRAGMENT, + CURLUPART_ZONEID /* added in 7.65.0 */ +} CURLUPart; + +#define CURLU_DEFAULT_PORT (1<<0) /* return default port number */ +#define CURLU_NO_DEFAULT_PORT (1<<1) /* act as if no port number was set, + if the port number matches the + default for the scheme */ +#define CURLU_DEFAULT_SCHEME (1<<2) /* return default scheme if + missing */ +#define CURLU_NON_SUPPORT_SCHEME (1<<3) /* allow non-supported scheme */ +#define CURLU_PATH_AS_IS (1<<4) /* leave dot sequences */ +#define CURLU_DISALLOW_USER (1<<5) /* no user+password allowed */ +#define CURLU_URLDECODE (1<<6) /* URL decode on get */ +#define CURLU_URLENCODE (1<<7) /* URL encode on set */ +#define CURLU_APPENDQUERY (1<<8) /* append a form style part */ +#define CURLU_GUESS_SCHEME (1<<9) /* legacy curl-style guessing */ +#define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the + scheme is unknown. */ +#define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */ +#define CURLU_PUNYCODE (1<<12) /* get the hostname in punycode */ +#define CURLU_PUNY2IDN (1<<13) /* punycode => IDN conversion */ +#define CURLU_GET_EMPTY (1<<14) /* allow empty queries and fragments + when extracting the URL or the + components */ +#define CURLU_NO_GUESS_SCHEME (1<<15) /* for get, do not accept a guess */ + +typedef struct Curl_URL CURLU; + +/* + * curl_url() creates a new CURLU handle and returns a pointer to it. + * Must be freed with curl_url_cleanup(). + */ +CURL_EXTERN CURLU *curl_url(void); + +/* + * curl_url_cleanup() frees the CURLU handle and related resources used for + * the URL parsing. It will not free strings previously returned with the URL + * API. + */ +CURL_EXTERN void curl_url_cleanup(CURLU *handle); + +/* + * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new + * handle must also be freed with curl_url_cleanup(). + */ +CURL_EXTERN CURLU *curl_url_dup(const CURLU *in); + +/* + * curl_url_get() extracts a specific part of the URL from a CURLU + * handle. Returns error code. The returned pointer MUST be freed with + * curl_free() afterwards. + */ +CURL_EXTERN CURLUcode curl_url_get(const CURLU *handle, CURLUPart what, + char **part, unsigned int flags); + +/* + * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns + * error code. The passed in string will be copied. Passing a NULL instead of + * a part string, clears that part. + */ +CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, + const char *part, unsigned int flags); + +/* + * curl_url_strerror() turns a CURLUcode value into the equivalent human + * readable error string. This is useful for printing meaningful error + * messages. + */ +CURL_EXTERN const char *curl_url_strerror(CURLUcode); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* CURLINC_URLAPI_H */ diff --git a/third_party/curl/include/curl/websockets.h b/third_party/curl/include/curl/websockets.h new file mode 100644 index 0000000000..6ef6a2bc92 --- /dev/null +++ b/third_party/curl/include/curl/websockets.h @@ -0,0 +1,84 @@ +#ifndef CURLINC_WEBSOCKETS_H +#define CURLINC_WEBSOCKETS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +struct curl_ws_frame { + int age; /* zero */ + int flags; /* See the CURLWS_* defines */ + curl_off_t offset; /* the offset of this data into the frame */ + curl_off_t bytesleft; /* number of pending bytes left of the payload */ + size_t len; /* size of the current data chunk */ +}; + +/* flag bits */ +#define CURLWS_TEXT (1<<0) +#define CURLWS_BINARY (1<<1) +#define CURLWS_CONT (1<<2) +#define CURLWS_CLOSE (1<<3) +#define CURLWS_PING (1<<4) +#define CURLWS_OFFSET (1<<5) + +/* + * NAME curl_ws_recv() + * + * DESCRIPTION + * + * Receives data from the websocket connection. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, + size_t *recv, + const struct curl_ws_frame **metap); + +/* flags for curl_ws_send() */ +#define CURLWS_PONG (1<<6) + +/* + * NAME curl_ws_send() + * + * DESCRIPTION + * + * Sends data over the websocket connection. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, + size_t buflen, size_t *sent, + curl_off_t fragsize, + unsigned int flags); + +/* bits for the CURLOPT_WS_OPTIONS bitmask: */ +#define CURLWS_RAW_MODE (1<<0) + +CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl); + +#ifdef __cplusplus +} +#endif + +#endif /* CURLINC_WEBSOCKETS_H */ diff --git a/third_party/curl/lib/.checksrc b/third_party/curl/lib/.checksrc new file mode 100644 index 0000000000..16133a44c5 --- /dev/null +++ b/third_party/curl/lib/.checksrc @@ -0,0 +1 @@ +enable STRERROR diff --git a/third_party/curl/lib/CMakeLists.txt b/third_party/curl/lib/CMakeLists.txt new file mode 100644 index 0000000000..63e5b91390 --- /dev/null +++ b/third_party/curl/lib/CMakeLists.txt @@ -0,0 +1,250 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +set(LIB_NAME libcurl) +set(LIBCURL_OUTPUT_NAME libcurl CACHE STRING "Basename of the curl library") +add_definitions(-DBUILDING_LIBCURL) + +configure_file(curl_config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h) + +transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") +include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake) + +# DllMain is added later for DLL builds only. +list(REMOVE_ITEM CSOURCES dllmain.c) + +list(APPEND HHEADERS ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h) + +# The rest of the build + +include_directories(${CMAKE_CURRENT_BINARY_DIR}/../include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/..) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +if(USE_ARES) + include_directories(${CARES_INCLUDE_DIR}) +endif() + +if(BUILD_TESTING) + add_library( + curlu # special libcurlu library just for unittests + STATIC + EXCLUDE_FROM_ALL + ${HHEADERS} ${CSOURCES} + ) + target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB) + target_link_libraries(curlu PRIVATE ${CURL_LIBS}) +endif() + +if(ENABLE_CURLDEBUG) + # We must compile these sources separately to avoid memdebug.h redefinitions + # applying to them. + set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) +endif() + +## Library definition + +# Add "_imp" as a suffix before the extension to avoid conflicting with +# the statically linked "libcurl.lib" (typically with MSVC) +if(WIN32 AND + NOT IMPORT_LIB_SUFFIX AND + CMAKE_STATIC_LIBRARY_SUFFIX STREQUAL CMAKE_IMPORT_LIBRARY_SUFFIX) + set(IMPORT_LIB_SUFFIX "_imp") +endif() + +# Whether to do a single compilation pass for libcurl sources and reuse these +# objects to generate both static and shared target. +if(NOT DEFINED SHARE_LIB_OBJECT) + # Enable it by default on platforms where PIC is the default for both shared + # and static and there is a way to tell the linker which libcurl symbols it + # should export (vs. marking these symbols exportable at compile-time). + if(WIN32) + set(SHARE_LIB_OBJECT ON) + else() + # On other platforms, make it an option disabled by default + set(SHARE_LIB_OBJECT OFF) + endif() +endif() + +if(SHARE_LIB_OBJECT) + set(LIB_OBJECT "libcurl_object") + add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) + if(WIN32) + # Define CURL_STATICLIB always, to disable __declspec(dllexport) for + # exported libcurl symbols. We handle exports via libcurl.def instead. + # Except with symbol hiding disabled or debug mode enabled, when we export + # _all_ symbols from libcurl DLL, without using libcurl.def. + set_property(TARGET ${LIB_OBJECT} APPEND + PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") + endif() + target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS}) + set_target_properties(${LIB_OBJECT} PROPERTIES + POSITION_INDEPENDENT_CODE ON) + if(HIDES_CURL_PRIVATE_SYMBOLS) + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") + endif() + if(CURL_HAS_LTO) + set_target_properties(${LIB_OBJECT} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE + INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) + endif() + + target_include_directories(${LIB_OBJECT} INTERFACE + $ + $) + + set(LIB_SOURCE $) +else() + set(LIB_SOURCE ${HHEADERS} ${CSOURCES}) +endif() + +# we want it to be called libcurl on all platforms +if(BUILD_STATIC_LIBS) + list(APPEND libcurl_export ${LIB_STATIC}) + add_library(${LIB_STATIC} STATIC ${LIB_SOURCE}) + add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC}) + if(WIN32) + set_property(TARGET ${LIB_OBJECT} APPEND + PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") + endif() + target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS}) + # Remove the "lib" prefix since the library is already named "libcurl". + set_target_properties(${LIB_STATIC} PROPERTIES + PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" + SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}" + INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB") + if(HIDES_CURL_PRIVATE_SYMBOLS) + set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") + set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") + endif() + if(CURL_HAS_LTO) + set_target_properties(${LIB_STATIC} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE + INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) + endif() + + target_include_directories(${LIB_STATIC} INTERFACE + $ + $) +endif() + +if(BUILD_SHARED_LIBS) + list(APPEND libcurl_export ${LIB_SHARED}) + add_library(${LIB_SHARED} SHARED ${LIB_SOURCE}) + add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED}) + if(WIN32 OR CYGWIN) + if(CYGWIN) + # For Cygwin always compile dllmain.c as a separate unit since it + # includes windows.h, which should not be included in other units. + set_source_files_properties(dllmain.c PROPERTIES + SKIP_UNITY_BUILD_INCLUSION ON) + endif() + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES dllmain.c) + endif() + if(WIN32) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES libcurl.rc) + if(HIDES_CURL_PRIVATE_SYMBOLS) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${CURL_SOURCE_DIR}/libcurl.def") + endif() + endif() + target_link_libraries(${LIB_SHARED} PRIVATE ${CURL_LIBS}) + # Remove the "lib" prefix since the library is already named "libcurl". + set_target_properties(${LIB_SHARED} PROPERTIES + PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" + IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" + POSITION_INDEPENDENT_CODE ON) + if(HIDES_CURL_PRIVATE_SYMBOLS) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") + endif() + if(CURL_HAS_LTO) + set_target_properties(${LIB_SHARED} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE + INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) + endif() + + target_include_directories(${LIB_SHARED} INTERFACE + $ + $) + + if(CMAKE_DLL_NAME_WITH_SOVERSION OR + CYGWIN OR + APPLE OR + CMAKE_SYSTEM_NAME STREQUAL "AIX" OR + CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR + CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR + CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR + # FreeBSD comes with the a.out and ELF flavours but a.out was supported + # up to v3.x and ELF from v3.x. I cannot imagine someone running CMake + # on those ancient systems. + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(soversion_default TRUE) + else() + set(soversion_default FALSE) + endif() + + option(CURL_LIBCURL_SOVERSION "Enable libcurl SOVERSION" ${soversion_default}) + + if(CURL_LIBCURL_SOVERSION) + transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") + include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake) + + math(EXPR CMAKESONAME "${VERSIONCHANGE} - ${VERSIONDEL}") + set(CMAKEVERSION "${CMAKESONAME}.${VERSIONDEL}.${VERSIONADD}") + + set_target_properties(${LIB_SHARED} PROPERTIES + VERSION "${CMAKEVERSION}" SOVERSION "${CMAKESONAME}") + endif() +endif() + +add_library(${LIB_NAME} ALIAS ${LIB_SELECTED}) +add_library(${PROJECT_NAME}::${LIB_NAME} ALIAS ${LIB_SELECTED}) + +if(CURL_ENABLE_EXPORT_TARGET) + if(BUILD_STATIC_LIBS) + install(TARGETS ${LIB_STATIC} + EXPORT ${TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + endif() + if(BUILD_SHARED_LIBS) + install(TARGETS ${LIB_SHARED} + EXPORT ${TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + endif() + + export(TARGETS ${libcurl_export} + FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake + NAMESPACE ${PROJECT_NAME}:: + ) +endif() diff --git a/third_party/curl/lib/Makefile.am b/third_party/curl/lib/Makefile.am new file mode 100644 index 0000000000..413fcd4d55 --- /dev/null +++ b/third_party/curl/lib/Makefile.am @@ -0,0 +1,148 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +AUTOMAKE_OPTIONS = foreign nostdinc + +CMAKE_DIST = CMakeLists.txt curl_config.h.cmake + +EXTRA_DIST = Makefile.mk config-win32.h config-win32ce.h config-plan9.h \ + config-riscos.h config-mac.h curl_config.h.in config-dos.h libcurl.rc \ + config-amigaos.h config-win32ce.h config-os400.h setup-os400.h \ + $(CMAKE_DIST) setup-win32.h .checksrc Makefile.soname + +lib_LTLIBRARIES = libcurl.la + +if BUILD_UNITTESTS +noinst_LTLIBRARIES = libcurlu.la +else +noinst_LTLIBRARIES = +endif + +# This might hold -Werror +CFLAGS += @CURL_CFLAG_EXTRAS@ + +# Specify our include paths here, and do it relative to $(top_srcdir) and +# $(top_builddir), to ensure that these paths which belong to the library +# being currently built and tested are searched before the library which +# might possibly already be installed in the system. +# +# $(top_srcdir)/include is for libcurl's external include files +# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file +# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "private" files + +AM_CPPFLAGS = -I$(top_srcdir)/include \ + -I$(top_builddir)/lib \ + -I$(top_srcdir)/lib + +# Prevent LIBS from being used for all link targets +LIBS = $(BLANK_AT_MAKETIME) + +include Makefile.soname + +AM_CPPFLAGS += -DBUILDING_LIBCURL +AM_LDFLAGS = +AM_CFLAGS = + +# Makefile.inc provides the CSOURCES and HHEADERS defines +include Makefile.inc + +libcurl_la_SOURCES = $(CSOURCES) $(HHEADERS) +libcurlu_la_SOURCES = $(CSOURCES) $(HHEADERS) + +libcurl_la_CPPFLAGS_EXTRA = +libcurl_la_LDFLAGS_EXTRA = +libcurl_la_CFLAGS_EXTRA = + +if CURL_LT_SHLIB_USE_VERSION_INFO +libcurl_la_LDFLAGS_EXTRA += $(VERSIONINFO) +endif + +if CURL_LT_SHLIB_USE_NO_UNDEFINED +libcurl_la_LDFLAGS_EXTRA += -no-undefined +endif + +if CURL_LT_SHLIB_USE_MIMPURE_TEXT +libcurl_la_LDFLAGS_EXTRA += -mimpure-text +endif + +if CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS +libcurl_la_LDFLAGS_EXTRA += -Wl,--version-script=libcurl.vers +else +# if symbol-hiding is enabled, hide them! +if DOING_CURL_SYMBOL_HIDING +libcurl_la_LDFLAGS_EXTRA += -export-symbols-regex '^curl_.*' +endif +endif + +if USE_CPPFLAG_CURL_STATICLIB +libcurl_la_CPPFLAGS_EXTRA += -DCURL_STATICLIB +else +if HAVE_WINDRES +libcurl_la_SOURCES += $(LIB_RCFILES) +$(LIB_RCFILES): $(top_srcdir)/include/curl/curlver.h +endif +endif + +if DOING_CURL_SYMBOL_HIDING +libcurl_la_CPPFLAGS_EXTRA += -DCURL_HIDDEN_SYMBOLS +libcurl_la_CFLAGS_EXTRA += $(CFLAG_CURL_SYMBOL_HIDING) +endif + +libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA) +libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(LIBCURL_LIBS) +libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA) + +libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS +libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_LIBS) +libcurlu_la_CFLAGS = $(AM_CFLAGS) + +CHECKSRC = $(CS_$(V)) +CS_0 = @echo " RUN " $@; +CS_1 = +CS_ = $(CS_0) + +checksrc: + $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \ + -W$(srcdir)/curl_config.h $(srcdir)/*.[ch] $(srcdir)/vauth/*.[ch] \ + $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch] $(srcdir)/vssh/*.[ch]) + +if DEBUGBUILD +# for debug builds, we scan the sources on all regular make invokes +all-local: checksrc +endif + +# disable the tests that are mostly causing false positives +TIDYFLAGS=-checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-valist.Uninitialized,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference -quiet + +TIDY:=clang-tidy + +tidy: + $(TIDY) $(CSOURCES) $(TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H + +optiontable: + perl optiontable.pl < $(top_srcdir)/include/curl/curl.h > easyoptions.c + +if HAVE_WINDRES +.rc.lo: + $(LIBTOOL) --tag=RC --mode=compile $(RC) -I$(top_srcdir)/include $(RCFLAGS) -i $< -o $@ +endif diff --git a/third_party/curl/lib/Makefile.in b/third_party/curl/lib/Makefile.in new file mode 100644 index 0000000000..e0a7c4f94c --- /dev/null +++ b/third_party/curl/lib/Makefile.in @@ -0,0 +1,5524 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@CURL_LT_SHLIB_USE_VERSION_INFO_TRUE@am__append_1 = $(VERSIONINFO) +@CURL_LT_SHLIB_USE_NO_UNDEFINED_TRUE@am__append_2 = -no-undefined +@CURL_LT_SHLIB_USE_MIMPURE_TEXT_TRUE@am__append_3 = -mimpure-text +@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_TRUE@am__append_4 = -Wl,--version-script=libcurl.vers +# if symbol-hiding is enabled, hide them! +@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_FALSE@@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_5 = -export-symbols-regex '^curl_.*' +@USE_CPPFLAG_CURL_STATICLIB_TRUE@am__append_6 = -DCURL_STATICLIB +@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__append_7 = $(LIB_RCFILES) +@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_8 = -DCURL_HIDDEN_SYMBOLS +@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_9 = $(CFLAG_CURL_SYMBOL_HIDING) +subdir = lib +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/curl-amissl.m4 \ + $(top_srcdir)/m4/curl-bearssl.m4 \ + $(top_srcdir)/m4/curl-compilers.m4 \ + $(top_srcdir)/m4/curl-confopts.m4 \ + $(top_srcdir)/m4/curl-functions.m4 \ + $(top_srcdir)/m4/curl-gnutls.m4 \ + $(top_srcdir)/m4/curl-mbedtls.m4 \ + $(top_srcdir)/m4/curl-openssl.m4 \ + $(top_srcdir)/m4/curl-override.m4 \ + $(top_srcdir)/m4/curl-reentrant.m4 \ + $(top_srcdir)/m4/curl-rustls.m4 \ + $(top_srcdir)/m4/curl-schannel.m4 \ + $(top_srcdir)/m4/curl-sectransp.m4 \ + $(top_srcdir)/m4/curl-sysconfig.m4 \ + $(top_srcdir)/m4/curl-wolfssl.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/xc-am-iface.m4 \ + $(top_srcdir)/m4/xc-cc-check.m4 \ + $(top_srcdir)/m4/xc-lt-iface.m4 \ + $(top_srcdir)/m4/xc-val-flgs.m4 \ + $(top_srcdir)/m4/zz40-xc-ovr.m4 \ + $(top_srcdir)/m4/zz50-xc-ovr.m4 \ + $(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = curl_config.h +CONFIG_CLEAN_FILES = libcurl.vers +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +libcurl_la_LIBADD = +am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \ + asyn-thread.c base64.c bufq.c bufref.c c-hyper.c cf-h1-proxy.c \ + cf-h2-proxy.c cf-haproxy.c cf-https-connect.c cf-socket.c \ + cfilters.c conncache.c connect.c content_encoding.c cookie.c \ + curl_addrinfo.c curl_des.c curl_endian.c curl_fnmatch.c \ + curl_get_line.c curl_gethostname.c curl_gssapi.c \ + curl_memrchr.c curl_multibyte.c curl_ntlm_core.c curl_path.c \ + curl_range.c curl_rtmp.c curl_sasl.c curl_sha512_256.c \ + curl_sspi.c curl_threads.c curl_trc.c cw-out.c dict.c \ + dllmain.c doh.c dynbuf.c dynhds.c easy.c easygetopt.c \ + easyoptions.c escape.c file.c fileinfo.c fopen.c formdata.c \ + ftp.c ftplistparser.c getenv.c getinfo.c gopher.c hash.c \ + headers.c hmac.c hostasyn.c hostip.c hostip4.c hostip6.c \ + hostsyn.c hsts.c http.c http1.c http2.c http_aws_sigv4.c \ + http_chunks.c http_digest.c http_negotiate.c http_ntlm.c \ + http_proxy.c idn.c if2ip.c imap.c inet_ntop.c inet_pton.c \ + krb5.c ldap.c llist.c macos.c md4.c md5.c memdebug.c mime.c \ + mprintf.c mqtt.c multi.c netrc.c nonblock.c noproxy.c \ + openldap.c parsedate.c pingpong.c pop3.c progress.c psl.c \ + rand.c rename.c request.c rtsp.c select.c sendf.c setopt.c \ + sha256.c share.c slist.c smb.c smtp.c socketpair.c socks.c \ + socks_gssapi.c socks_sspi.c speedcheck.c splay.c strcase.c \ + strdup.c strerror.c strtok.c strtoofft.c system_win32.c \ + telnet.c tftp.c timediff.c timeval.c transfer.c url.c urlapi.c \ + version.c version_win32.c warnless.c ws.c vauth/cleartext.c \ + vauth/cram.c vauth/digest.c vauth/digest_sspi.c vauth/gsasl.c \ + vauth/krb5_gssapi.c vauth/krb5_sspi.c vauth/ntlm.c \ + vauth/ntlm_sspi.c vauth/oauth2.c vauth/spnego_gssapi.c \ + vauth/spnego_sspi.c vauth/vauth.c vtls/bearssl.c \ + vtls/cipher_suite.c vtls/gtls.c vtls/hostcheck.c vtls/keylog.c \ + vtls/mbedtls.c vtls/mbedtls_threadlock.c vtls/openssl.c \ + vtls/rustls.c vtls/schannel.c vtls/schannel_verify.c \ + vtls/sectransp.c vtls/vtls.c vtls/wolfssl.c vtls/x509asn1.c \ + vquic/curl_msh3.c vquic/curl_ngtcp2.c vquic/curl_osslq.c \ + vquic/curl_quiche.c vquic/vquic.c vquic/vquic-tls.c \ + vssh/libssh.c vssh/libssh2.c vssh/wolfssh.c altsvc.h amigaos.h \ + arpa_telnet.h asyn.h bufq.h bufref.h c-hyper.h cf-h1-proxy.h \ + cf-h2-proxy.h cf-haproxy.h cf-https-connect.h cf-socket.h \ + cfilters.h conncache.h connect.h content_encoding.h cookie.h \ + curl_addrinfo.h curl_base64.h curl_ctype.h curl_des.h \ + curl_endian.h curl_fnmatch.h curl_get_line.h \ + curl_gethostname.h curl_gssapi.h curl_hmac.h curl_krb5.h \ + curl_ldap.h curl_md4.h curl_md5.h curl_memory.h curl_memrchr.h \ + curl_multibyte.h curl_ntlm_core.h curl_path.h curl_printf.h \ + curl_range.h curl_rtmp.h curl_sasl.h curl_setup.h \ + curl_setup_once.h curl_sha256.h curl_sha512_256.h curl_sspi.h \ + curl_threads.h curl_trc.h curlx.h cw-out.h dict.h doh.h \ + dynbuf.h dynhds.h easy_lock.h easyif.h easyoptions.h escape.h \ + file.h fileinfo.h fopen.h formdata.h ftp.h ftplistparser.h \ + functypes.h getinfo.h gopher.h hash.h headers.h hostip.h \ + hsts.h http.h http1.h http2.h http_aws_sigv4.h http_chunks.h \ + http_digest.h http_negotiate.h http_ntlm.h http_proxy.h idn.h \ + if2ip.h imap.h inet_ntop.h inet_pton.h llist.h macos.h \ + memdebug.h mime.h mqtt.h multihandle.h multiif.h netrc.h \ + nonblock.h noproxy.h parsedate.h pingpong.h pop3.h progress.h \ + psl.h rand.h rename.h request.h rtsp.h select.h sendf.h \ + setopt.h setup-vms.h share.h sigpipe.h slist.h smb.h smtp.h \ + sockaddr.h socketpair.h socks.h speedcheck.h splay.h strcase.h \ + strdup.h strerror.h strtok.h strtoofft.h system_win32.h \ + telnet.h tftp.h timediff.h timeval.h transfer.h url.h \ + urlapi-int.h urldata.h version_win32.h warnless.h ws.h \ + vauth/digest.h vauth/ntlm.h vauth/vauth.h vtls/bearssl.h \ + vtls/cipher_suite.h vtls/gtls.h vtls/hostcheck.h vtls/keylog.h \ + vtls/mbedtls.h vtls/mbedtls_threadlock.h vtls/openssl.h \ + vtls/rustls.h vtls/schannel.h vtls/schannel_int.h \ + vtls/sectransp.h vtls/vtls.h vtls/vtls_int.h vtls/wolfssl.h \ + vtls/x509asn1.h vquic/curl_msh3.h vquic/curl_ngtcp2.h \ + vquic/curl_osslq.h vquic/curl_quiche.h vquic/vquic.h \ + vquic/vquic_int.h vquic/vquic-tls.h vssh/ssh.h libcurl.rc +am__objects_1 = libcurl_la-altsvc.lo libcurl_la-amigaos.lo \ + libcurl_la-asyn-ares.lo libcurl_la-asyn-thread.lo \ + libcurl_la-base64.lo libcurl_la-bufq.lo libcurl_la-bufref.lo \ + libcurl_la-c-hyper.lo libcurl_la-cf-h1-proxy.lo \ + libcurl_la-cf-h2-proxy.lo libcurl_la-cf-haproxy.lo \ + libcurl_la-cf-https-connect.lo libcurl_la-cf-socket.lo \ + libcurl_la-cfilters.lo libcurl_la-conncache.lo \ + libcurl_la-connect.lo libcurl_la-content_encoding.lo \ + libcurl_la-cookie.lo libcurl_la-curl_addrinfo.lo \ + libcurl_la-curl_des.lo libcurl_la-curl_endian.lo \ + libcurl_la-curl_fnmatch.lo libcurl_la-curl_get_line.lo \ + libcurl_la-curl_gethostname.lo libcurl_la-curl_gssapi.lo \ + libcurl_la-curl_memrchr.lo libcurl_la-curl_multibyte.lo \ + libcurl_la-curl_ntlm_core.lo libcurl_la-curl_path.lo \ + libcurl_la-curl_range.lo libcurl_la-curl_rtmp.lo \ + libcurl_la-curl_sasl.lo libcurl_la-curl_sha512_256.lo \ + libcurl_la-curl_sspi.lo libcurl_la-curl_threads.lo \ + libcurl_la-curl_trc.lo libcurl_la-cw-out.lo libcurl_la-dict.lo \ + libcurl_la-dllmain.lo libcurl_la-doh.lo libcurl_la-dynbuf.lo \ + libcurl_la-dynhds.lo libcurl_la-easy.lo \ + libcurl_la-easygetopt.lo libcurl_la-easyoptions.lo \ + libcurl_la-escape.lo libcurl_la-file.lo libcurl_la-fileinfo.lo \ + libcurl_la-fopen.lo libcurl_la-formdata.lo libcurl_la-ftp.lo \ + libcurl_la-ftplistparser.lo libcurl_la-getenv.lo \ + libcurl_la-getinfo.lo libcurl_la-gopher.lo libcurl_la-hash.lo \ + libcurl_la-headers.lo libcurl_la-hmac.lo \ + libcurl_la-hostasyn.lo libcurl_la-hostip.lo \ + libcurl_la-hostip4.lo libcurl_la-hostip6.lo \ + libcurl_la-hostsyn.lo libcurl_la-hsts.lo libcurl_la-http.lo \ + libcurl_la-http1.lo libcurl_la-http2.lo \ + libcurl_la-http_aws_sigv4.lo libcurl_la-http_chunks.lo \ + libcurl_la-http_digest.lo libcurl_la-http_negotiate.lo \ + libcurl_la-http_ntlm.lo libcurl_la-http_proxy.lo \ + libcurl_la-idn.lo libcurl_la-if2ip.lo libcurl_la-imap.lo \ + libcurl_la-inet_ntop.lo libcurl_la-inet_pton.lo \ + libcurl_la-krb5.lo libcurl_la-ldap.lo libcurl_la-llist.lo \ + libcurl_la-macos.lo libcurl_la-md4.lo libcurl_la-md5.lo \ + libcurl_la-memdebug.lo libcurl_la-mime.lo \ + libcurl_la-mprintf.lo libcurl_la-mqtt.lo libcurl_la-multi.lo \ + libcurl_la-netrc.lo libcurl_la-nonblock.lo \ + libcurl_la-noproxy.lo libcurl_la-openldap.lo \ + libcurl_la-parsedate.lo libcurl_la-pingpong.lo \ + libcurl_la-pop3.lo libcurl_la-progress.lo libcurl_la-psl.lo \ + libcurl_la-rand.lo libcurl_la-rename.lo libcurl_la-request.lo \ + libcurl_la-rtsp.lo libcurl_la-select.lo libcurl_la-sendf.lo \ + libcurl_la-setopt.lo libcurl_la-sha256.lo libcurl_la-share.lo \ + libcurl_la-slist.lo libcurl_la-smb.lo libcurl_la-smtp.lo \ + libcurl_la-socketpair.lo libcurl_la-socks.lo \ + libcurl_la-socks_gssapi.lo libcurl_la-socks_sspi.lo \ + libcurl_la-speedcheck.lo libcurl_la-splay.lo \ + libcurl_la-strcase.lo libcurl_la-strdup.lo \ + libcurl_la-strerror.lo libcurl_la-strtok.lo \ + libcurl_la-strtoofft.lo libcurl_la-system_win32.lo \ + libcurl_la-telnet.lo libcurl_la-tftp.lo libcurl_la-timediff.lo \ + libcurl_la-timeval.lo libcurl_la-transfer.lo libcurl_la-url.lo \ + libcurl_la-urlapi.lo libcurl_la-version.lo \ + libcurl_la-version_win32.lo libcurl_la-warnless.lo \ + libcurl_la-ws.lo +am__dirstamp = $(am__leading_dot)dirstamp +am__objects_2 = vauth/libcurl_la-cleartext.lo vauth/libcurl_la-cram.lo \ + vauth/libcurl_la-digest.lo vauth/libcurl_la-digest_sspi.lo \ + vauth/libcurl_la-gsasl.lo vauth/libcurl_la-krb5_gssapi.lo \ + vauth/libcurl_la-krb5_sspi.lo vauth/libcurl_la-ntlm.lo \ + vauth/libcurl_la-ntlm_sspi.lo vauth/libcurl_la-oauth2.lo \ + vauth/libcurl_la-spnego_gssapi.lo \ + vauth/libcurl_la-spnego_sspi.lo vauth/libcurl_la-vauth.lo +am__objects_3 = vtls/libcurl_la-bearssl.lo \ + vtls/libcurl_la-cipher_suite.lo vtls/libcurl_la-gtls.lo \ + vtls/libcurl_la-hostcheck.lo vtls/libcurl_la-keylog.lo \ + vtls/libcurl_la-mbedtls.lo \ + vtls/libcurl_la-mbedtls_threadlock.lo \ + vtls/libcurl_la-openssl.lo vtls/libcurl_la-rustls.lo \ + vtls/libcurl_la-schannel.lo vtls/libcurl_la-schannel_verify.lo \ + vtls/libcurl_la-sectransp.lo vtls/libcurl_la-vtls.lo \ + vtls/libcurl_la-wolfssl.lo vtls/libcurl_la-x509asn1.lo +am__objects_4 = vquic/libcurl_la-curl_msh3.lo \ + vquic/libcurl_la-curl_ngtcp2.lo vquic/libcurl_la-curl_osslq.lo \ + vquic/libcurl_la-curl_quiche.lo vquic/libcurl_la-vquic.lo \ + vquic/libcurl_la-vquic-tls.lo +am__objects_5 = vssh/libcurl_la-libssh.lo vssh/libcurl_la-libssh2.lo \ + vssh/libcurl_la-wolfssh.lo +am__objects_6 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) $(am__objects_5) +am__objects_7 = +am__objects_8 = $(am__objects_7) $(am__objects_7) $(am__objects_7) \ + $(am__objects_7) $(am__objects_7) +am__objects_9 = libcurl.lo +@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__objects_10 = $(am__objects_9) +am_libcurl_la_OBJECTS = $(am__objects_6) $(am__objects_8) \ + $(am__objects_10) +libcurl_la_OBJECTS = $(am_libcurl_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libcurl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libcurl_la_CFLAGS) \ + $(CFLAGS) $(libcurl_la_LDFLAGS) $(LDFLAGS) -o $@ +libcurlu_la_LIBADD = +am__objects_11 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \ + libcurlu_la-asyn-ares.lo libcurlu_la-asyn-thread.lo \ + libcurlu_la-base64.lo libcurlu_la-bufq.lo \ + libcurlu_la-bufref.lo libcurlu_la-c-hyper.lo \ + libcurlu_la-cf-h1-proxy.lo libcurlu_la-cf-h2-proxy.lo \ + libcurlu_la-cf-haproxy.lo libcurlu_la-cf-https-connect.lo \ + libcurlu_la-cf-socket.lo libcurlu_la-cfilters.lo \ + libcurlu_la-conncache.lo libcurlu_la-connect.lo \ + libcurlu_la-content_encoding.lo libcurlu_la-cookie.lo \ + libcurlu_la-curl_addrinfo.lo libcurlu_la-curl_des.lo \ + libcurlu_la-curl_endian.lo libcurlu_la-curl_fnmatch.lo \ + libcurlu_la-curl_get_line.lo libcurlu_la-curl_gethostname.lo \ + libcurlu_la-curl_gssapi.lo libcurlu_la-curl_memrchr.lo \ + libcurlu_la-curl_multibyte.lo libcurlu_la-curl_ntlm_core.lo \ + libcurlu_la-curl_path.lo libcurlu_la-curl_range.lo \ + libcurlu_la-curl_rtmp.lo libcurlu_la-curl_sasl.lo \ + libcurlu_la-curl_sha512_256.lo libcurlu_la-curl_sspi.lo \ + libcurlu_la-curl_threads.lo libcurlu_la-curl_trc.lo \ + libcurlu_la-cw-out.lo libcurlu_la-dict.lo \ + libcurlu_la-dllmain.lo libcurlu_la-doh.lo \ + libcurlu_la-dynbuf.lo libcurlu_la-dynhds.lo \ + libcurlu_la-easy.lo libcurlu_la-easygetopt.lo \ + libcurlu_la-easyoptions.lo libcurlu_la-escape.lo \ + libcurlu_la-file.lo libcurlu_la-fileinfo.lo \ + libcurlu_la-fopen.lo libcurlu_la-formdata.lo \ + libcurlu_la-ftp.lo libcurlu_la-ftplistparser.lo \ + libcurlu_la-getenv.lo libcurlu_la-getinfo.lo \ + libcurlu_la-gopher.lo libcurlu_la-hash.lo \ + libcurlu_la-headers.lo libcurlu_la-hmac.lo \ + libcurlu_la-hostasyn.lo libcurlu_la-hostip.lo \ + libcurlu_la-hostip4.lo libcurlu_la-hostip6.lo \ + libcurlu_la-hostsyn.lo libcurlu_la-hsts.lo libcurlu_la-http.lo \ + libcurlu_la-http1.lo libcurlu_la-http2.lo \ + libcurlu_la-http_aws_sigv4.lo libcurlu_la-http_chunks.lo \ + libcurlu_la-http_digest.lo libcurlu_la-http_negotiate.lo \ + libcurlu_la-http_ntlm.lo libcurlu_la-http_proxy.lo \ + libcurlu_la-idn.lo libcurlu_la-if2ip.lo libcurlu_la-imap.lo \ + libcurlu_la-inet_ntop.lo libcurlu_la-inet_pton.lo \ + libcurlu_la-krb5.lo libcurlu_la-ldap.lo libcurlu_la-llist.lo \ + libcurlu_la-macos.lo libcurlu_la-md4.lo libcurlu_la-md5.lo \ + libcurlu_la-memdebug.lo libcurlu_la-mime.lo \ + libcurlu_la-mprintf.lo libcurlu_la-mqtt.lo \ + libcurlu_la-multi.lo libcurlu_la-netrc.lo \ + libcurlu_la-nonblock.lo libcurlu_la-noproxy.lo \ + libcurlu_la-openldap.lo libcurlu_la-parsedate.lo \ + libcurlu_la-pingpong.lo libcurlu_la-pop3.lo \ + libcurlu_la-progress.lo libcurlu_la-psl.lo libcurlu_la-rand.lo \ + libcurlu_la-rename.lo libcurlu_la-request.lo \ + libcurlu_la-rtsp.lo libcurlu_la-select.lo libcurlu_la-sendf.lo \ + libcurlu_la-setopt.lo libcurlu_la-sha256.lo \ + libcurlu_la-share.lo libcurlu_la-slist.lo libcurlu_la-smb.lo \ + libcurlu_la-smtp.lo libcurlu_la-socketpair.lo \ + libcurlu_la-socks.lo libcurlu_la-socks_gssapi.lo \ + libcurlu_la-socks_sspi.lo libcurlu_la-speedcheck.lo \ + libcurlu_la-splay.lo libcurlu_la-strcase.lo \ + libcurlu_la-strdup.lo libcurlu_la-strerror.lo \ + libcurlu_la-strtok.lo libcurlu_la-strtoofft.lo \ + libcurlu_la-system_win32.lo libcurlu_la-telnet.lo \ + libcurlu_la-tftp.lo libcurlu_la-timediff.lo \ + libcurlu_la-timeval.lo libcurlu_la-transfer.lo \ + libcurlu_la-url.lo libcurlu_la-urlapi.lo \ + libcurlu_la-version.lo libcurlu_la-version_win32.lo \ + libcurlu_la-warnless.lo libcurlu_la-ws.lo +am__objects_12 = vauth/libcurlu_la-cleartext.lo \ + vauth/libcurlu_la-cram.lo vauth/libcurlu_la-digest.lo \ + vauth/libcurlu_la-digest_sspi.lo vauth/libcurlu_la-gsasl.lo \ + vauth/libcurlu_la-krb5_gssapi.lo \ + vauth/libcurlu_la-krb5_sspi.lo vauth/libcurlu_la-ntlm.lo \ + vauth/libcurlu_la-ntlm_sspi.lo vauth/libcurlu_la-oauth2.lo \ + vauth/libcurlu_la-spnego_gssapi.lo \ + vauth/libcurlu_la-spnego_sspi.lo vauth/libcurlu_la-vauth.lo +am__objects_13 = vtls/libcurlu_la-bearssl.lo \ + vtls/libcurlu_la-cipher_suite.lo vtls/libcurlu_la-gtls.lo \ + vtls/libcurlu_la-hostcheck.lo vtls/libcurlu_la-keylog.lo \ + vtls/libcurlu_la-mbedtls.lo \ + vtls/libcurlu_la-mbedtls_threadlock.lo \ + vtls/libcurlu_la-openssl.lo vtls/libcurlu_la-rustls.lo \ + vtls/libcurlu_la-schannel.lo \ + vtls/libcurlu_la-schannel_verify.lo \ + vtls/libcurlu_la-sectransp.lo vtls/libcurlu_la-vtls.lo \ + vtls/libcurlu_la-wolfssl.lo vtls/libcurlu_la-x509asn1.lo +am__objects_14 = vquic/libcurlu_la-curl_msh3.lo \ + vquic/libcurlu_la-curl_ngtcp2.lo \ + vquic/libcurlu_la-curl_osslq.lo \ + vquic/libcurlu_la-curl_quiche.lo vquic/libcurlu_la-vquic.lo \ + vquic/libcurlu_la-vquic-tls.lo +am__objects_15 = vssh/libcurlu_la-libssh.lo \ + vssh/libcurlu_la-libssh2.lo vssh/libcurlu_la-wolfssh.lo +am__objects_16 = $(am__objects_11) $(am__objects_12) $(am__objects_13) \ + $(am__objects_14) $(am__objects_15) +am_libcurlu_la_OBJECTS = $(am__objects_16) $(am__objects_8) +libcurlu_la_OBJECTS = $(am_libcurlu_la_OBJECTS) +libcurlu_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libcurlu_la_CFLAGS) \ + $(CFLAGS) $(libcurlu_la_LDFLAGS) $(LDFLAGS) -o $@ +@BUILD_UNITTESTS_TRUE@am_libcurlu_la_rpath = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \ + ./$(DEPDIR)/libcurl_la-amigaos.Plo \ + ./$(DEPDIR)/libcurl_la-asyn-ares.Plo \ + ./$(DEPDIR)/libcurl_la-asyn-thread.Plo \ + ./$(DEPDIR)/libcurl_la-base64.Plo \ + ./$(DEPDIR)/libcurl_la-bufq.Plo \ + ./$(DEPDIR)/libcurl_la-bufref.Plo \ + ./$(DEPDIR)/libcurl_la-c-hyper.Plo \ + ./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo \ + ./$(DEPDIR)/libcurl_la-cf-h2-proxy.Plo \ + ./$(DEPDIR)/libcurl_la-cf-haproxy.Plo \ + ./$(DEPDIR)/libcurl_la-cf-https-connect.Plo \ + ./$(DEPDIR)/libcurl_la-cf-socket.Plo \ + ./$(DEPDIR)/libcurl_la-cfilters.Plo \ + ./$(DEPDIR)/libcurl_la-conncache.Plo \ + ./$(DEPDIR)/libcurl_la-connect.Plo \ + ./$(DEPDIR)/libcurl_la-content_encoding.Plo \ + ./$(DEPDIR)/libcurl_la-cookie.Plo \ + ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo \ + ./$(DEPDIR)/libcurl_la-curl_des.Plo \ + ./$(DEPDIR)/libcurl_la-curl_endian.Plo \ + ./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo \ + ./$(DEPDIR)/libcurl_la-curl_get_line.Plo \ + ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo \ + ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo \ + ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo \ + ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo \ + ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo \ + ./$(DEPDIR)/libcurl_la-curl_path.Plo \ + ./$(DEPDIR)/libcurl_la-curl_range.Plo \ + ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo \ + ./$(DEPDIR)/libcurl_la-curl_sasl.Plo \ + ./$(DEPDIR)/libcurl_la-curl_sha512_256.Plo \ + ./$(DEPDIR)/libcurl_la-curl_sspi.Plo \ + ./$(DEPDIR)/libcurl_la-curl_threads.Plo \ + ./$(DEPDIR)/libcurl_la-curl_trc.Plo \ + ./$(DEPDIR)/libcurl_la-cw-out.Plo \ + ./$(DEPDIR)/libcurl_la-dict.Plo \ + ./$(DEPDIR)/libcurl_la-dllmain.Plo \ + ./$(DEPDIR)/libcurl_la-doh.Plo \ + ./$(DEPDIR)/libcurl_la-dynbuf.Plo \ + ./$(DEPDIR)/libcurl_la-dynhds.Plo \ + ./$(DEPDIR)/libcurl_la-easy.Plo \ + ./$(DEPDIR)/libcurl_la-easygetopt.Plo \ + ./$(DEPDIR)/libcurl_la-easyoptions.Plo \ + ./$(DEPDIR)/libcurl_la-escape.Plo \ + ./$(DEPDIR)/libcurl_la-file.Plo \ + ./$(DEPDIR)/libcurl_la-fileinfo.Plo \ + ./$(DEPDIR)/libcurl_la-fopen.Plo \ + ./$(DEPDIR)/libcurl_la-formdata.Plo \ + ./$(DEPDIR)/libcurl_la-ftp.Plo \ + ./$(DEPDIR)/libcurl_la-ftplistparser.Plo \ + ./$(DEPDIR)/libcurl_la-getenv.Plo \ + ./$(DEPDIR)/libcurl_la-getinfo.Plo \ + ./$(DEPDIR)/libcurl_la-gopher.Plo \ + ./$(DEPDIR)/libcurl_la-hash.Plo \ + ./$(DEPDIR)/libcurl_la-headers.Plo \ + ./$(DEPDIR)/libcurl_la-hmac.Plo \ + ./$(DEPDIR)/libcurl_la-hostasyn.Plo \ + ./$(DEPDIR)/libcurl_la-hostip.Plo \ + ./$(DEPDIR)/libcurl_la-hostip4.Plo \ + ./$(DEPDIR)/libcurl_la-hostip6.Plo \ + ./$(DEPDIR)/libcurl_la-hostsyn.Plo \ + ./$(DEPDIR)/libcurl_la-hsts.Plo \ + ./$(DEPDIR)/libcurl_la-http.Plo \ + ./$(DEPDIR)/libcurl_la-http1.Plo \ + ./$(DEPDIR)/libcurl_la-http2.Plo \ + ./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo \ + ./$(DEPDIR)/libcurl_la-http_chunks.Plo \ + ./$(DEPDIR)/libcurl_la-http_digest.Plo \ + ./$(DEPDIR)/libcurl_la-http_negotiate.Plo \ + ./$(DEPDIR)/libcurl_la-http_ntlm.Plo \ + ./$(DEPDIR)/libcurl_la-http_proxy.Plo \ + ./$(DEPDIR)/libcurl_la-idn.Plo \ + ./$(DEPDIR)/libcurl_la-if2ip.Plo \ + ./$(DEPDIR)/libcurl_la-imap.Plo \ + ./$(DEPDIR)/libcurl_la-inet_ntop.Plo \ + ./$(DEPDIR)/libcurl_la-inet_pton.Plo \ + ./$(DEPDIR)/libcurl_la-krb5.Plo \ + ./$(DEPDIR)/libcurl_la-ldap.Plo \ + ./$(DEPDIR)/libcurl_la-llist.Plo \ + ./$(DEPDIR)/libcurl_la-macos.Plo \ + ./$(DEPDIR)/libcurl_la-md4.Plo ./$(DEPDIR)/libcurl_la-md5.Plo \ + ./$(DEPDIR)/libcurl_la-memdebug.Plo \ + ./$(DEPDIR)/libcurl_la-mime.Plo \ + ./$(DEPDIR)/libcurl_la-mprintf.Plo \ + ./$(DEPDIR)/libcurl_la-mqtt.Plo \ + ./$(DEPDIR)/libcurl_la-multi.Plo \ + ./$(DEPDIR)/libcurl_la-netrc.Plo \ + ./$(DEPDIR)/libcurl_la-nonblock.Plo \ + ./$(DEPDIR)/libcurl_la-noproxy.Plo \ + ./$(DEPDIR)/libcurl_la-openldap.Plo \ + ./$(DEPDIR)/libcurl_la-parsedate.Plo \ + ./$(DEPDIR)/libcurl_la-pingpong.Plo \ + ./$(DEPDIR)/libcurl_la-pop3.Plo \ + ./$(DEPDIR)/libcurl_la-progress.Plo \ + ./$(DEPDIR)/libcurl_la-psl.Plo ./$(DEPDIR)/libcurl_la-rand.Plo \ + ./$(DEPDIR)/libcurl_la-rename.Plo \ + ./$(DEPDIR)/libcurl_la-request.Plo \ + ./$(DEPDIR)/libcurl_la-rtsp.Plo \ + ./$(DEPDIR)/libcurl_la-select.Plo \ + ./$(DEPDIR)/libcurl_la-sendf.Plo \ + ./$(DEPDIR)/libcurl_la-setopt.Plo \ + ./$(DEPDIR)/libcurl_la-sha256.Plo \ + ./$(DEPDIR)/libcurl_la-share.Plo \ + ./$(DEPDIR)/libcurl_la-slist.Plo \ + ./$(DEPDIR)/libcurl_la-smb.Plo ./$(DEPDIR)/libcurl_la-smtp.Plo \ + ./$(DEPDIR)/libcurl_la-socketpair.Plo \ + ./$(DEPDIR)/libcurl_la-socks.Plo \ + ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo \ + ./$(DEPDIR)/libcurl_la-socks_sspi.Plo \ + ./$(DEPDIR)/libcurl_la-speedcheck.Plo \ + ./$(DEPDIR)/libcurl_la-splay.Plo \ + ./$(DEPDIR)/libcurl_la-strcase.Plo \ + ./$(DEPDIR)/libcurl_la-strdup.Plo \ + ./$(DEPDIR)/libcurl_la-strerror.Plo \ + ./$(DEPDIR)/libcurl_la-strtok.Plo \ + ./$(DEPDIR)/libcurl_la-strtoofft.Plo \ + ./$(DEPDIR)/libcurl_la-system_win32.Plo \ + ./$(DEPDIR)/libcurl_la-telnet.Plo \ + ./$(DEPDIR)/libcurl_la-tftp.Plo \ + ./$(DEPDIR)/libcurl_la-timediff.Plo \ + ./$(DEPDIR)/libcurl_la-timeval.Plo \ + ./$(DEPDIR)/libcurl_la-transfer.Plo \ + ./$(DEPDIR)/libcurl_la-url.Plo \ + ./$(DEPDIR)/libcurl_la-urlapi.Plo \ + ./$(DEPDIR)/libcurl_la-version.Plo \ + ./$(DEPDIR)/libcurl_la-version_win32.Plo \ + ./$(DEPDIR)/libcurl_la-warnless.Plo \ + ./$(DEPDIR)/libcurl_la-ws.Plo \ + ./$(DEPDIR)/libcurlu_la-altsvc.Plo \ + ./$(DEPDIR)/libcurlu_la-amigaos.Plo \ + ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo \ + ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo \ + ./$(DEPDIR)/libcurlu_la-base64.Plo \ + ./$(DEPDIR)/libcurlu_la-bufq.Plo \ + ./$(DEPDIR)/libcurlu_la-bufref.Plo \ + ./$(DEPDIR)/libcurlu_la-c-hyper.Plo \ + ./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo \ + ./$(DEPDIR)/libcurlu_la-cf-h2-proxy.Plo \ + ./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo \ + ./$(DEPDIR)/libcurlu_la-cf-https-connect.Plo \ + ./$(DEPDIR)/libcurlu_la-cf-socket.Plo \ + ./$(DEPDIR)/libcurlu_la-cfilters.Plo \ + ./$(DEPDIR)/libcurlu_la-conncache.Plo \ + ./$(DEPDIR)/libcurlu_la-connect.Plo \ + ./$(DEPDIR)/libcurlu_la-content_encoding.Plo \ + ./$(DEPDIR)/libcurlu_la-cookie.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_des.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_endian.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_get_line.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_path.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_range.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_sasl.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_sha512_256.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_sspi.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_threads.Plo \ + ./$(DEPDIR)/libcurlu_la-curl_trc.Plo \ + ./$(DEPDIR)/libcurlu_la-cw-out.Plo \ + ./$(DEPDIR)/libcurlu_la-dict.Plo \ + ./$(DEPDIR)/libcurlu_la-dllmain.Plo \ + ./$(DEPDIR)/libcurlu_la-doh.Plo \ + ./$(DEPDIR)/libcurlu_la-dynbuf.Plo \ + ./$(DEPDIR)/libcurlu_la-dynhds.Plo \ + ./$(DEPDIR)/libcurlu_la-easy.Plo \ + ./$(DEPDIR)/libcurlu_la-easygetopt.Plo \ + ./$(DEPDIR)/libcurlu_la-easyoptions.Plo \ + ./$(DEPDIR)/libcurlu_la-escape.Plo \ + ./$(DEPDIR)/libcurlu_la-file.Plo \ + ./$(DEPDIR)/libcurlu_la-fileinfo.Plo \ + ./$(DEPDIR)/libcurlu_la-fopen.Plo \ + ./$(DEPDIR)/libcurlu_la-formdata.Plo \ + ./$(DEPDIR)/libcurlu_la-ftp.Plo \ + ./$(DEPDIR)/libcurlu_la-ftplistparser.Plo \ + ./$(DEPDIR)/libcurlu_la-getenv.Plo \ + ./$(DEPDIR)/libcurlu_la-getinfo.Plo \ + ./$(DEPDIR)/libcurlu_la-gopher.Plo \ + ./$(DEPDIR)/libcurlu_la-hash.Plo \ + ./$(DEPDIR)/libcurlu_la-headers.Plo \ + ./$(DEPDIR)/libcurlu_la-hmac.Plo \ + ./$(DEPDIR)/libcurlu_la-hostasyn.Plo \ + ./$(DEPDIR)/libcurlu_la-hostip.Plo \ + ./$(DEPDIR)/libcurlu_la-hostip4.Plo \ + ./$(DEPDIR)/libcurlu_la-hostip6.Plo \ + ./$(DEPDIR)/libcurlu_la-hostsyn.Plo \ + ./$(DEPDIR)/libcurlu_la-hsts.Plo \ + ./$(DEPDIR)/libcurlu_la-http.Plo \ + ./$(DEPDIR)/libcurlu_la-http1.Plo \ + ./$(DEPDIR)/libcurlu_la-http2.Plo \ + ./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo \ + ./$(DEPDIR)/libcurlu_la-http_chunks.Plo \ + ./$(DEPDIR)/libcurlu_la-http_digest.Plo \ + ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo \ + ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo \ + ./$(DEPDIR)/libcurlu_la-http_proxy.Plo \ + ./$(DEPDIR)/libcurlu_la-idn.Plo \ + ./$(DEPDIR)/libcurlu_la-if2ip.Plo \ + ./$(DEPDIR)/libcurlu_la-imap.Plo \ + ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo \ + ./$(DEPDIR)/libcurlu_la-inet_pton.Plo \ + ./$(DEPDIR)/libcurlu_la-krb5.Plo \ + ./$(DEPDIR)/libcurlu_la-ldap.Plo \ + ./$(DEPDIR)/libcurlu_la-llist.Plo \ + ./$(DEPDIR)/libcurlu_la-macos.Plo \ + ./$(DEPDIR)/libcurlu_la-md4.Plo \ + ./$(DEPDIR)/libcurlu_la-md5.Plo \ + ./$(DEPDIR)/libcurlu_la-memdebug.Plo \ + ./$(DEPDIR)/libcurlu_la-mime.Plo \ + ./$(DEPDIR)/libcurlu_la-mprintf.Plo \ + ./$(DEPDIR)/libcurlu_la-mqtt.Plo \ + ./$(DEPDIR)/libcurlu_la-multi.Plo \ + ./$(DEPDIR)/libcurlu_la-netrc.Plo \ + ./$(DEPDIR)/libcurlu_la-nonblock.Plo \ + ./$(DEPDIR)/libcurlu_la-noproxy.Plo \ + ./$(DEPDIR)/libcurlu_la-openldap.Plo \ + ./$(DEPDIR)/libcurlu_la-parsedate.Plo \ + ./$(DEPDIR)/libcurlu_la-pingpong.Plo \ + ./$(DEPDIR)/libcurlu_la-pop3.Plo \ + ./$(DEPDIR)/libcurlu_la-progress.Plo \ + ./$(DEPDIR)/libcurlu_la-psl.Plo \ + ./$(DEPDIR)/libcurlu_la-rand.Plo \ + ./$(DEPDIR)/libcurlu_la-rename.Plo \ + ./$(DEPDIR)/libcurlu_la-request.Plo \ + ./$(DEPDIR)/libcurlu_la-rtsp.Plo \ + ./$(DEPDIR)/libcurlu_la-select.Plo \ + ./$(DEPDIR)/libcurlu_la-sendf.Plo \ + ./$(DEPDIR)/libcurlu_la-setopt.Plo \ + ./$(DEPDIR)/libcurlu_la-sha256.Plo \ + ./$(DEPDIR)/libcurlu_la-share.Plo \ + ./$(DEPDIR)/libcurlu_la-slist.Plo \ + ./$(DEPDIR)/libcurlu_la-smb.Plo \ + ./$(DEPDIR)/libcurlu_la-smtp.Plo \ + ./$(DEPDIR)/libcurlu_la-socketpair.Plo \ + ./$(DEPDIR)/libcurlu_la-socks.Plo \ + ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo \ + ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo \ + ./$(DEPDIR)/libcurlu_la-speedcheck.Plo \ + ./$(DEPDIR)/libcurlu_la-splay.Plo \ + ./$(DEPDIR)/libcurlu_la-strcase.Plo \ + ./$(DEPDIR)/libcurlu_la-strdup.Plo \ + ./$(DEPDIR)/libcurlu_la-strerror.Plo \ + ./$(DEPDIR)/libcurlu_la-strtok.Plo \ + ./$(DEPDIR)/libcurlu_la-strtoofft.Plo \ + ./$(DEPDIR)/libcurlu_la-system_win32.Plo \ + ./$(DEPDIR)/libcurlu_la-telnet.Plo \ + ./$(DEPDIR)/libcurlu_la-tftp.Plo \ + ./$(DEPDIR)/libcurlu_la-timediff.Plo \ + ./$(DEPDIR)/libcurlu_la-timeval.Plo \ + ./$(DEPDIR)/libcurlu_la-transfer.Plo \ + ./$(DEPDIR)/libcurlu_la-url.Plo \ + ./$(DEPDIR)/libcurlu_la-urlapi.Plo \ + ./$(DEPDIR)/libcurlu_la-version.Plo \ + ./$(DEPDIR)/libcurlu_la-version_win32.Plo \ + ./$(DEPDIR)/libcurlu_la-warnless.Plo \ + ./$(DEPDIR)/libcurlu_la-ws.Plo \ + vauth/$(DEPDIR)/libcurl_la-cleartext.Plo \ + vauth/$(DEPDIR)/libcurl_la-cram.Plo \ + vauth/$(DEPDIR)/libcurl_la-digest.Plo \ + vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo \ + vauth/$(DEPDIR)/libcurl_la-gsasl.Plo \ + vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo \ + vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo \ + vauth/$(DEPDIR)/libcurl_la-ntlm.Plo \ + vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo \ + vauth/$(DEPDIR)/libcurl_la-oauth2.Plo \ + vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo \ + vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo \ + vauth/$(DEPDIR)/libcurl_la-vauth.Plo \ + vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo \ + vauth/$(DEPDIR)/libcurlu_la-cram.Plo \ + vauth/$(DEPDIR)/libcurlu_la-digest.Plo \ + vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo \ + vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo \ + vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo \ + vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo \ + vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo \ + vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo \ + vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo \ + vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo \ + vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo \ + vauth/$(DEPDIR)/libcurlu_la-vauth.Plo \ + vquic/$(DEPDIR)/libcurl_la-curl_msh3.Plo \ + vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo \ + vquic/$(DEPDIR)/libcurl_la-curl_osslq.Plo \ + vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo \ + vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo \ + vquic/$(DEPDIR)/libcurl_la-vquic.Plo \ + vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Plo \ + vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo \ + vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Plo \ + vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo \ + vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo \ + vquic/$(DEPDIR)/libcurlu_la-vquic.Plo \ + vssh/$(DEPDIR)/libcurl_la-libssh.Plo \ + vssh/$(DEPDIR)/libcurl_la-libssh2.Plo \ + vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo \ + vssh/$(DEPDIR)/libcurlu_la-libssh.Plo \ + vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo \ + vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo \ + vtls/$(DEPDIR)/libcurl_la-bearssl.Plo \ + vtls/$(DEPDIR)/libcurl_la-cipher_suite.Plo \ + vtls/$(DEPDIR)/libcurl_la-gtls.Plo \ + vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo \ + vtls/$(DEPDIR)/libcurl_la-keylog.Plo \ + vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo \ + vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo \ + vtls/$(DEPDIR)/libcurl_la-openssl.Plo \ + vtls/$(DEPDIR)/libcurl_la-rustls.Plo \ + vtls/$(DEPDIR)/libcurl_la-schannel.Plo \ + vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo \ + vtls/$(DEPDIR)/libcurl_la-sectransp.Plo \ + vtls/$(DEPDIR)/libcurl_la-vtls.Plo \ + vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo \ + vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo \ + vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo \ + vtls/$(DEPDIR)/libcurlu_la-cipher_suite.Plo \ + vtls/$(DEPDIR)/libcurlu_la-gtls.Plo \ + vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo \ + vtls/$(DEPDIR)/libcurlu_la-keylog.Plo \ + vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo \ + vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo \ + vtls/$(DEPDIR)/libcurlu_la-openssl.Plo \ + vtls/$(DEPDIR)/libcurlu_la-rustls.Plo \ + vtls/$(DEPDIR)/libcurlu_la-schannel.Plo \ + vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo \ + vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo \ + vtls/$(DEPDIR)/libcurlu_la-vtls.Plo \ + vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo \ + vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libcurl_la_SOURCES) $(libcurlu_la_SOURCES) +DIST_SOURCES = $(am__libcurl_la_SOURCES_DIST) $(libcurlu_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ + curl_config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.inc \ + $(srcdir)/Makefile.soname $(srcdir)/curl_config.h.in \ + $(srcdir)/libcurl.vers.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APACHECTL = @APACHECTL@ +APXS = @APXS@ +AR = @AR@ +AR_FLAGS = @AR_FLAGS@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@ +CADDY = @CADDY@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ + +# This might hold -Werror +CFLAGS = @CFLAGS@ @CURL_CFLAG_EXTRAS@ +CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@ +CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURLVERSION = @CURLVERSION@ +CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ +CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@ +CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ +CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ +CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ +CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ +CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ +CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@ +CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ +CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@ +CURL_DISABLE_MQTT = @CURL_DISABLE_MQTT@ +CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@ +CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@ +CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@ +CURL_DISABLE_SMB = @CURL_DISABLE_SMB@ +CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@ +CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ +CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@ +CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@ +CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@ +CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@ +CURL_WITH_MULTI_SSL = @CURL_WITH_MULTI_SSL@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_SSL_BACKEND = @DEFAULT_SSL_BACKEND@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_SHARED = @ENABLE_SHARED@ +ENABLE_STATIC = @ENABLE_STATIC@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@ +GCOV = @GCOV@ +GREP = @GREP@ +HAVE_BROTLI = @HAVE_BROTLI@ +HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@ +HAVE_LDAP_SSL = @HAVE_LDAP_SSL@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_OPENSSL_QUIC = @HAVE_OPENSSL_QUIC@ +HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@ +HAVE_PROTO_BSDSOCKET_H = @HAVE_PROTO_BSDSOCKET_H@ +HAVE_ZSTD = @HAVE_ZSTD@ +HTTPD = @HTTPD@ +HTTPD_NGHTTPX = @HTTPD_NGHTTPX@ +IDN_ENABLED = @IDN_ENABLED@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPV6_ENABLED = @IPV6_ENABLED@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCURL_LIBS = @LIBCURL_LIBS@ +LIBCURL_NO_SHARED = @LIBCURL_NO_SHARED@ +LIBCURL_PC_REQUIRES = @LIBCURL_PC_REQUIRES@ +LIBCURL_PC_REQUIRES_PRIVATE = @LIBCURL_PC_REQUIRES_PRIVATE@ +LIBOBJS = @LIBOBJS@ + +# Prevent LIBS from being used for all link targets +LIBS = $(BLANK_AT_MAKETIME) +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKGADD_NAME = @PKGADD_NAME@ +PKGADD_PKG = @PKGADD_PKG@ +PKGADD_VENDOR = @PKGADD_VENDOR@ +PKGCONFIG = @PKGCONFIG@ +RANDOM_FILE = @RANDOM_FILE@ +RANLIB = @RANLIB@ +RC = @RC@ +REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSL_BACKENDS = @SSL_BACKENDS@ +SSL_ENABLED = @SSL_ENABLED@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SUPPORT_FEATURES = @SUPPORT_FEATURES@ +SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@ +TEST_NGHTTPX = @TEST_NGHTTPX@ +USE_ARES = @USE_ARES@ +USE_BEARSSL = @USE_BEARSSL@ +USE_GNUTLS = @USE_GNUTLS@ +USE_HYPER = @USE_HYPER@ +USE_LIBPSL = @USE_LIBPSL@ +USE_LIBRTMP = @USE_LIBRTMP@ +USE_LIBSSH = @USE_LIBSSH@ +USE_LIBSSH2 = @USE_LIBSSH2@ +USE_MBEDTLS = @USE_MBEDTLS@ +USE_MSH3 = @USE_MSH3@ +USE_NGHTTP2 = @USE_NGHTTP2@ +USE_NGHTTP3 = @USE_NGHTTP3@ +USE_NGTCP2 = @USE_NGTCP2@ +USE_NGTCP2_CRYPTO_BORINGSSL = @USE_NGTCP2_CRYPTO_BORINGSSL@ +USE_NGTCP2_CRYPTO_GNUTLS = @USE_NGTCP2_CRYPTO_GNUTLS@ +USE_NGTCP2_CRYPTO_QUICTLS = @USE_NGTCP2_CRYPTO_QUICTLS@ +USE_NGTCP2_CRYPTO_WOLFSSL = @USE_NGTCP2_CRYPTO_WOLFSSL@ +USE_NGTCP2_H3 = @USE_NGTCP2_H3@ +USE_OPENLDAP = @USE_OPENLDAP@ +USE_OPENSSL_H3 = @USE_OPENSSL_H3@ +USE_OPENSSL_QUIC = @USE_OPENSSL_QUIC@ +USE_QUICHE = @USE_QUICHE@ +USE_RUSTLS = @USE_RUSTLS@ +USE_SCHANNEL = @USE_SCHANNEL@ +USE_SECTRANSP = @USE_SECTRANSP@ +USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@ +USE_WIN32_CRYPTO = @USE_WIN32_CRYPTO@ +USE_WIN32_LARGE_FILES = @USE_WIN32_LARGE_FILES@ +USE_WIN32_SMALL_FILES = @USE_WIN32_SMALL_FILES@ +USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@ +USE_WOLFSSH = @USE_WOLFSSH@ +USE_WOLFSSL = @USE_WOLFSSL@ +VERSION = @VERSION@ +VERSIONNUM = @VERSIONNUM@ +VSFTPD = @VSFTPD@ +ZLIB_LIBS = @ZLIB_LIBS@ +ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libext = @libext@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +AUTOMAKE_OPTIONS = foreign nostdinc +CMAKE_DIST = CMakeLists.txt curl_config.h.cmake +EXTRA_DIST = Makefile.mk config-win32.h config-win32ce.h config-plan9.h \ + config-riscos.h config-mac.h curl_config.h.in config-dos.h libcurl.rc \ + config-amigaos.h config-win32ce.h config-os400.h setup-os400.h \ + $(CMAKE_DIST) setup-win32.h .checksrc Makefile.soname + +lib_LTLIBRARIES = libcurl.la +@BUILD_UNITTESTS_FALSE@noinst_LTLIBRARIES = +@BUILD_UNITTESTS_TRUE@noinst_LTLIBRARIES = libcurlu.la + +# Specify our include paths here, and do it relative to $(top_srcdir) and +# $(top_builddir), to ensure that these paths which belong to the library +# being currently built and tested are searched before the library which +# might possibly already be installed in the system. +# +# $(top_srcdir)/include is for libcurl's external include files +# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file +# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "private" files +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/lib \ + -I$(top_srcdir)/lib -DBUILDING_LIBCURL +VERSIONCHANGE = 12 +VERSIONADD = 0 +VERSIONDEL = 8 + +# libtool version: +VERSIONINFO = -version-info $(VERSIONCHANGE):$(VERSIONADD):$(VERSIONDEL) +AM_LDFLAGS = +AM_CFLAGS = +LIB_VAUTH_CFILES = \ + vauth/cleartext.c \ + vauth/cram.c \ + vauth/digest.c \ + vauth/digest_sspi.c \ + vauth/gsasl.c \ + vauth/krb5_gssapi.c \ + vauth/krb5_sspi.c \ + vauth/ntlm.c \ + vauth/ntlm_sspi.c \ + vauth/oauth2.c \ + vauth/spnego_gssapi.c \ + vauth/spnego_sspi.c \ + vauth/vauth.c + +LIB_VAUTH_HFILES = \ + vauth/digest.h \ + vauth/ntlm.h \ + vauth/vauth.h + +LIB_VTLS_CFILES = \ + vtls/bearssl.c \ + vtls/cipher_suite.c \ + vtls/gtls.c \ + vtls/hostcheck.c \ + vtls/keylog.c \ + vtls/mbedtls.c \ + vtls/mbedtls_threadlock.c \ + vtls/openssl.c \ + vtls/rustls.c \ + vtls/schannel.c \ + vtls/schannel_verify.c \ + vtls/sectransp.c \ + vtls/vtls.c \ + vtls/wolfssl.c \ + vtls/x509asn1.c + +LIB_VTLS_HFILES = \ + vtls/bearssl.h \ + vtls/cipher_suite.h \ + vtls/gtls.h \ + vtls/hostcheck.h \ + vtls/keylog.h \ + vtls/mbedtls.h \ + vtls/mbedtls_threadlock.h \ + vtls/openssl.h \ + vtls/rustls.h \ + vtls/schannel.h \ + vtls/schannel_int.h \ + vtls/sectransp.h \ + vtls/vtls.h \ + vtls/vtls_int.h \ + vtls/wolfssl.h \ + vtls/x509asn1.h + +LIB_VQUIC_CFILES = \ + vquic/curl_msh3.c \ + vquic/curl_ngtcp2.c \ + vquic/curl_osslq.c \ + vquic/curl_quiche.c \ + vquic/vquic.c \ + vquic/vquic-tls.c + +LIB_VQUIC_HFILES = \ + vquic/curl_msh3.h \ + vquic/curl_ngtcp2.h \ + vquic/curl_osslq.h \ + vquic/curl_quiche.h \ + vquic/vquic.h \ + vquic/vquic_int.h \ + vquic/vquic-tls.h + +LIB_VSSH_CFILES = \ + vssh/libssh.c \ + vssh/libssh2.c \ + vssh/wolfssh.c + +LIB_VSSH_HFILES = \ + vssh/ssh.h + +LIB_CFILES = \ + altsvc.c \ + amigaos.c \ + asyn-ares.c \ + asyn-thread.c \ + base64.c \ + bufq.c \ + bufref.c \ + c-hyper.c \ + cf-h1-proxy.c \ + cf-h2-proxy.c \ + cf-haproxy.c \ + cf-https-connect.c \ + cf-socket.c \ + cfilters.c \ + conncache.c \ + connect.c \ + content_encoding.c \ + cookie.c \ + curl_addrinfo.c \ + curl_des.c \ + curl_endian.c \ + curl_fnmatch.c \ + curl_get_line.c \ + curl_gethostname.c \ + curl_gssapi.c \ + curl_memrchr.c \ + curl_multibyte.c \ + curl_ntlm_core.c \ + curl_path.c \ + curl_range.c \ + curl_rtmp.c \ + curl_sasl.c \ + curl_sha512_256.c \ + curl_sspi.c \ + curl_threads.c \ + curl_trc.c \ + cw-out.c \ + dict.c \ + dllmain.c \ + doh.c \ + dynbuf.c \ + dynhds.c \ + easy.c \ + easygetopt.c \ + easyoptions.c \ + escape.c \ + file.c \ + fileinfo.c \ + fopen.c \ + formdata.c \ + ftp.c \ + ftplistparser.c \ + getenv.c \ + getinfo.c \ + gopher.c \ + hash.c \ + headers.c \ + hmac.c \ + hostasyn.c \ + hostip.c \ + hostip4.c \ + hostip6.c \ + hostsyn.c \ + hsts.c \ + http.c \ + http1.c \ + http2.c \ + http_aws_sigv4.c \ + http_chunks.c \ + http_digest.c \ + http_negotiate.c \ + http_ntlm.c \ + http_proxy.c \ + idn.c \ + if2ip.c \ + imap.c \ + inet_ntop.c \ + inet_pton.c \ + krb5.c \ + ldap.c \ + llist.c \ + macos.c \ + md4.c \ + md5.c \ + memdebug.c \ + mime.c \ + mprintf.c \ + mqtt.c \ + multi.c \ + netrc.c \ + nonblock.c \ + noproxy.c \ + openldap.c \ + parsedate.c \ + pingpong.c \ + pop3.c \ + progress.c \ + psl.c \ + rand.c \ + rename.c \ + request.c \ + rtsp.c \ + select.c \ + sendf.c \ + setopt.c \ + sha256.c \ + share.c \ + slist.c \ + smb.c \ + smtp.c \ + socketpair.c \ + socks.c \ + socks_gssapi.c \ + socks_sspi.c \ + speedcheck.c \ + splay.c \ + strcase.c \ + strdup.c \ + strerror.c \ + strtok.c \ + strtoofft.c \ + system_win32.c \ + telnet.c \ + tftp.c \ + timediff.c \ + timeval.c \ + transfer.c \ + url.c \ + urlapi.c \ + version.c \ + version_win32.c \ + warnless.c \ + ws.c + +LIB_HFILES = \ + altsvc.h \ + amigaos.h \ + arpa_telnet.h \ + asyn.h \ + bufq.h \ + bufref.h \ + c-hyper.h \ + cf-h1-proxy.h \ + cf-h2-proxy.h \ + cf-haproxy.h \ + cf-https-connect.h \ + cf-socket.h \ + cfilters.h \ + conncache.h \ + connect.h \ + content_encoding.h \ + cookie.h \ + curl_addrinfo.h \ + curl_base64.h \ + curl_ctype.h \ + curl_des.h \ + curl_endian.h \ + curl_fnmatch.h \ + curl_get_line.h \ + curl_gethostname.h \ + curl_gssapi.h \ + curl_hmac.h \ + curl_krb5.h \ + curl_ldap.h \ + curl_md4.h \ + curl_md5.h \ + curl_memory.h \ + curl_memrchr.h \ + curl_multibyte.h \ + curl_ntlm_core.h \ + curl_path.h \ + curl_printf.h \ + curl_range.h \ + curl_rtmp.h \ + curl_sasl.h \ + curl_setup.h \ + curl_setup_once.h \ + curl_sha256.h \ + curl_sha512_256.h \ + curl_sspi.h \ + curl_threads.h \ + curl_trc.h \ + curlx.h \ + cw-out.h \ + dict.h \ + doh.h \ + dynbuf.h \ + dynhds.h \ + easy_lock.h \ + easyif.h \ + easyoptions.h \ + escape.h \ + file.h \ + fileinfo.h \ + fopen.h \ + formdata.h \ + ftp.h \ + ftplistparser.h \ + functypes.h \ + getinfo.h \ + gopher.h \ + hash.h \ + headers.h \ + hostip.h \ + hsts.h \ + http.h \ + http1.h \ + http2.h \ + http_aws_sigv4.h \ + http_chunks.h \ + http_digest.h \ + http_negotiate.h \ + http_ntlm.h \ + http_proxy.h \ + idn.h \ + if2ip.h \ + imap.h \ + inet_ntop.h \ + inet_pton.h \ + llist.h \ + macos.h \ + memdebug.h \ + mime.h \ + mqtt.h \ + multihandle.h \ + multiif.h \ + netrc.h \ + nonblock.h \ + noproxy.h \ + parsedate.h \ + pingpong.h \ + pop3.h \ + progress.h \ + psl.h \ + rand.h \ + rename.h \ + request.h \ + rtsp.h \ + select.h \ + sendf.h \ + setopt.h \ + setup-vms.h \ + share.h \ + sigpipe.h \ + slist.h \ + smb.h \ + smtp.h \ + sockaddr.h \ + socketpair.h \ + socks.h \ + speedcheck.h \ + splay.h \ + strcase.h \ + strdup.h \ + strerror.h \ + strtok.h \ + strtoofft.h \ + system_win32.h \ + telnet.h \ + tftp.h \ + timediff.h \ + timeval.h \ + transfer.h \ + url.h \ + urlapi-int.h \ + urldata.h \ + version_win32.h \ + warnless.h \ + ws.h + +LIB_RCFILES = libcurl.rc +CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \ + $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) + +HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \ + $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) + + +# Makefile.inc provides the CSOURCES and HHEADERS defines +libcurl_la_SOURCES = $(CSOURCES) $(HHEADERS) $(am__append_7) +libcurlu_la_SOURCES = $(CSOURCES) $(HHEADERS) +libcurl_la_CPPFLAGS_EXTRA = $(am__append_6) $(am__append_8) +libcurl_la_LDFLAGS_EXTRA = $(am__append_1) $(am__append_2) \ + $(am__append_3) $(am__append_4) $(am__append_5) +libcurl_la_CFLAGS_EXTRA = $(am__append_9) +libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA) +libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(LIBCURL_LIBS) +libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA) +libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS +libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_LIBS) +libcurlu_la_CFLAGS = $(AM_CFLAGS) +CHECKSRC = $(CS_$(V)) +CS_0 = @echo " RUN " $@; +CS_1 = +CS_ = $(CS_0) + +# disable the tests that are mostly causing false positives +TIDYFLAGS = -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-valist.Uninitialized,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference -quiet +TIDY := clang-tidy +all: curl_config.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj .rc +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.soname $(srcdir)/Makefile.inc $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(srcdir)/Makefile.soname $(srcdir)/Makefile.inc $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +curl_config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(srcdir)/curl_config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status lib/curl_config.h +$(srcdir)/curl_config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f curl_config.h stamp-h1 +libcurl.vers: $(top_builddir)/config.status $(srcdir)/libcurl.vers.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +vauth/$(am__dirstamp): + @$(MKDIR_P) vauth + @: > vauth/$(am__dirstamp) +vauth/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) vauth/$(DEPDIR) + @: > vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-cleartext.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-cram.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-digest.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-digest_sspi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-gsasl.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-krb5_gssapi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-krb5_sspi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-ntlm.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-ntlm_sspi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-oauth2.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-spnego_gssapi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-spnego_sspi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurl_la-vauth.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vtls/$(am__dirstamp): + @$(MKDIR_P) vtls + @: > vtls/$(am__dirstamp) +vtls/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) vtls/$(DEPDIR) + @: > vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-bearssl.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-cipher_suite.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-gtls.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-hostcheck.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-keylog.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-mbedtls.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-mbedtls_threadlock.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-openssl.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-rustls.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-schannel.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-schannel_verify.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-sectransp.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-vtls.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-wolfssl.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-x509asn1.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vquic/$(am__dirstamp): + @$(MKDIR_P) vquic + @: > vquic/$(am__dirstamp) +vquic/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) vquic/$(DEPDIR) + @: > vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurl_la-curl_msh3.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurl_la-curl_ngtcp2.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurl_la-curl_osslq.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurl_la-curl_quiche.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurl_la-vquic.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurl_la-vquic-tls.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vssh/$(am__dirstamp): + @$(MKDIR_P) vssh + @: > vssh/$(am__dirstamp) +vssh/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) vssh/$(DEPDIR) + @: > vssh/$(DEPDIR)/$(am__dirstamp) +vssh/libcurl_la-libssh.lo: vssh/$(am__dirstamp) \ + vssh/$(DEPDIR)/$(am__dirstamp) +vssh/libcurl_la-libssh2.lo: vssh/$(am__dirstamp) \ + vssh/$(DEPDIR)/$(am__dirstamp) +vssh/libcurl_la-wolfssh.lo: vssh/$(am__dirstamp) \ + vssh/$(DEPDIR)/$(am__dirstamp) + +libcurl.la: $(libcurl_la_OBJECTS) $(libcurl_la_DEPENDENCIES) $(EXTRA_libcurl_la_DEPENDENCIES) + $(AM_V_CCLD)$(libcurl_la_LINK) -rpath $(libdir) $(libcurl_la_OBJECTS) $(libcurl_la_LIBADD) $(LIBS) +vauth/libcurlu_la-cleartext.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-cram.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-digest.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-digest_sspi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-gsasl.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-krb5_gssapi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-krb5_sspi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-ntlm.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-ntlm_sspi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-oauth2.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-spnego_gssapi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-spnego_sspi.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vauth/libcurlu_la-vauth.lo: vauth/$(am__dirstamp) \ + vauth/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-bearssl.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-cipher_suite.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-gtls.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-hostcheck.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-keylog.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-mbedtls.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-mbedtls_threadlock.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-openssl.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-rustls.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-schannel.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-schannel_verify.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-sectransp.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-vtls.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-wolfssl.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-x509asn1.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) +vquic/libcurlu_la-curl_msh3.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurlu_la-curl_ngtcp2.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurlu_la-curl_osslq.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurlu_la-curl_quiche.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurlu_la-vquic.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurlu_la-vquic-tls.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vssh/libcurlu_la-libssh.lo: vssh/$(am__dirstamp) \ + vssh/$(DEPDIR)/$(am__dirstamp) +vssh/libcurlu_la-libssh2.lo: vssh/$(am__dirstamp) \ + vssh/$(DEPDIR)/$(am__dirstamp) +vssh/libcurlu_la-wolfssh.lo: vssh/$(am__dirstamp) \ + vssh/$(DEPDIR)/$(am__dirstamp) + +libcurlu.la: $(libcurlu_la_OBJECTS) $(libcurlu_la_DEPENDENCIES) $(EXTRA_libcurlu_la_DEPENDENCIES) + $(AM_V_CCLD)$(libcurlu_la_LINK) $(am_libcurlu_la_rpath) $(libcurlu_la_OBJECTS) $(libcurlu_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f vauth/*.$(OBJEXT) + -rm -f vauth/*.lo + -rm -f vquic/*.$(OBJEXT) + -rm -f vquic/*.lo + -rm -f vssh/*.$(OBJEXT) + -rm -f vssh/*.lo + -rm -f vtls/*.$(OBJEXT) + -rm -f vtls/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-altsvc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-amigaos.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-ares.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-thread.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-base64.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-bufq.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-bufref.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-c-hyper.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-h2-proxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-haproxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-https-connect.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-socket.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cfilters.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-conncache.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-connect.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-content_encoding.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cookie.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_des.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_endian.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_get_line.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_gethostname.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_gssapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_memrchr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_multibyte.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_path.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_range.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_rtmp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_sasl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_sha512_256.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_threads.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_trc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cw-out.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dict.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dllmain.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-doh.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dynbuf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dynhds.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easygetopt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easyoptions.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-escape.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-file.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fileinfo.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fopen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-formdata.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ftp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ftplistparser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-getenv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-getinfo.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-gopher.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-headers.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hmac.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostasyn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip4.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip6.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostsyn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hsts.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http1.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_chunks.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_digest.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_negotiate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_ntlm.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_proxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-idn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-if2ip.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-imap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-inet_ntop.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-inet_pton.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-krb5.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ldap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-llist.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-macos.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-md4.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-md5.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-memdebug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mime.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mprintf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mqtt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-multi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-netrc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-nonblock.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-noproxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-openldap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-parsedate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-pingpong.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-pop3.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-progress.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-psl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-rand.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-rename.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-request.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-rtsp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-select.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-sendf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-setopt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-sha256.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-share.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-slist.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-smb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-smtp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socketpair.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks_gssapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-speedcheck.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-splay.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strcase.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strdup.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strerror.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strtok.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strtoofft.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-system_win32.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-telnet.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-tftp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-timediff.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-timeval.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-transfer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-url.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-urlapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-version.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-version_win32.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-warnless.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ws.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-altsvc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-amigaos.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-ares.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-thread.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-base64.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-bufq.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-bufref.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-c-hyper.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-h2-proxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-https-connect.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-socket.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cfilters.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-conncache.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-connect.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-content_encoding.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cookie.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_des.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_endian.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_get_line.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_path.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_range.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_sasl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_sha512_256.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_threads.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_trc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cw-out.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dict.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dllmain.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-doh.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dynbuf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dynhds.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easygetopt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easyoptions.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-escape.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-file.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fileinfo.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fopen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-formdata.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ftp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ftplistparser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-getenv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-getinfo.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-gopher.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-headers.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hmac.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostasyn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip4.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip6.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostsyn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hsts.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http1.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_chunks.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_digest.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_negotiate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_ntlm.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_proxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-idn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-if2ip.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-imap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-inet_ntop.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-inet_pton.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-krb5.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ldap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-llist.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-macos.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-md4.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-md5.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-memdebug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mime.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mprintf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mqtt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-multi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-netrc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-nonblock.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-noproxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-openldap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-parsedate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-pingpong.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-pop3.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-progress.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-psl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-rand.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-rename.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-request.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-rtsp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-select.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-sendf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-setopt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-sha256.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-share.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-slist.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-smb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-smtp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socketpair.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-speedcheck.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-splay.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strcase.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strdup.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strerror.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strtok.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strtoofft.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-system_win32.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-telnet.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-tftp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-timediff.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-timeval.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-transfer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-url.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-urlapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-version.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-version_win32.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-warnless.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ws.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-cleartext.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-cram.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-digest.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-gsasl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-ntlm.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-oauth2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-vauth.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-cram.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-digest.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-vauth.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-curl_msh3.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-curl_osslq.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-vquic.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-vquic.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-libssh.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-libssh2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurlu_la-libssh.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-bearssl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-cipher_suite.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-gtls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-keylog.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-openssl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-rustls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-schannel.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-sectransp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-vtls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-cipher_suite.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-gtls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-keylog.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-openssl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-rustls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-schannel.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-vtls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libcurl_la-altsvc.lo: altsvc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-altsvc.lo -MD -MP -MF $(DEPDIR)/libcurl_la-altsvc.Tpo -c -o libcurl_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-altsvc.Tpo $(DEPDIR)/libcurl_la-altsvc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='altsvc.c' object='libcurl_la-altsvc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c + +libcurl_la-amigaos.lo: amigaos.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-amigaos.lo -MD -MP -MF $(DEPDIR)/libcurl_la-amigaos.Tpo -c -o libcurl_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-amigaos.Tpo $(DEPDIR)/libcurl_la-amigaos.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='amigaos.c' object='libcurl_la-amigaos.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c + +libcurl_la-asyn-ares.lo: asyn-ares.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-ares.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-ares.Tpo -c -o libcurl_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-ares.Tpo $(DEPDIR)/libcurl_la-asyn-ares.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-ares.c' object='libcurl_la-asyn-ares.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c + +libcurl_la-asyn-thread.lo: asyn-thread.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-thread.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-thread.Tpo -c -o libcurl_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-thread.Tpo $(DEPDIR)/libcurl_la-asyn-thread.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-thread.c' object='libcurl_la-asyn-thread.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c + +libcurl_la-base64.lo: base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-base64.lo -MD -MP -MF $(DEPDIR)/libcurl_la-base64.Tpo -c -o libcurl_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-base64.Tpo $(DEPDIR)/libcurl_la-base64.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libcurl_la-base64.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c + +libcurl_la-bufq.lo: bufq.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-bufq.lo -MD -MP -MF $(DEPDIR)/libcurl_la-bufq.Tpo -c -o libcurl_la-bufq.lo `test -f 'bufq.c' || echo '$(srcdir)/'`bufq.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-bufq.Tpo $(DEPDIR)/libcurl_la-bufq.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bufq.c' object='libcurl_la-bufq.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-bufq.lo `test -f 'bufq.c' || echo '$(srcdir)/'`bufq.c + +libcurl_la-bufref.lo: bufref.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-bufref.lo -MD -MP -MF $(DEPDIR)/libcurl_la-bufref.Tpo -c -o libcurl_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-bufref.Tpo $(DEPDIR)/libcurl_la-bufref.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bufref.c' object='libcurl_la-bufref.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c + +libcurl_la-c-hyper.lo: c-hyper.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-c-hyper.lo -MD -MP -MF $(DEPDIR)/libcurl_la-c-hyper.Tpo -c -o libcurl_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-c-hyper.Tpo $(DEPDIR)/libcurl_la-c-hyper.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='c-hyper.c' object='libcurl_la-c-hyper.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c + +libcurl_la-cf-h1-proxy.lo: cf-h1-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cf-h1-proxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cf-h1-proxy.Tpo -c -o libcurl_la-cf-h1-proxy.lo `test -f 'cf-h1-proxy.c' || echo '$(srcdir)/'`cf-h1-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cf-h1-proxy.Tpo $(DEPDIR)/libcurl_la-cf-h1-proxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-h1-proxy.c' object='libcurl_la-cf-h1-proxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cf-h1-proxy.lo `test -f 'cf-h1-proxy.c' || echo '$(srcdir)/'`cf-h1-proxy.c + +libcurl_la-cf-h2-proxy.lo: cf-h2-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cf-h2-proxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cf-h2-proxy.Tpo -c -o libcurl_la-cf-h2-proxy.lo `test -f 'cf-h2-proxy.c' || echo '$(srcdir)/'`cf-h2-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cf-h2-proxy.Tpo $(DEPDIR)/libcurl_la-cf-h2-proxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-h2-proxy.c' object='libcurl_la-cf-h2-proxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cf-h2-proxy.lo `test -f 'cf-h2-proxy.c' || echo '$(srcdir)/'`cf-h2-proxy.c + +libcurl_la-cf-haproxy.lo: cf-haproxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cf-haproxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cf-haproxy.Tpo -c -o libcurl_la-cf-haproxy.lo `test -f 'cf-haproxy.c' || echo '$(srcdir)/'`cf-haproxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cf-haproxy.Tpo $(DEPDIR)/libcurl_la-cf-haproxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-haproxy.c' object='libcurl_la-cf-haproxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cf-haproxy.lo `test -f 'cf-haproxy.c' || echo '$(srcdir)/'`cf-haproxy.c + +libcurl_la-cf-https-connect.lo: cf-https-connect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cf-https-connect.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cf-https-connect.Tpo -c -o libcurl_la-cf-https-connect.lo `test -f 'cf-https-connect.c' || echo '$(srcdir)/'`cf-https-connect.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cf-https-connect.Tpo $(DEPDIR)/libcurl_la-cf-https-connect.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-https-connect.c' object='libcurl_la-cf-https-connect.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cf-https-connect.lo `test -f 'cf-https-connect.c' || echo '$(srcdir)/'`cf-https-connect.c + +libcurl_la-cf-socket.lo: cf-socket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cf-socket.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cf-socket.Tpo -c -o libcurl_la-cf-socket.lo `test -f 'cf-socket.c' || echo '$(srcdir)/'`cf-socket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cf-socket.Tpo $(DEPDIR)/libcurl_la-cf-socket.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-socket.c' object='libcurl_la-cf-socket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cf-socket.lo `test -f 'cf-socket.c' || echo '$(srcdir)/'`cf-socket.c + +libcurl_la-cfilters.lo: cfilters.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cfilters.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cfilters.Tpo -c -o libcurl_la-cfilters.lo `test -f 'cfilters.c' || echo '$(srcdir)/'`cfilters.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cfilters.Tpo $(DEPDIR)/libcurl_la-cfilters.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cfilters.c' object='libcurl_la-cfilters.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cfilters.lo `test -f 'cfilters.c' || echo '$(srcdir)/'`cfilters.c + +libcurl_la-conncache.lo: conncache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-conncache.lo -MD -MP -MF $(DEPDIR)/libcurl_la-conncache.Tpo -c -o libcurl_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-conncache.Tpo $(DEPDIR)/libcurl_la-conncache.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conncache.c' object='libcurl_la-conncache.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c + +libcurl_la-connect.lo: connect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-connect.lo -MD -MP -MF $(DEPDIR)/libcurl_la-connect.Tpo -c -o libcurl_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-connect.Tpo $(DEPDIR)/libcurl_la-connect.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connect.c' object='libcurl_la-connect.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c + +libcurl_la-content_encoding.lo: content_encoding.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-content_encoding.lo -MD -MP -MF $(DEPDIR)/libcurl_la-content_encoding.Tpo -c -o libcurl_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-content_encoding.Tpo $(DEPDIR)/libcurl_la-content_encoding.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='content_encoding.c' object='libcurl_la-content_encoding.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c + +libcurl_la-cookie.lo: cookie.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cookie.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cookie.Tpo -c -o libcurl_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cookie.Tpo $(DEPDIR)/libcurl_la-cookie.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cookie.c' object='libcurl_la-cookie.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c + +libcurl_la-curl_addrinfo.lo: curl_addrinfo.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_addrinfo.Tpo -c -o libcurl_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_addrinfo.Tpo $(DEPDIR)/libcurl_la-curl_addrinfo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_addrinfo.c' object='libcurl_la-curl_addrinfo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c + +libcurl_la-curl_des.lo: curl_des.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_des.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_des.Tpo -c -o libcurl_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_des.Tpo $(DEPDIR)/libcurl_la-curl_des.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_des.c' object='libcurl_la-curl_des.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c + +libcurl_la-curl_endian.lo: curl_endian.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_endian.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_endian.Tpo -c -o libcurl_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_endian.Tpo $(DEPDIR)/libcurl_la-curl_endian.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_endian.c' object='libcurl_la-curl_endian.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c + +libcurl_la-curl_fnmatch.lo: curl_fnmatch.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_fnmatch.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_fnmatch.Tpo -c -o libcurl_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_fnmatch.Tpo $(DEPDIR)/libcurl_la-curl_fnmatch.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_fnmatch.c' object='libcurl_la-curl_fnmatch.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c + +libcurl_la-curl_get_line.lo: curl_get_line.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_get_line.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_get_line.Tpo -c -o libcurl_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_get_line.Tpo $(DEPDIR)/libcurl_la-curl_get_line.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_get_line.c' object='libcurl_la-curl_get_line.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c + +libcurl_la-curl_gethostname.lo: curl_gethostname.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_gethostname.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_gethostname.Tpo -c -o libcurl_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_gethostname.Tpo $(DEPDIR)/libcurl_la-curl_gethostname.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gethostname.c' object='libcurl_la-curl_gethostname.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c + +libcurl_la-curl_gssapi.lo: curl_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_gssapi.Tpo -c -o libcurl_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_gssapi.Tpo $(DEPDIR)/libcurl_la-curl_gssapi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gssapi.c' object='libcurl_la-curl_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c + +libcurl_la-curl_memrchr.lo: curl_memrchr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_memrchr.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_memrchr.Tpo -c -o libcurl_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_memrchr.Tpo $(DEPDIR)/libcurl_la-curl_memrchr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_memrchr.c' object='libcurl_la-curl_memrchr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c + +libcurl_la-curl_multibyte.lo: curl_multibyte.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_multibyte.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_multibyte.Tpo -c -o libcurl_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_multibyte.Tpo $(DEPDIR)/libcurl_la-curl_multibyte.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_multibyte.c' object='libcurl_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c + +libcurl_la-curl_ntlm_core.lo: curl_ntlm_core.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_ntlm_core.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_ntlm_core.Tpo -c -o libcurl_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_ntlm_core.Tpo $(DEPDIR)/libcurl_la-curl_ntlm_core.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_ntlm_core.c' object='libcurl_la-curl_ntlm_core.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c + +libcurl_la-curl_path.lo: curl_path.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_path.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_path.Tpo -c -o libcurl_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_path.Tpo $(DEPDIR)/libcurl_la-curl_path.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_path.c' object='libcurl_la-curl_path.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c + +libcurl_la-curl_range.lo: curl_range.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_range.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_range.Tpo -c -o libcurl_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_range.Tpo $(DEPDIR)/libcurl_la-curl_range.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_range.c' object='libcurl_la-curl_range.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c + +libcurl_la-curl_rtmp.lo: curl_rtmp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_rtmp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_rtmp.Tpo -c -o libcurl_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_rtmp.Tpo $(DEPDIR)/libcurl_la-curl_rtmp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_rtmp.c' object='libcurl_la-curl_rtmp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c + +libcurl_la-curl_sasl.lo: curl_sasl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_sasl.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_sasl.Tpo -c -o libcurl_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_sasl.Tpo $(DEPDIR)/libcurl_la-curl_sasl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sasl.c' object='libcurl_la-curl_sasl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c + +libcurl_la-curl_sha512_256.lo: curl_sha512_256.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_sha512_256.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_sha512_256.Tpo -c -o libcurl_la-curl_sha512_256.lo `test -f 'curl_sha512_256.c' || echo '$(srcdir)/'`curl_sha512_256.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_sha512_256.Tpo $(DEPDIR)/libcurl_la-curl_sha512_256.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sha512_256.c' object='libcurl_la-curl_sha512_256.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_sha512_256.lo `test -f 'curl_sha512_256.c' || echo '$(srcdir)/'`curl_sha512_256.c + +libcurl_la-curl_sspi.lo: curl_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_sspi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_sspi.Tpo -c -o libcurl_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_sspi.Tpo $(DEPDIR)/libcurl_la-curl_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sspi.c' object='libcurl_la-curl_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c + +libcurl_la-curl_threads.lo: curl_threads.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_threads.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_threads.Tpo -c -o libcurl_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_threads.Tpo $(DEPDIR)/libcurl_la-curl_threads.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_threads.c' object='libcurl_la-curl_threads.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c + +libcurl_la-curl_trc.lo: curl_trc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_trc.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_trc.Tpo -c -o libcurl_la-curl_trc.lo `test -f 'curl_trc.c' || echo '$(srcdir)/'`curl_trc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_trc.Tpo $(DEPDIR)/libcurl_la-curl_trc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_trc.c' object='libcurl_la-curl_trc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_trc.lo `test -f 'curl_trc.c' || echo '$(srcdir)/'`curl_trc.c + +libcurl_la-cw-out.lo: cw-out.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cw-out.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cw-out.Tpo -c -o libcurl_la-cw-out.lo `test -f 'cw-out.c' || echo '$(srcdir)/'`cw-out.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cw-out.Tpo $(DEPDIR)/libcurl_la-cw-out.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cw-out.c' object='libcurl_la-cw-out.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cw-out.lo `test -f 'cw-out.c' || echo '$(srcdir)/'`cw-out.c + +libcurl_la-dict.lo: dict.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dict.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dict.Tpo -c -o libcurl_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dict.Tpo $(DEPDIR)/libcurl_la-dict.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dict.c' object='libcurl_la-dict.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c + +libcurl_la-dllmain.lo: dllmain.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dllmain.Tpo -c -o libcurl_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dllmain.Tpo $(DEPDIR)/libcurl_la-dllmain.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dllmain.c' object='libcurl_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c + +libcurl_la-doh.lo: doh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-doh.lo -MD -MP -MF $(DEPDIR)/libcurl_la-doh.Tpo -c -o libcurl_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-doh.Tpo $(DEPDIR)/libcurl_la-doh.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='doh.c' object='libcurl_la-doh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c + +libcurl_la-dynbuf.lo: dynbuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dynbuf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dynbuf.Tpo -c -o libcurl_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dynbuf.Tpo $(DEPDIR)/libcurl_la-dynbuf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dynbuf.c' object='libcurl_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c + +libcurl_la-dynhds.lo: dynhds.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dynhds.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dynhds.Tpo -c -o libcurl_la-dynhds.lo `test -f 'dynhds.c' || echo '$(srcdir)/'`dynhds.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dynhds.Tpo $(DEPDIR)/libcurl_la-dynhds.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dynhds.c' object='libcurl_la-dynhds.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dynhds.lo `test -f 'dynhds.c' || echo '$(srcdir)/'`dynhds.c + +libcurl_la-easy.lo: easy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-easy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-easy.Tpo -c -o libcurl_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-easy.Tpo $(DEPDIR)/libcurl_la-easy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easy.c' object='libcurl_la-easy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c + +libcurl_la-easygetopt.lo: easygetopt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-easygetopt.lo -MD -MP -MF $(DEPDIR)/libcurl_la-easygetopt.Tpo -c -o libcurl_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-easygetopt.Tpo $(DEPDIR)/libcurl_la-easygetopt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easygetopt.c' object='libcurl_la-easygetopt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c + +libcurl_la-easyoptions.lo: easyoptions.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-easyoptions.lo -MD -MP -MF $(DEPDIR)/libcurl_la-easyoptions.Tpo -c -o libcurl_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-easyoptions.Tpo $(DEPDIR)/libcurl_la-easyoptions.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easyoptions.c' object='libcurl_la-easyoptions.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c + +libcurl_la-escape.lo: escape.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-escape.lo -MD -MP -MF $(DEPDIR)/libcurl_la-escape.Tpo -c -o libcurl_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-escape.Tpo $(DEPDIR)/libcurl_la-escape.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='escape.c' object='libcurl_la-escape.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c + +libcurl_la-file.lo: file.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-file.lo -MD -MP -MF $(DEPDIR)/libcurl_la-file.Tpo -c -o libcurl_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-file.Tpo $(DEPDIR)/libcurl_la-file.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file.c' object='libcurl_la-file.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c + +libcurl_la-fileinfo.lo: fileinfo.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-fileinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-fileinfo.Tpo -c -o libcurl_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-fileinfo.Tpo $(DEPDIR)/libcurl_la-fileinfo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fileinfo.c' object='libcurl_la-fileinfo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c + +libcurl_la-fopen.lo: fopen.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-fopen.lo -MD -MP -MF $(DEPDIR)/libcurl_la-fopen.Tpo -c -o libcurl_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-fopen.Tpo $(DEPDIR)/libcurl_la-fopen.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fopen.c' object='libcurl_la-fopen.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c + +libcurl_la-formdata.lo: formdata.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-formdata.lo -MD -MP -MF $(DEPDIR)/libcurl_la-formdata.Tpo -c -o libcurl_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-formdata.Tpo $(DEPDIR)/libcurl_la-formdata.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='formdata.c' object='libcurl_la-formdata.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c + +libcurl_la-ftp.lo: ftp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ftp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ftp.Tpo -c -o libcurl_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ftp.Tpo $(DEPDIR)/libcurl_la-ftp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftp.c' object='libcurl_la-ftp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c + +libcurl_la-ftplistparser.lo: ftplistparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ftplistparser.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ftplistparser.Tpo -c -o libcurl_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ftplistparser.Tpo $(DEPDIR)/libcurl_la-ftplistparser.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftplistparser.c' object='libcurl_la-ftplistparser.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c + +libcurl_la-getenv.lo: getenv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-getenv.lo -MD -MP -MF $(DEPDIR)/libcurl_la-getenv.Tpo -c -o libcurl_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-getenv.Tpo $(DEPDIR)/libcurl_la-getenv.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getenv.c' object='libcurl_la-getenv.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c + +libcurl_la-getinfo.lo: getinfo.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-getinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-getinfo.Tpo -c -o libcurl_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-getinfo.Tpo $(DEPDIR)/libcurl_la-getinfo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getinfo.c' object='libcurl_la-getinfo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c + +libcurl_la-gopher.lo: gopher.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-gopher.lo -MD -MP -MF $(DEPDIR)/libcurl_la-gopher.Tpo -c -o libcurl_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-gopher.Tpo $(DEPDIR)/libcurl_la-gopher.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gopher.c' object='libcurl_la-gopher.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c + +libcurl_la-hash.lo: hash.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hash.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hash.Tpo -c -o libcurl_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hash.Tpo $(DEPDIR)/libcurl_la-hash.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hash.c' object='libcurl_la-hash.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c + +libcurl_la-headers.lo: headers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-headers.lo -MD -MP -MF $(DEPDIR)/libcurl_la-headers.Tpo -c -o libcurl_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-headers.Tpo $(DEPDIR)/libcurl_la-headers.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='headers.c' object='libcurl_la-headers.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c + +libcurl_la-hmac.lo: hmac.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hmac.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hmac.Tpo -c -o libcurl_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hmac.Tpo $(DEPDIR)/libcurl_la-hmac.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hmac.c' object='libcurl_la-hmac.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c + +libcurl_la-hostasyn.lo: hostasyn.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostasyn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostasyn.Tpo -c -o libcurl_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostasyn.Tpo $(DEPDIR)/libcurl_la-hostasyn.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostasyn.c' object='libcurl_la-hostasyn.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c + +libcurl_la-hostip.lo: hostip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip.Tpo -c -o libcurl_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip.Tpo $(DEPDIR)/libcurl_la-hostip.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip.c' object='libcurl_la-hostip.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c + +libcurl_la-hostip4.lo: hostip4.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip4.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip4.Tpo -c -o libcurl_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip4.Tpo $(DEPDIR)/libcurl_la-hostip4.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip4.c' object='libcurl_la-hostip4.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c + +libcurl_la-hostip6.lo: hostip6.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip6.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip6.Tpo -c -o libcurl_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip6.Tpo $(DEPDIR)/libcurl_la-hostip6.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip6.c' object='libcurl_la-hostip6.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c + +libcurl_la-hostsyn.lo: hostsyn.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostsyn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostsyn.Tpo -c -o libcurl_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostsyn.Tpo $(DEPDIR)/libcurl_la-hostsyn.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostsyn.c' object='libcurl_la-hostsyn.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c + +libcurl_la-hsts.lo: hsts.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hsts.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hsts.Tpo -c -o libcurl_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hsts.Tpo $(DEPDIR)/libcurl_la-hsts.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hsts.c' object='libcurl_la-hsts.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c + +libcurl_la-http.lo: http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http.Tpo -c -o libcurl_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http.Tpo $(DEPDIR)/libcurl_la-http.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='libcurl_la-http.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c + +libcurl_la-http1.lo: http1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http1.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http1.Tpo -c -o libcurl_la-http1.lo `test -f 'http1.c' || echo '$(srcdir)/'`http1.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http1.Tpo $(DEPDIR)/libcurl_la-http1.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http1.c' object='libcurl_la-http1.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http1.lo `test -f 'http1.c' || echo '$(srcdir)/'`http1.c + +libcurl_la-http2.lo: http2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http2.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http2.Tpo -c -o libcurl_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http2.Tpo $(DEPDIR)/libcurl_la-http2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http2.c' object='libcurl_la-http2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c + +libcurl_la-http_aws_sigv4.lo: http_aws_sigv4.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_aws_sigv4.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_aws_sigv4.Tpo -c -o libcurl_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_aws_sigv4.Tpo $(DEPDIR)/libcurl_la-http_aws_sigv4.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_aws_sigv4.c' object='libcurl_la-http_aws_sigv4.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c + +libcurl_la-http_chunks.lo: http_chunks.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_chunks.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_chunks.Tpo -c -o libcurl_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_chunks.Tpo $(DEPDIR)/libcurl_la-http_chunks.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_chunks.c' object='libcurl_la-http_chunks.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c + +libcurl_la-http_digest.lo: http_digest.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_digest.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_digest.Tpo -c -o libcurl_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_digest.Tpo $(DEPDIR)/libcurl_la-http_digest.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_digest.c' object='libcurl_la-http_digest.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c + +libcurl_la-http_negotiate.lo: http_negotiate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_negotiate.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_negotiate.Tpo -c -o libcurl_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_negotiate.Tpo $(DEPDIR)/libcurl_la-http_negotiate.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_negotiate.c' object='libcurl_la-http_negotiate.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c + +libcurl_la-http_ntlm.lo: http_ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_ntlm.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_ntlm.Tpo -c -o libcurl_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_ntlm.Tpo $(DEPDIR)/libcurl_la-http_ntlm.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_ntlm.c' object='libcurl_la-http_ntlm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c + +libcurl_la-http_proxy.lo: http_proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_proxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_proxy.Tpo -c -o libcurl_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_proxy.Tpo $(DEPDIR)/libcurl_la-http_proxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_proxy.c' object='libcurl_la-http_proxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c + +libcurl_la-idn.lo: idn.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-idn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-idn.Tpo -c -o libcurl_la-idn.lo `test -f 'idn.c' || echo '$(srcdir)/'`idn.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-idn.Tpo $(DEPDIR)/libcurl_la-idn.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='idn.c' object='libcurl_la-idn.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-idn.lo `test -f 'idn.c' || echo '$(srcdir)/'`idn.c + +libcurl_la-if2ip.lo: if2ip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-if2ip.lo -MD -MP -MF $(DEPDIR)/libcurl_la-if2ip.Tpo -c -o libcurl_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-if2ip.Tpo $(DEPDIR)/libcurl_la-if2ip.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='if2ip.c' object='libcurl_la-if2ip.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c + +libcurl_la-imap.lo: imap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-imap.lo -MD -MP -MF $(DEPDIR)/libcurl_la-imap.Tpo -c -o libcurl_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-imap.Tpo $(DEPDIR)/libcurl_la-imap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap.c' object='libcurl_la-imap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c + +libcurl_la-inet_ntop.lo: inet_ntop.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-inet_ntop.lo -MD -MP -MF $(DEPDIR)/libcurl_la-inet_ntop.Tpo -c -o libcurl_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-inet_ntop.Tpo $(DEPDIR)/libcurl_la-inet_ntop.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_ntop.c' object='libcurl_la-inet_ntop.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c + +libcurl_la-inet_pton.lo: inet_pton.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-inet_pton.lo -MD -MP -MF $(DEPDIR)/libcurl_la-inet_pton.Tpo -c -o libcurl_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-inet_pton.Tpo $(DEPDIR)/libcurl_la-inet_pton.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_pton.c' object='libcurl_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c + +libcurl_la-krb5.lo: krb5.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-krb5.lo -MD -MP -MF $(DEPDIR)/libcurl_la-krb5.Tpo -c -o libcurl_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-krb5.Tpo $(DEPDIR)/libcurl_la-krb5.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5.c' object='libcurl_la-krb5.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c + +libcurl_la-ldap.lo: ldap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ldap.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ldap.Tpo -c -o libcurl_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ldap.Tpo $(DEPDIR)/libcurl_la-ldap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap.c' object='libcurl_la-ldap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c + +libcurl_la-llist.lo: llist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-llist.lo -MD -MP -MF $(DEPDIR)/libcurl_la-llist.Tpo -c -o libcurl_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-llist.Tpo $(DEPDIR)/libcurl_la-llist.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='llist.c' object='libcurl_la-llist.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c + +libcurl_la-macos.lo: macos.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-macos.lo -MD -MP -MF $(DEPDIR)/libcurl_la-macos.Tpo -c -o libcurl_la-macos.lo `test -f 'macos.c' || echo '$(srcdir)/'`macos.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-macos.Tpo $(DEPDIR)/libcurl_la-macos.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='macos.c' object='libcurl_la-macos.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-macos.lo `test -f 'macos.c' || echo '$(srcdir)/'`macos.c + +libcurl_la-md4.lo: md4.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-md4.lo -MD -MP -MF $(DEPDIR)/libcurl_la-md4.Tpo -c -o libcurl_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-md4.Tpo $(DEPDIR)/libcurl_la-md4.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md4.c' object='libcurl_la-md4.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c + +libcurl_la-md5.lo: md5.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-md5.lo -MD -MP -MF $(DEPDIR)/libcurl_la-md5.Tpo -c -o libcurl_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-md5.Tpo $(DEPDIR)/libcurl_la-md5.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='libcurl_la-md5.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c + +libcurl_la-memdebug.lo: memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-memdebug.lo -MD -MP -MF $(DEPDIR)/libcurl_la-memdebug.Tpo -c -o libcurl_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-memdebug.Tpo $(DEPDIR)/libcurl_la-memdebug.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='memdebug.c' object='libcurl_la-memdebug.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c + +libcurl_la-mime.lo: mime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-mime.lo -MD -MP -MF $(DEPDIR)/libcurl_la-mime.Tpo -c -o libcurl_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-mime.Tpo $(DEPDIR)/libcurl_la-mime.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mime.c' object='libcurl_la-mime.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c + +libcurl_la-mprintf.lo: mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-mprintf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-mprintf.Tpo -c -o libcurl_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-mprintf.Tpo $(DEPDIR)/libcurl_la-mprintf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mprintf.c' object='libcurl_la-mprintf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c + +libcurl_la-mqtt.lo: mqtt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-mqtt.lo -MD -MP -MF $(DEPDIR)/libcurl_la-mqtt.Tpo -c -o libcurl_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-mqtt.Tpo $(DEPDIR)/libcurl_la-mqtt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mqtt.c' object='libcurl_la-mqtt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c + +libcurl_la-multi.lo: multi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-multi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-multi.Tpo -c -o libcurl_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-multi.Tpo $(DEPDIR)/libcurl_la-multi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='multi.c' object='libcurl_la-multi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c + +libcurl_la-netrc.lo: netrc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-netrc.lo -MD -MP -MF $(DEPDIR)/libcurl_la-netrc.Tpo -c -o libcurl_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-netrc.Tpo $(DEPDIR)/libcurl_la-netrc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netrc.c' object='libcurl_la-netrc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c + +libcurl_la-nonblock.lo: nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-nonblock.lo -MD -MP -MF $(DEPDIR)/libcurl_la-nonblock.Tpo -c -o libcurl_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-nonblock.Tpo $(DEPDIR)/libcurl_la-nonblock.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nonblock.c' object='libcurl_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c + +libcurl_la-noproxy.lo: noproxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-noproxy.Tpo -c -o libcurl_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-noproxy.Tpo $(DEPDIR)/libcurl_la-noproxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='noproxy.c' object='libcurl_la-noproxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c + +libcurl_la-openldap.lo: openldap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-openldap.lo -MD -MP -MF $(DEPDIR)/libcurl_la-openldap.Tpo -c -o libcurl_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-openldap.Tpo $(DEPDIR)/libcurl_la-openldap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openldap.c' object='libcurl_la-openldap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c + +libcurl_la-parsedate.lo: parsedate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-parsedate.lo -MD -MP -MF $(DEPDIR)/libcurl_la-parsedate.Tpo -c -o libcurl_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-parsedate.Tpo $(DEPDIR)/libcurl_la-parsedate.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parsedate.c' object='libcurl_la-parsedate.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c + +libcurl_la-pingpong.lo: pingpong.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-pingpong.lo -MD -MP -MF $(DEPDIR)/libcurl_la-pingpong.Tpo -c -o libcurl_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-pingpong.Tpo $(DEPDIR)/libcurl_la-pingpong.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pingpong.c' object='libcurl_la-pingpong.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c + +libcurl_la-pop3.lo: pop3.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-pop3.lo -MD -MP -MF $(DEPDIR)/libcurl_la-pop3.Tpo -c -o libcurl_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-pop3.Tpo $(DEPDIR)/libcurl_la-pop3.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pop3.c' object='libcurl_la-pop3.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c + +libcurl_la-progress.lo: progress.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-progress.lo -MD -MP -MF $(DEPDIR)/libcurl_la-progress.Tpo -c -o libcurl_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-progress.Tpo $(DEPDIR)/libcurl_la-progress.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='progress.c' object='libcurl_la-progress.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c + +libcurl_la-psl.lo: psl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-psl.lo -MD -MP -MF $(DEPDIR)/libcurl_la-psl.Tpo -c -o libcurl_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-psl.Tpo $(DEPDIR)/libcurl_la-psl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='psl.c' object='libcurl_la-psl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c + +libcurl_la-rand.lo: rand.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-rand.lo -MD -MP -MF $(DEPDIR)/libcurl_la-rand.Tpo -c -o libcurl_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-rand.Tpo $(DEPDIR)/libcurl_la-rand.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='libcurl_la-rand.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c + +libcurl_la-rename.lo: rename.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-rename.lo -MD -MP -MF $(DEPDIR)/libcurl_la-rename.Tpo -c -o libcurl_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-rename.Tpo $(DEPDIR)/libcurl_la-rename.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rename.c' object='libcurl_la-rename.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c + +libcurl_la-request.lo: request.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-request.lo -MD -MP -MF $(DEPDIR)/libcurl_la-request.Tpo -c -o libcurl_la-request.lo `test -f 'request.c' || echo '$(srcdir)/'`request.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-request.Tpo $(DEPDIR)/libcurl_la-request.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='request.c' object='libcurl_la-request.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-request.lo `test -f 'request.c' || echo '$(srcdir)/'`request.c + +libcurl_la-rtsp.lo: rtsp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-rtsp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-rtsp.Tpo -c -o libcurl_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-rtsp.Tpo $(DEPDIR)/libcurl_la-rtsp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtsp.c' object='libcurl_la-rtsp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c + +libcurl_la-select.lo: select.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-select.lo -MD -MP -MF $(DEPDIR)/libcurl_la-select.Tpo -c -o libcurl_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-select.Tpo $(DEPDIR)/libcurl_la-select.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='select.c' object='libcurl_la-select.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c + +libcurl_la-sendf.lo: sendf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-sendf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-sendf.Tpo -c -o libcurl_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-sendf.Tpo $(DEPDIR)/libcurl_la-sendf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sendf.c' object='libcurl_la-sendf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c + +libcurl_la-setopt.lo: setopt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-setopt.lo -MD -MP -MF $(DEPDIR)/libcurl_la-setopt.Tpo -c -o libcurl_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-setopt.Tpo $(DEPDIR)/libcurl_la-setopt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='setopt.c' object='libcurl_la-setopt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c + +libcurl_la-sha256.lo: sha256.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-sha256.lo -MD -MP -MF $(DEPDIR)/libcurl_la-sha256.Tpo -c -o libcurl_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-sha256.Tpo $(DEPDIR)/libcurl_la-sha256.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sha256.c' object='libcurl_la-sha256.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c + +libcurl_la-share.lo: share.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-share.lo -MD -MP -MF $(DEPDIR)/libcurl_la-share.Tpo -c -o libcurl_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-share.Tpo $(DEPDIR)/libcurl_la-share.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='share.c' object='libcurl_la-share.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c + +libcurl_la-slist.lo: slist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-slist.lo -MD -MP -MF $(DEPDIR)/libcurl_la-slist.Tpo -c -o libcurl_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-slist.Tpo $(DEPDIR)/libcurl_la-slist.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='slist.c' object='libcurl_la-slist.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c + +libcurl_la-smb.lo: smb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-smb.lo -MD -MP -MF $(DEPDIR)/libcurl_la-smb.Tpo -c -o libcurl_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-smb.Tpo $(DEPDIR)/libcurl_la-smb.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smb.c' object='libcurl_la-smb.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c + +libcurl_la-smtp.lo: smtp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-smtp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-smtp.Tpo -c -o libcurl_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-smtp.Tpo $(DEPDIR)/libcurl_la-smtp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smtp.c' object='libcurl_la-smtp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c + +libcurl_la-socketpair.lo: socketpair.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socketpair.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socketpair.Tpo -c -o libcurl_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socketpair.Tpo $(DEPDIR)/libcurl_la-socketpair.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socketpair.c' object='libcurl_la-socketpair.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c + +libcurl_la-socks.lo: socks.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socks.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socks.Tpo -c -o libcurl_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socks.Tpo $(DEPDIR)/libcurl_la-socks.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks.c' object='libcurl_la-socks.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c + +libcurl_la-socks_gssapi.lo: socks_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socks_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socks_gssapi.Tpo -c -o libcurl_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socks_gssapi.Tpo $(DEPDIR)/libcurl_la-socks_gssapi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_gssapi.c' object='libcurl_la-socks_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c + +libcurl_la-socks_sspi.lo: socks_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socks_sspi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socks_sspi.Tpo -c -o libcurl_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socks_sspi.Tpo $(DEPDIR)/libcurl_la-socks_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_sspi.c' object='libcurl_la-socks_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c + +libcurl_la-speedcheck.lo: speedcheck.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-speedcheck.lo -MD -MP -MF $(DEPDIR)/libcurl_la-speedcheck.Tpo -c -o libcurl_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-speedcheck.Tpo $(DEPDIR)/libcurl_la-speedcheck.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='speedcheck.c' object='libcurl_la-speedcheck.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c + +libcurl_la-splay.lo: splay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-splay.lo -MD -MP -MF $(DEPDIR)/libcurl_la-splay.Tpo -c -o libcurl_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-splay.Tpo $(DEPDIR)/libcurl_la-splay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='splay.c' object='libcurl_la-splay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c + +libcurl_la-strcase.lo: strcase.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strcase.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strcase.Tpo -c -o libcurl_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strcase.Tpo $(DEPDIR)/libcurl_la-strcase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strcase.c' object='libcurl_la-strcase.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c + +libcurl_la-strdup.lo: strdup.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strdup.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strdup.Tpo -c -o libcurl_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strdup.Tpo $(DEPDIR)/libcurl_la-strdup.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strdup.c' object='libcurl_la-strdup.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c + +libcurl_la-strerror.lo: strerror.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strerror.Tpo -c -o libcurl_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strerror.Tpo $(DEPDIR)/libcurl_la-strerror.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strerror.c' object='libcurl_la-strerror.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c + +libcurl_la-strtok.lo: strtok.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strtok.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strtok.Tpo -c -o libcurl_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strtok.Tpo $(DEPDIR)/libcurl_la-strtok.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtok.c' object='libcurl_la-strtok.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c + +libcurl_la-strtoofft.lo: strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strtoofft.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strtoofft.Tpo -c -o libcurl_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strtoofft.Tpo $(DEPDIR)/libcurl_la-strtoofft.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtoofft.c' object='libcurl_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c + +libcurl_la-system_win32.lo: system_win32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-system_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-system_win32.Tpo -c -o libcurl_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-system_win32.Tpo $(DEPDIR)/libcurl_la-system_win32.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='system_win32.c' object='libcurl_la-system_win32.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c + +libcurl_la-telnet.lo: telnet.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-telnet.lo -MD -MP -MF $(DEPDIR)/libcurl_la-telnet.Tpo -c -o libcurl_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-telnet.Tpo $(DEPDIR)/libcurl_la-telnet.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='telnet.c' object='libcurl_la-telnet.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c + +libcurl_la-tftp.lo: tftp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-tftp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-tftp.Tpo -c -o libcurl_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-tftp.Tpo $(DEPDIR)/libcurl_la-tftp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tftp.c' object='libcurl_la-tftp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c + +libcurl_la-timediff.lo: timediff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-timediff.lo -MD -MP -MF $(DEPDIR)/libcurl_la-timediff.Tpo -c -o libcurl_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-timediff.Tpo $(DEPDIR)/libcurl_la-timediff.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timediff.c' object='libcurl_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c + +libcurl_la-timeval.lo: timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-timeval.lo -MD -MP -MF $(DEPDIR)/libcurl_la-timeval.Tpo -c -o libcurl_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-timeval.Tpo $(DEPDIR)/libcurl_la-timeval.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timeval.c' object='libcurl_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c + +libcurl_la-transfer.lo: transfer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-transfer.lo -MD -MP -MF $(DEPDIR)/libcurl_la-transfer.Tpo -c -o libcurl_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-transfer.Tpo $(DEPDIR)/libcurl_la-transfer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='transfer.c' object='libcurl_la-transfer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c + +libcurl_la-url.lo: url.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-url.lo -MD -MP -MF $(DEPDIR)/libcurl_la-url.Tpo -c -o libcurl_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-url.Tpo $(DEPDIR)/libcurl_la-url.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='url.c' object='libcurl_la-url.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c + +libcurl_la-urlapi.lo: urlapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-urlapi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-urlapi.Tpo -c -o libcurl_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-urlapi.Tpo $(DEPDIR)/libcurl_la-urlapi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='urlapi.c' object='libcurl_la-urlapi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c + +libcurl_la-version.lo: version.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-version.lo -MD -MP -MF $(DEPDIR)/libcurl_la-version.Tpo -c -o libcurl_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-version.Tpo $(DEPDIR)/libcurl_la-version.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version.c' object='libcurl_la-version.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c + +libcurl_la-version_win32.lo: version_win32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-version_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-version_win32.Tpo -c -o libcurl_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-version_win32.Tpo $(DEPDIR)/libcurl_la-version_win32.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version_win32.c' object='libcurl_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c + +libcurl_la-warnless.lo: warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-warnless.lo -MD -MP -MF $(DEPDIR)/libcurl_la-warnless.Tpo -c -o libcurl_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-warnless.Tpo $(DEPDIR)/libcurl_la-warnless.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warnless.c' object='libcurl_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c + +libcurl_la-ws.lo: ws.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ws.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ws.Tpo -c -o libcurl_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ws.Tpo $(DEPDIR)/libcurl_la-ws.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ws.c' object='libcurl_la-ws.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c + +vauth/libcurl_la-cleartext.lo: vauth/cleartext.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-cleartext.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-cleartext.Tpo -c -o vauth/libcurl_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-cleartext.Tpo vauth/$(DEPDIR)/libcurl_la-cleartext.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cleartext.c' object='vauth/libcurl_la-cleartext.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c + +vauth/libcurl_la-cram.lo: vauth/cram.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-cram.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-cram.Tpo -c -o vauth/libcurl_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-cram.Tpo vauth/$(DEPDIR)/libcurl_la-cram.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cram.c' object='vauth/libcurl_la-cram.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c + +vauth/libcurl_la-digest.lo: vauth/digest.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-digest.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-digest.Tpo -c -o vauth/libcurl_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-digest.Tpo vauth/$(DEPDIR)/libcurl_la-digest.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest.c' object='vauth/libcurl_la-digest.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c + +vauth/libcurl_la-digest_sspi.lo: vauth/digest_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-digest_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-digest_sspi.Tpo -c -o vauth/libcurl_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-digest_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest_sspi.c' object='vauth/libcurl_la-digest_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c + +vauth/libcurl_la-gsasl.lo: vauth/gsasl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-gsasl.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-gsasl.Tpo -c -o vauth/libcurl_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-gsasl.Tpo vauth/$(DEPDIR)/libcurl_la-gsasl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/gsasl.c' object='vauth/libcurl_la-gsasl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c + +vauth/libcurl_la-krb5_gssapi.lo: vauth/krb5_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-krb5_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Tpo -c -o vauth/libcurl_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Tpo vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_gssapi.c' object='vauth/libcurl_la-krb5_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c + +vauth/libcurl_la-krb5_sspi.lo: vauth/krb5_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-krb5_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Tpo -c -o vauth/libcurl_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_sspi.c' object='vauth/libcurl_la-krb5_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c + +vauth/libcurl_la-ntlm.lo: vauth/ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-ntlm.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-ntlm.Tpo -c -o vauth/libcurl_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-ntlm.Tpo vauth/$(DEPDIR)/libcurl_la-ntlm.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm.c' object='vauth/libcurl_la-ntlm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c + +vauth/libcurl_la-ntlm_sspi.lo: vauth/ntlm_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-ntlm_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Tpo -c -o vauth/libcurl_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm_sspi.c' object='vauth/libcurl_la-ntlm_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c + +vauth/libcurl_la-oauth2.lo: vauth/oauth2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-oauth2.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-oauth2.Tpo -c -o vauth/libcurl_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-oauth2.Tpo vauth/$(DEPDIR)/libcurl_la-oauth2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/oauth2.c' object='vauth/libcurl_la-oauth2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c + +vauth/libcurl_la-spnego_gssapi.lo: vauth/spnego_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-spnego_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Tpo -c -o vauth/libcurl_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Tpo vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_gssapi.c' object='vauth/libcurl_la-spnego_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c + +vauth/libcurl_la-spnego_sspi.lo: vauth/spnego_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-spnego_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Tpo -c -o vauth/libcurl_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_sspi.c' object='vauth/libcurl_la-spnego_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c + +vauth/libcurl_la-vauth.lo: vauth/vauth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-vauth.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-vauth.Tpo -c -o vauth/libcurl_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-vauth.Tpo vauth/$(DEPDIR)/libcurl_la-vauth.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/vauth.c' object='vauth/libcurl_la-vauth.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c + +vtls/libcurl_la-bearssl.lo: vtls/bearssl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-bearssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-bearssl.Tpo -c -o vtls/libcurl_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-bearssl.Tpo vtls/$(DEPDIR)/libcurl_la-bearssl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/bearssl.c' object='vtls/libcurl_la-bearssl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c + +vtls/libcurl_la-cipher_suite.lo: vtls/cipher_suite.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-cipher_suite.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-cipher_suite.Tpo -c -o vtls/libcurl_la-cipher_suite.lo `test -f 'vtls/cipher_suite.c' || echo '$(srcdir)/'`vtls/cipher_suite.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-cipher_suite.Tpo vtls/$(DEPDIR)/libcurl_la-cipher_suite.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/cipher_suite.c' object='vtls/libcurl_la-cipher_suite.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-cipher_suite.lo `test -f 'vtls/cipher_suite.c' || echo '$(srcdir)/'`vtls/cipher_suite.c + +vtls/libcurl_la-gtls.lo: vtls/gtls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-gtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-gtls.Tpo -c -o vtls/libcurl_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-gtls.Tpo vtls/$(DEPDIR)/libcurl_la-gtls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/gtls.c' object='vtls/libcurl_la-gtls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c + +vtls/libcurl_la-hostcheck.lo: vtls/hostcheck.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-hostcheck.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-hostcheck.Tpo -c -o vtls/libcurl_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-hostcheck.Tpo vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/hostcheck.c' object='vtls/libcurl_la-hostcheck.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c + +vtls/libcurl_la-keylog.lo: vtls/keylog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-keylog.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-keylog.Tpo -c -o vtls/libcurl_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-keylog.Tpo vtls/$(DEPDIR)/libcurl_la-keylog.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/keylog.c' object='vtls/libcurl_la-keylog.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c + +vtls/libcurl_la-mbedtls.lo: vtls/mbedtls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-mbedtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-mbedtls.Tpo -c -o vtls/libcurl_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-mbedtls.Tpo vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls.c' object='vtls/libcurl_la-mbedtls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c + +vtls/libcurl_la-mbedtls_threadlock.lo: vtls/mbedtls_threadlock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-mbedtls_threadlock.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Tpo -c -o vtls/libcurl_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Tpo vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls_threadlock.c' object='vtls/libcurl_la-mbedtls_threadlock.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c + +vtls/libcurl_la-openssl.lo: vtls/openssl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-openssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-openssl.Tpo -c -o vtls/libcurl_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-openssl.Tpo vtls/$(DEPDIR)/libcurl_la-openssl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/openssl.c' object='vtls/libcurl_la-openssl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c + +vtls/libcurl_la-rustls.lo: vtls/rustls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-rustls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-rustls.Tpo -c -o vtls/libcurl_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-rustls.Tpo vtls/$(DEPDIR)/libcurl_la-rustls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/rustls.c' object='vtls/libcurl_la-rustls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c + +vtls/libcurl_la-schannel.lo: vtls/schannel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-schannel.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-schannel.Tpo -c -o vtls/libcurl_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-schannel.Tpo vtls/$(DEPDIR)/libcurl_la-schannel.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel.c' object='vtls/libcurl_la-schannel.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c + +vtls/libcurl_la-schannel_verify.lo: vtls/schannel_verify.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-schannel_verify.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-schannel_verify.Tpo -c -o vtls/libcurl_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-schannel_verify.Tpo vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel_verify.c' object='vtls/libcurl_la-schannel_verify.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c + +vtls/libcurl_la-sectransp.lo: vtls/sectransp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-sectransp.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-sectransp.Tpo -c -o vtls/libcurl_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-sectransp.Tpo vtls/$(DEPDIR)/libcurl_la-sectransp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/sectransp.c' object='vtls/libcurl_la-sectransp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c + +vtls/libcurl_la-vtls.lo: vtls/vtls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-vtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-vtls.Tpo -c -o vtls/libcurl_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-vtls.Tpo vtls/$(DEPDIR)/libcurl_la-vtls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls.c' object='vtls/libcurl_la-vtls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c + +vtls/libcurl_la-wolfssl.lo: vtls/wolfssl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-wolfssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-wolfssl.Tpo -c -o vtls/libcurl_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-wolfssl.Tpo vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/wolfssl.c' object='vtls/libcurl_la-wolfssl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c + +vtls/libcurl_la-x509asn1.lo: vtls/x509asn1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-x509asn1.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-x509asn1.Tpo -c -o vtls/libcurl_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-x509asn1.Tpo vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/x509asn1.c' object='vtls/libcurl_la-x509asn1.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c + +vquic/libcurl_la-curl_msh3.lo: vquic/curl_msh3.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-curl_msh3.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-curl_msh3.Tpo -c -o vquic/libcurl_la-curl_msh3.lo `test -f 'vquic/curl_msh3.c' || echo '$(srcdir)/'`vquic/curl_msh3.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-curl_msh3.Tpo vquic/$(DEPDIR)/libcurl_la-curl_msh3.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_msh3.c' object='vquic/libcurl_la-curl_msh3.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-curl_msh3.lo `test -f 'vquic/curl_msh3.c' || echo '$(srcdir)/'`vquic/curl_msh3.c + +vquic/libcurl_la-curl_ngtcp2.lo: vquic/curl_ngtcp2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-curl_ngtcp2.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Tpo -c -o vquic/libcurl_la-curl_ngtcp2.lo `test -f 'vquic/curl_ngtcp2.c' || echo '$(srcdir)/'`vquic/curl_ngtcp2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Tpo vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_ngtcp2.c' object='vquic/libcurl_la-curl_ngtcp2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-curl_ngtcp2.lo `test -f 'vquic/curl_ngtcp2.c' || echo '$(srcdir)/'`vquic/curl_ngtcp2.c + +vquic/libcurl_la-curl_osslq.lo: vquic/curl_osslq.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-curl_osslq.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-curl_osslq.Tpo -c -o vquic/libcurl_la-curl_osslq.lo `test -f 'vquic/curl_osslq.c' || echo '$(srcdir)/'`vquic/curl_osslq.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-curl_osslq.Tpo vquic/$(DEPDIR)/libcurl_la-curl_osslq.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_osslq.c' object='vquic/libcurl_la-curl_osslq.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-curl_osslq.lo `test -f 'vquic/curl_osslq.c' || echo '$(srcdir)/'`vquic/curl_osslq.c + +vquic/libcurl_la-curl_quiche.lo: vquic/curl_quiche.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-curl_quiche.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-curl_quiche.Tpo -c -o vquic/libcurl_la-curl_quiche.lo `test -f 'vquic/curl_quiche.c' || echo '$(srcdir)/'`vquic/curl_quiche.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-curl_quiche.Tpo vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_quiche.c' object='vquic/libcurl_la-curl_quiche.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-curl_quiche.lo `test -f 'vquic/curl_quiche.c' || echo '$(srcdir)/'`vquic/curl_quiche.c + +vquic/libcurl_la-vquic.lo: vquic/vquic.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-vquic.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-vquic.Tpo -c -o vquic/libcurl_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-vquic.Tpo vquic/$(DEPDIR)/libcurl_la-vquic.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/vquic.c' object='vquic/libcurl_la-vquic.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c + +vquic/libcurl_la-vquic-tls.lo: vquic/vquic-tls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-vquic-tls.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-vquic-tls.Tpo -c -o vquic/libcurl_la-vquic-tls.lo `test -f 'vquic/vquic-tls.c' || echo '$(srcdir)/'`vquic/vquic-tls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-vquic-tls.Tpo vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/vquic-tls.c' object='vquic/libcurl_la-vquic-tls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-vquic-tls.lo `test -f 'vquic/vquic-tls.c' || echo '$(srcdir)/'`vquic/vquic-tls.c + +vssh/libcurl_la-libssh.lo: vssh/libssh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vssh/libcurl_la-libssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurl_la-libssh.Tpo -c -o vssh/libcurl_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurl_la-libssh.Tpo vssh/$(DEPDIR)/libcurl_la-libssh.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh.c' object='vssh/libcurl_la-libssh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurl_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c + +vssh/libcurl_la-libssh2.lo: vssh/libssh2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vssh/libcurl_la-libssh2.lo -MD -MP -MF vssh/$(DEPDIR)/libcurl_la-libssh2.Tpo -c -o vssh/libcurl_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurl_la-libssh2.Tpo vssh/$(DEPDIR)/libcurl_la-libssh2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh2.c' object='vssh/libcurl_la-libssh2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurl_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c + +vssh/libcurl_la-wolfssh.lo: vssh/wolfssh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vssh/libcurl_la-wolfssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurl_la-wolfssh.Tpo -c -o vssh/libcurl_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurl_la-wolfssh.Tpo vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/wolfssh.c' object='vssh/libcurl_la-wolfssh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurl_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c + +libcurlu_la-altsvc.lo: altsvc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-altsvc.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-altsvc.Tpo -c -o libcurlu_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-altsvc.Tpo $(DEPDIR)/libcurlu_la-altsvc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='altsvc.c' object='libcurlu_la-altsvc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c + +libcurlu_la-amigaos.lo: amigaos.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-amigaos.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-amigaos.Tpo -c -o libcurlu_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-amigaos.Tpo $(DEPDIR)/libcurlu_la-amigaos.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='amigaos.c' object='libcurlu_la-amigaos.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c + +libcurlu_la-asyn-ares.lo: asyn-ares.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-ares.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-ares.Tpo -c -o libcurlu_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-ares.Tpo $(DEPDIR)/libcurlu_la-asyn-ares.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-ares.c' object='libcurlu_la-asyn-ares.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c + +libcurlu_la-asyn-thread.lo: asyn-thread.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-thread.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-thread.Tpo -c -o libcurlu_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-thread.Tpo $(DEPDIR)/libcurlu_la-asyn-thread.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-thread.c' object='libcurlu_la-asyn-thread.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c + +libcurlu_la-base64.lo: base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-base64.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-base64.Tpo -c -o libcurlu_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-base64.Tpo $(DEPDIR)/libcurlu_la-base64.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libcurlu_la-base64.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c + +libcurlu_la-bufq.lo: bufq.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-bufq.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-bufq.Tpo -c -o libcurlu_la-bufq.lo `test -f 'bufq.c' || echo '$(srcdir)/'`bufq.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-bufq.Tpo $(DEPDIR)/libcurlu_la-bufq.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bufq.c' object='libcurlu_la-bufq.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-bufq.lo `test -f 'bufq.c' || echo '$(srcdir)/'`bufq.c + +libcurlu_la-bufref.lo: bufref.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-bufref.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-bufref.Tpo -c -o libcurlu_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-bufref.Tpo $(DEPDIR)/libcurlu_la-bufref.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bufref.c' object='libcurlu_la-bufref.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c + +libcurlu_la-c-hyper.lo: c-hyper.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-c-hyper.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-c-hyper.Tpo -c -o libcurlu_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-c-hyper.Tpo $(DEPDIR)/libcurlu_la-c-hyper.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='c-hyper.c' object='libcurlu_la-c-hyper.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c + +libcurlu_la-cf-h1-proxy.lo: cf-h1-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cf-h1-proxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cf-h1-proxy.Tpo -c -o libcurlu_la-cf-h1-proxy.lo `test -f 'cf-h1-proxy.c' || echo '$(srcdir)/'`cf-h1-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cf-h1-proxy.Tpo $(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-h1-proxy.c' object='libcurlu_la-cf-h1-proxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cf-h1-proxy.lo `test -f 'cf-h1-proxy.c' || echo '$(srcdir)/'`cf-h1-proxy.c + +libcurlu_la-cf-h2-proxy.lo: cf-h2-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cf-h2-proxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cf-h2-proxy.Tpo -c -o libcurlu_la-cf-h2-proxy.lo `test -f 'cf-h2-proxy.c' || echo '$(srcdir)/'`cf-h2-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cf-h2-proxy.Tpo $(DEPDIR)/libcurlu_la-cf-h2-proxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-h2-proxy.c' object='libcurlu_la-cf-h2-proxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cf-h2-proxy.lo `test -f 'cf-h2-proxy.c' || echo '$(srcdir)/'`cf-h2-proxy.c + +libcurlu_la-cf-haproxy.lo: cf-haproxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cf-haproxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cf-haproxy.Tpo -c -o libcurlu_la-cf-haproxy.lo `test -f 'cf-haproxy.c' || echo '$(srcdir)/'`cf-haproxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cf-haproxy.Tpo $(DEPDIR)/libcurlu_la-cf-haproxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-haproxy.c' object='libcurlu_la-cf-haproxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cf-haproxy.lo `test -f 'cf-haproxy.c' || echo '$(srcdir)/'`cf-haproxy.c + +libcurlu_la-cf-https-connect.lo: cf-https-connect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cf-https-connect.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cf-https-connect.Tpo -c -o libcurlu_la-cf-https-connect.lo `test -f 'cf-https-connect.c' || echo '$(srcdir)/'`cf-https-connect.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cf-https-connect.Tpo $(DEPDIR)/libcurlu_la-cf-https-connect.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-https-connect.c' object='libcurlu_la-cf-https-connect.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cf-https-connect.lo `test -f 'cf-https-connect.c' || echo '$(srcdir)/'`cf-https-connect.c + +libcurlu_la-cf-socket.lo: cf-socket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cf-socket.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cf-socket.Tpo -c -o libcurlu_la-cf-socket.lo `test -f 'cf-socket.c' || echo '$(srcdir)/'`cf-socket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cf-socket.Tpo $(DEPDIR)/libcurlu_la-cf-socket.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-socket.c' object='libcurlu_la-cf-socket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cf-socket.lo `test -f 'cf-socket.c' || echo '$(srcdir)/'`cf-socket.c + +libcurlu_la-cfilters.lo: cfilters.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cfilters.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cfilters.Tpo -c -o libcurlu_la-cfilters.lo `test -f 'cfilters.c' || echo '$(srcdir)/'`cfilters.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cfilters.Tpo $(DEPDIR)/libcurlu_la-cfilters.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cfilters.c' object='libcurlu_la-cfilters.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cfilters.lo `test -f 'cfilters.c' || echo '$(srcdir)/'`cfilters.c + +libcurlu_la-conncache.lo: conncache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-conncache.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-conncache.Tpo -c -o libcurlu_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-conncache.Tpo $(DEPDIR)/libcurlu_la-conncache.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conncache.c' object='libcurlu_la-conncache.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c + +libcurlu_la-connect.lo: connect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-connect.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-connect.Tpo -c -o libcurlu_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-connect.Tpo $(DEPDIR)/libcurlu_la-connect.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connect.c' object='libcurlu_la-connect.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c + +libcurlu_la-content_encoding.lo: content_encoding.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-content_encoding.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-content_encoding.Tpo -c -o libcurlu_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-content_encoding.Tpo $(DEPDIR)/libcurlu_la-content_encoding.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='content_encoding.c' object='libcurlu_la-content_encoding.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c + +libcurlu_la-cookie.lo: cookie.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cookie.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cookie.Tpo -c -o libcurlu_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cookie.Tpo $(DEPDIR)/libcurlu_la-cookie.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cookie.c' object='libcurlu_la-cookie.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c + +libcurlu_la-curl_addrinfo.lo: curl_addrinfo.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_addrinfo.Tpo -c -o libcurlu_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_addrinfo.Tpo $(DEPDIR)/libcurlu_la-curl_addrinfo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_addrinfo.c' object='libcurlu_la-curl_addrinfo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c + +libcurlu_la-curl_des.lo: curl_des.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_des.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_des.Tpo -c -o libcurlu_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_des.Tpo $(DEPDIR)/libcurlu_la-curl_des.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_des.c' object='libcurlu_la-curl_des.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c + +libcurlu_la-curl_endian.lo: curl_endian.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_endian.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_endian.Tpo -c -o libcurlu_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_endian.Tpo $(DEPDIR)/libcurlu_la-curl_endian.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_endian.c' object='libcurlu_la-curl_endian.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c + +libcurlu_la-curl_fnmatch.lo: curl_fnmatch.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_fnmatch.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_fnmatch.Tpo -c -o libcurlu_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_fnmatch.Tpo $(DEPDIR)/libcurlu_la-curl_fnmatch.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_fnmatch.c' object='libcurlu_la-curl_fnmatch.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c + +libcurlu_la-curl_get_line.lo: curl_get_line.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_get_line.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_get_line.Tpo -c -o libcurlu_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_get_line.Tpo $(DEPDIR)/libcurlu_la-curl_get_line.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_get_line.c' object='libcurlu_la-curl_get_line.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c + +libcurlu_la-curl_gethostname.lo: curl_gethostname.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_gethostname.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_gethostname.Tpo -c -o libcurlu_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_gethostname.Tpo $(DEPDIR)/libcurlu_la-curl_gethostname.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gethostname.c' object='libcurlu_la-curl_gethostname.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c + +libcurlu_la-curl_gssapi.lo: curl_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_gssapi.Tpo -c -o libcurlu_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_gssapi.Tpo $(DEPDIR)/libcurlu_la-curl_gssapi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gssapi.c' object='libcurlu_la-curl_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c + +libcurlu_la-curl_memrchr.lo: curl_memrchr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_memrchr.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_memrchr.Tpo -c -o libcurlu_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_memrchr.Tpo $(DEPDIR)/libcurlu_la-curl_memrchr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_memrchr.c' object='libcurlu_la-curl_memrchr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c + +libcurlu_la-curl_multibyte.lo: curl_multibyte.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_multibyte.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_multibyte.Tpo -c -o libcurlu_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_multibyte.Tpo $(DEPDIR)/libcurlu_la-curl_multibyte.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_multibyte.c' object='libcurlu_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c + +libcurlu_la-curl_ntlm_core.lo: curl_ntlm_core.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_ntlm_core.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_ntlm_core.Tpo -c -o libcurlu_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_ntlm_core.Tpo $(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_ntlm_core.c' object='libcurlu_la-curl_ntlm_core.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c + +libcurlu_la-curl_path.lo: curl_path.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_path.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_path.Tpo -c -o libcurlu_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_path.Tpo $(DEPDIR)/libcurlu_la-curl_path.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_path.c' object='libcurlu_la-curl_path.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c + +libcurlu_la-curl_range.lo: curl_range.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_range.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_range.Tpo -c -o libcurlu_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_range.Tpo $(DEPDIR)/libcurlu_la-curl_range.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_range.c' object='libcurlu_la-curl_range.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c + +libcurlu_la-curl_rtmp.lo: curl_rtmp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_rtmp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_rtmp.Tpo -c -o libcurlu_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_rtmp.Tpo $(DEPDIR)/libcurlu_la-curl_rtmp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_rtmp.c' object='libcurlu_la-curl_rtmp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c + +libcurlu_la-curl_sasl.lo: curl_sasl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_sasl.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_sasl.Tpo -c -o libcurlu_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_sasl.Tpo $(DEPDIR)/libcurlu_la-curl_sasl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sasl.c' object='libcurlu_la-curl_sasl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c + +libcurlu_la-curl_sha512_256.lo: curl_sha512_256.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_sha512_256.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_sha512_256.Tpo -c -o libcurlu_la-curl_sha512_256.lo `test -f 'curl_sha512_256.c' || echo '$(srcdir)/'`curl_sha512_256.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_sha512_256.Tpo $(DEPDIR)/libcurlu_la-curl_sha512_256.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sha512_256.c' object='libcurlu_la-curl_sha512_256.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_sha512_256.lo `test -f 'curl_sha512_256.c' || echo '$(srcdir)/'`curl_sha512_256.c + +libcurlu_la-curl_sspi.lo: curl_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_sspi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_sspi.Tpo -c -o libcurlu_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_sspi.Tpo $(DEPDIR)/libcurlu_la-curl_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sspi.c' object='libcurlu_la-curl_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c + +libcurlu_la-curl_threads.lo: curl_threads.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_threads.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_threads.Tpo -c -o libcurlu_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_threads.Tpo $(DEPDIR)/libcurlu_la-curl_threads.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_threads.c' object='libcurlu_la-curl_threads.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c + +libcurlu_la-curl_trc.lo: curl_trc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_trc.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_trc.Tpo -c -o libcurlu_la-curl_trc.lo `test -f 'curl_trc.c' || echo '$(srcdir)/'`curl_trc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_trc.Tpo $(DEPDIR)/libcurlu_la-curl_trc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_trc.c' object='libcurlu_la-curl_trc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_trc.lo `test -f 'curl_trc.c' || echo '$(srcdir)/'`curl_trc.c + +libcurlu_la-cw-out.lo: cw-out.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cw-out.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cw-out.Tpo -c -o libcurlu_la-cw-out.lo `test -f 'cw-out.c' || echo '$(srcdir)/'`cw-out.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cw-out.Tpo $(DEPDIR)/libcurlu_la-cw-out.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cw-out.c' object='libcurlu_la-cw-out.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cw-out.lo `test -f 'cw-out.c' || echo '$(srcdir)/'`cw-out.c + +libcurlu_la-dict.lo: dict.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dict.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dict.Tpo -c -o libcurlu_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dict.Tpo $(DEPDIR)/libcurlu_la-dict.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dict.c' object='libcurlu_la-dict.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c + +libcurlu_la-dllmain.lo: dllmain.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dllmain.Tpo -c -o libcurlu_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dllmain.Tpo $(DEPDIR)/libcurlu_la-dllmain.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dllmain.c' object='libcurlu_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c + +libcurlu_la-doh.lo: doh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-doh.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-doh.Tpo -c -o libcurlu_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-doh.Tpo $(DEPDIR)/libcurlu_la-doh.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='doh.c' object='libcurlu_la-doh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c + +libcurlu_la-dynbuf.lo: dynbuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dynbuf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dynbuf.Tpo -c -o libcurlu_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dynbuf.Tpo $(DEPDIR)/libcurlu_la-dynbuf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dynbuf.c' object='libcurlu_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c + +libcurlu_la-dynhds.lo: dynhds.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dynhds.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dynhds.Tpo -c -o libcurlu_la-dynhds.lo `test -f 'dynhds.c' || echo '$(srcdir)/'`dynhds.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dynhds.Tpo $(DEPDIR)/libcurlu_la-dynhds.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dynhds.c' object='libcurlu_la-dynhds.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dynhds.lo `test -f 'dynhds.c' || echo '$(srcdir)/'`dynhds.c + +libcurlu_la-easy.lo: easy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-easy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-easy.Tpo -c -o libcurlu_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-easy.Tpo $(DEPDIR)/libcurlu_la-easy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easy.c' object='libcurlu_la-easy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c + +libcurlu_la-easygetopt.lo: easygetopt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-easygetopt.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-easygetopt.Tpo -c -o libcurlu_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-easygetopt.Tpo $(DEPDIR)/libcurlu_la-easygetopt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easygetopt.c' object='libcurlu_la-easygetopt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c + +libcurlu_la-easyoptions.lo: easyoptions.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-easyoptions.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-easyoptions.Tpo -c -o libcurlu_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-easyoptions.Tpo $(DEPDIR)/libcurlu_la-easyoptions.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easyoptions.c' object='libcurlu_la-easyoptions.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c + +libcurlu_la-escape.lo: escape.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-escape.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-escape.Tpo -c -o libcurlu_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-escape.Tpo $(DEPDIR)/libcurlu_la-escape.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='escape.c' object='libcurlu_la-escape.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c + +libcurlu_la-file.lo: file.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-file.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-file.Tpo -c -o libcurlu_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-file.Tpo $(DEPDIR)/libcurlu_la-file.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file.c' object='libcurlu_la-file.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c + +libcurlu_la-fileinfo.lo: fileinfo.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-fileinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-fileinfo.Tpo -c -o libcurlu_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-fileinfo.Tpo $(DEPDIR)/libcurlu_la-fileinfo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fileinfo.c' object='libcurlu_la-fileinfo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c + +libcurlu_la-fopen.lo: fopen.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-fopen.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-fopen.Tpo -c -o libcurlu_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-fopen.Tpo $(DEPDIR)/libcurlu_la-fopen.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fopen.c' object='libcurlu_la-fopen.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c + +libcurlu_la-formdata.lo: formdata.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-formdata.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-formdata.Tpo -c -o libcurlu_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-formdata.Tpo $(DEPDIR)/libcurlu_la-formdata.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='formdata.c' object='libcurlu_la-formdata.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c + +libcurlu_la-ftp.lo: ftp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ftp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ftp.Tpo -c -o libcurlu_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ftp.Tpo $(DEPDIR)/libcurlu_la-ftp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftp.c' object='libcurlu_la-ftp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c + +libcurlu_la-ftplistparser.lo: ftplistparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ftplistparser.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ftplistparser.Tpo -c -o libcurlu_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ftplistparser.Tpo $(DEPDIR)/libcurlu_la-ftplistparser.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftplistparser.c' object='libcurlu_la-ftplistparser.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c + +libcurlu_la-getenv.lo: getenv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-getenv.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-getenv.Tpo -c -o libcurlu_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-getenv.Tpo $(DEPDIR)/libcurlu_la-getenv.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getenv.c' object='libcurlu_la-getenv.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c + +libcurlu_la-getinfo.lo: getinfo.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-getinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-getinfo.Tpo -c -o libcurlu_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-getinfo.Tpo $(DEPDIR)/libcurlu_la-getinfo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getinfo.c' object='libcurlu_la-getinfo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c + +libcurlu_la-gopher.lo: gopher.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-gopher.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-gopher.Tpo -c -o libcurlu_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-gopher.Tpo $(DEPDIR)/libcurlu_la-gopher.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gopher.c' object='libcurlu_la-gopher.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c + +libcurlu_la-hash.lo: hash.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hash.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hash.Tpo -c -o libcurlu_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hash.Tpo $(DEPDIR)/libcurlu_la-hash.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hash.c' object='libcurlu_la-hash.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c + +libcurlu_la-headers.lo: headers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-headers.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-headers.Tpo -c -o libcurlu_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-headers.Tpo $(DEPDIR)/libcurlu_la-headers.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='headers.c' object='libcurlu_la-headers.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c + +libcurlu_la-hmac.lo: hmac.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hmac.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hmac.Tpo -c -o libcurlu_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hmac.Tpo $(DEPDIR)/libcurlu_la-hmac.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hmac.c' object='libcurlu_la-hmac.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c + +libcurlu_la-hostasyn.lo: hostasyn.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostasyn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostasyn.Tpo -c -o libcurlu_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostasyn.Tpo $(DEPDIR)/libcurlu_la-hostasyn.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostasyn.c' object='libcurlu_la-hostasyn.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c + +libcurlu_la-hostip.lo: hostip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip.Tpo -c -o libcurlu_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip.Tpo $(DEPDIR)/libcurlu_la-hostip.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip.c' object='libcurlu_la-hostip.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c + +libcurlu_la-hostip4.lo: hostip4.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip4.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip4.Tpo -c -o libcurlu_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip4.Tpo $(DEPDIR)/libcurlu_la-hostip4.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip4.c' object='libcurlu_la-hostip4.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c + +libcurlu_la-hostip6.lo: hostip6.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip6.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip6.Tpo -c -o libcurlu_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip6.Tpo $(DEPDIR)/libcurlu_la-hostip6.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip6.c' object='libcurlu_la-hostip6.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c + +libcurlu_la-hostsyn.lo: hostsyn.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostsyn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostsyn.Tpo -c -o libcurlu_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostsyn.Tpo $(DEPDIR)/libcurlu_la-hostsyn.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostsyn.c' object='libcurlu_la-hostsyn.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c + +libcurlu_la-hsts.lo: hsts.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hsts.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hsts.Tpo -c -o libcurlu_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hsts.Tpo $(DEPDIR)/libcurlu_la-hsts.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hsts.c' object='libcurlu_la-hsts.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c + +libcurlu_la-http.lo: http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http.Tpo -c -o libcurlu_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http.Tpo $(DEPDIR)/libcurlu_la-http.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='libcurlu_la-http.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c + +libcurlu_la-http1.lo: http1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http1.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http1.Tpo -c -o libcurlu_la-http1.lo `test -f 'http1.c' || echo '$(srcdir)/'`http1.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http1.Tpo $(DEPDIR)/libcurlu_la-http1.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http1.c' object='libcurlu_la-http1.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http1.lo `test -f 'http1.c' || echo '$(srcdir)/'`http1.c + +libcurlu_la-http2.lo: http2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http2.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http2.Tpo -c -o libcurlu_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http2.Tpo $(DEPDIR)/libcurlu_la-http2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http2.c' object='libcurlu_la-http2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c + +libcurlu_la-http_aws_sigv4.lo: http_aws_sigv4.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_aws_sigv4.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_aws_sigv4.Tpo -c -o libcurlu_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_aws_sigv4.Tpo $(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_aws_sigv4.c' object='libcurlu_la-http_aws_sigv4.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c + +libcurlu_la-http_chunks.lo: http_chunks.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_chunks.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_chunks.Tpo -c -o libcurlu_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_chunks.Tpo $(DEPDIR)/libcurlu_la-http_chunks.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_chunks.c' object='libcurlu_la-http_chunks.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c + +libcurlu_la-http_digest.lo: http_digest.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_digest.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_digest.Tpo -c -o libcurlu_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_digest.Tpo $(DEPDIR)/libcurlu_la-http_digest.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_digest.c' object='libcurlu_la-http_digest.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c + +libcurlu_la-http_negotiate.lo: http_negotiate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_negotiate.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_negotiate.Tpo -c -o libcurlu_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_negotiate.Tpo $(DEPDIR)/libcurlu_la-http_negotiate.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_negotiate.c' object='libcurlu_la-http_negotiate.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c + +libcurlu_la-http_ntlm.lo: http_ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_ntlm.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_ntlm.Tpo -c -o libcurlu_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_ntlm.Tpo $(DEPDIR)/libcurlu_la-http_ntlm.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_ntlm.c' object='libcurlu_la-http_ntlm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c + +libcurlu_la-http_proxy.lo: http_proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_proxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_proxy.Tpo -c -o libcurlu_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_proxy.Tpo $(DEPDIR)/libcurlu_la-http_proxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_proxy.c' object='libcurlu_la-http_proxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c + +libcurlu_la-idn.lo: idn.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-idn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-idn.Tpo -c -o libcurlu_la-idn.lo `test -f 'idn.c' || echo '$(srcdir)/'`idn.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-idn.Tpo $(DEPDIR)/libcurlu_la-idn.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='idn.c' object='libcurlu_la-idn.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-idn.lo `test -f 'idn.c' || echo '$(srcdir)/'`idn.c + +libcurlu_la-if2ip.lo: if2ip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-if2ip.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-if2ip.Tpo -c -o libcurlu_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-if2ip.Tpo $(DEPDIR)/libcurlu_la-if2ip.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='if2ip.c' object='libcurlu_la-if2ip.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c + +libcurlu_la-imap.lo: imap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-imap.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-imap.Tpo -c -o libcurlu_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-imap.Tpo $(DEPDIR)/libcurlu_la-imap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap.c' object='libcurlu_la-imap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c + +libcurlu_la-inet_ntop.lo: inet_ntop.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-inet_ntop.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-inet_ntop.Tpo -c -o libcurlu_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-inet_ntop.Tpo $(DEPDIR)/libcurlu_la-inet_ntop.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_ntop.c' object='libcurlu_la-inet_ntop.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c + +libcurlu_la-inet_pton.lo: inet_pton.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-inet_pton.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-inet_pton.Tpo -c -o libcurlu_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-inet_pton.Tpo $(DEPDIR)/libcurlu_la-inet_pton.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_pton.c' object='libcurlu_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c + +libcurlu_la-krb5.lo: krb5.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-krb5.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-krb5.Tpo -c -o libcurlu_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-krb5.Tpo $(DEPDIR)/libcurlu_la-krb5.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5.c' object='libcurlu_la-krb5.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c + +libcurlu_la-ldap.lo: ldap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ldap.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ldap.Tpo -c -o libcurlu_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ldap.Tpo $(DEPDIR)/libcurlu_la-ldap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap.c' object='libcurlu_la-ldap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c + +libcurlu_la-llist.lo: llist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-llist.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-llist.Tpo -c -o libcurlu_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-llist.Tpo $(DEPDIR)/libcurlu_la-llist.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='llist.c' object='libcurlu_la-llist.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c + +libcurlu_la-macos.lo: macos.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-macos.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-macos.Tpo -c -o libcurlu_la-macos.lo `test -f 'macos.c' || echo '$(srcdir)/'`macos.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-macos.Tpo $(DEPDIR)/libcurlu_la-macos.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='macos.c' object='libcurlu_la-macos.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-macos.lo `test -f 'macos.c' || echo '$(srcdir)/'`macos.c + +libcurlu_la-md4.lo: md4.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-md4.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-md4.Tpo -c -o libcurlu_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-md4.Tpo $(DEPDIR)/libcurlu_la-md4.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md4.c' object='libcurlu_la-md4.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c + +libcurlu_la-md5.lo: md5.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-md5.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-md5.Tpo -c -o libcurlu_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-md5.Tpo $(DEPDIR)/libcurlu_la-md5.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='libcurlu_la-md5.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c + +libcurlu_la-memdebug.lo: memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-memdebug.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-memdebug.Tpo -c -o libcurlu_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-memdebug.Tpo $(DEPDIR)/libcurlu_la-memdebug.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='memdebug.c' object='libcurlu_la-memdebug.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c + +libcurlu_la-mime.lo: mime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-mime.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-mime.Tpo -c -o libcurlu_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-mime.Tpo $(DEPDIR)/libcurlu_la-mime.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mime.c' object='libcurlu_la-mime.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c + +libcurlu_la-mprintf.lo: mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-mprintf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-mprintf.Tpo -c -o libcurlu_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-mprintf.Tpo $(DEPDIR)/libcurlu_la-mprintf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mprintf.c' object='libcurlu_la-mprintf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c + +libcurlu_la-mqtt.lo: mqtt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-mqtt.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-mqtt.Tpo -c -o libcurlu_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-mqtt.Tpo $(DEPDIR)/libcurlu_la-mqtt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mqtt.c' object='libcurlu_la-mqtt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c + +libcurlu_la-multi.lo: multi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-multi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-multi.Tpo -c -o libcurlu_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-multi.Tpo $(DEPDIR)/libcurlu_la-multi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='multi.c' object='libcurlu_la-multi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c + +libcurlu_la-netrc.lo: netrc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-netrc.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-netrc.Tpo -c -o libcurlu_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-netrc.Tpo $(DEPDIR)/libcurlu_la-netrc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netrc.c' object='libcurlu_la-netrc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c + +libcurlu_la-nonblock.lo: nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-nonblock.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-nonblock.Tpo -c -o libcurlu_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-nonblock.Tpo $(DEPDIR)/libcurlu_la-nonblock.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nonblock.c' object='libcurlu_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c + +libcurlu_la-noproxy.lo: noproxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-noproxy.Tpo -c -o libcurlu_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-noproxy.Tpo $(DEPDIR)/libcurlu_la-noproxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='noproxy.c' object='libcurlu_la-noproxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c + +libcurlu_la-openldap.lo: openldap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-openldap.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-openldap.Tpo -c -o libcurlu_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-openldap.Tpo $(DEPDIR)/libcurlu_la-openldap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openldap.c' object='libcurlu_la-openldap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c + +libcurlu_la-parsedate.lo: parsedate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-parsedate.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-parsedate.Tpo -c -o libcurlu_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-parsedate.Tpo $(DEPDIR)/libcurlu_la-parsedate.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parsedate.c' object='libcurlu_la-parsedate.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c + +libcurlu_la-pingpong.lo: pingpong.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-pingpong.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-pingpong.Tpo -c -o libcurlu_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-pingpong.Tpo $(DEPDIR)/libcurlu_la-pingpong.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pingpong.c' object='libcurlu_la-pingpong.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c + +libcurlu_la-pop3.lo: pop3.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-pop3.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-pop3.Tpo -c -o libcurlu_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-pop3.Tpo $(DEPDIR)/libcurlu_la-pop3.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pop3.c' object='libcurlu_la-pop3.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c + +libcurlu_la-progress.lo: progress.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-progress.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-progress.Tpo -c -o libcurlu_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-progress.Tpo $(DEPDIR)/libcurlu_la-progress.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='progress.c' object='libcurlu_la-progress.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c + +libcurlu_la-psl.lo: psl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-psl.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-psl.Tpo -c -o libcurlu_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-psl.Tpo $(DEPDIR)/libcurlu_la-psl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='psl.c' object='libcurlu_la-psl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c + +libcurlu_la-rand.lo: rand.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-rand.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-rand.Tpo -c -o libcurlu_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-rand.Tpo $(DEPDIR)/libcurlu_la-rand.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='libcurlu_la-rand.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c + +libcurlu_la-rename.lo: rename.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-rename.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-rename.Tpo -c -o libcurlu_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-rename.Tpo $(DEPDIR)/libcurlu_la-rename.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rename.c' object='libcurlu_la-rename.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c + +libcurlu_la-request.lo: request.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-request.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-request.Tpo -c -o libcurlu_la-request.lo `test -f 'request.c' || echo '$(srcdir)/'`request.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-request.Tpo $(DEPDIR)/libcurlu_la-request.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='request.c' object='libcurlu_la-request.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-request.lo `test -f 'request.c' || echo '$(srcdir)/'`request.c + +libcurlu_la-rtsp.lo: rtsp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-rtsp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-rtsp.Tpo -c -o libcurlu_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-rtsp.Tpo $(DEPDIR)/libcurlu_la-rtsp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtsp.c' object='libcurlu_la-rtsp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c + +libcurlu_la-select.lo: select.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-select.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-select.Tpo -c -o libcurlu_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-select.Tpo $(DEPDIR)/libcurlu_la-select.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='select.c' object='libcurlu_la-select.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c + +libcurlu_la-sendf.lo: sendf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-sendf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-sendf.Tpo -c -o libcurlu_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-sendf.Tpo $(DEPDIR)/libcurlu_la-sendf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sendf.c' object='libcurlu_la-sendf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c + +libcurlu_la-setopt.lo: setopt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-setopt.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-setopt.Tpo -c -o libcurlu_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-setopt.Tpo $(DEPDIR)/libcurlu_la-setopt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='setopt.c' object='libcurlu_la-setopt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c + +libcurlu_la-sha256.lo: sha256.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-sha256.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-sha256.Tpo -c -o libcurlu_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-sha256.Tpo $(DEPDIR)/libcurlu_la-sha256.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sha256.c' object='libcurlu_la-sha256.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c + +libcurlu_la-share.lo: share.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-share.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-share.Tpo -c -o libcurlu_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-share.Tpo $(DEPDIR)/libcurlu_la-share.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='share.c' object='libcurlu_la-share.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c + +libcurlu_la-slist.lo: slist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-slist.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-slist.Tpo -c -o libcurlu_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-slist.Tpo $(DEPDIR)/libcurlu_la-slist.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='slist.c' object='libcurlu_la-slist.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c + +libcurlu_la-smb.lo: smb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-smb.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-smb.Tpo -c -o libcurlu_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-smb.Tpo $(DEPDIR)/libcurlu_la-smb.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smb.c' object='libcurlu_la-smb.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c + +libcurlu_la-smtp.lo: smtp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-smtp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-smtp.Tpo -c -o libcurlu_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-smtp.Tpo $(DEPDIR)/libcurlu_la-smtp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smtp.c' object='libcurlu_la-smtp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c + +libcurlu_la-socketpair.lo: socketpair.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socketpair.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socketpair.Tpo -c -o libcurlu_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socketpair.Tpo $(DEPDIR)/libcurlu_la-socketpair.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socketpair.c' object='libcurlu_la-socketpair.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c + +libcurlu_la-socks.lo: socks.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socks.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socks.Tpo -c -o libcurlu_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socks.Tpo $(DEPDIR)/libcurlu_la-socks.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks.c' object='libcurlu_la-socks.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c + +libcurlu_la-socks_gssapi.lo: socks_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socks_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socks_gssapi.Tpo -c -o libcurlu_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socks_gssapi.Tpo $(DEPDIR)/libcurlu_la-socks_gssapi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_gssapi.c' object='libcurlu_la-socks_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c + +libcurlu_la-socks_sspi.lo: socks_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socks_sspi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socks_sspi.Tpo -c -o libcurlu_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socks_sspi.Tpo $(DEPDIR)/libcurlu_la-socks_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_sspi.c' object='libcurlu_la-socks_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c + +libcurlu_la-speedcheck.lo: speedcheck.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-speedcheck.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-speedcheck.Tpo -c -o libcurlu_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-speedcheck.Tpo $(DEPDIR)/libcurlu_la-speedcheck.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='speedcheck.c' object='libcurlu_la-speedcheck.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c + +libcurlu_la-splay.lo: splay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-splay.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-splay.Tpo -c -o libcurlu_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-splay.Tpo $(DEPDIR)/libcurlu_la-splay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='splay.c' object='libcurlu_la-splay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c + +libcurlu_la-strcase.lo: strcase.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strcase.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strcase.Tpo -c -o libcurlu_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strcase.Tpo $(DEPDIR)/libcurlu_la-strcase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strcase.c' object='libcurlu_la-strcase.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c + +libcurlu_la-strdup.lo: strdup.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strdup.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strdup.Tpo -c -o libcurlu_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strdup.Tpo $(DEPDIR)/libcurlu_la-strdup.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strdup.c' object='libcurlu_la-strdup.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c + +libcurlu_la-strerror.lo: strerror.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strerror.Tpo -c -o libcurlu_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strerror.Tpo $(DEPDIR)/libcurlu_la-strerror.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strerror.c' object='libcurlu_la-strerror.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c + +libcurlu_la-strtok.lo: strtok.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strtok.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strtok.Tpo -c -o libcurlu_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strtok.Tpo $(DEPDIR)/libcurlu_la-strtok.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtok.c' object='libcurlu_la-strtok.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c + +libcurlu_la-strtoofft.lo: strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strtoofft.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strtoofft.Tpo -c -o libcurlu_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strtoofft.Tpo $(DEPDIR)/libcurlu_la-strtoofft.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtoofft.c' object='libcurlu_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c + +libcurlu_la-system_win32.lo: system_win32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-system_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-system_win32.Tpo -c -o libcurlu_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-system_win32.Tpo $(DEPDIR)/libcurlu_la-system_win32.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='system_win32.c' object='libcurlu_la-system_win32.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c + +libcurlu_la-telnet.lo: telnet.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-telnet.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-telnet.Tpo -c -o libcurlu_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-telnet.Tpo $(DEPDIR)/libcurlu_la-telnet.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='telnet.c' object='libcurlu_la-telnet.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c + +libcurlu_la-tftp.lo: tftp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-tftp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-tftp.Tpo -c -o libcurlu_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-tftp.Tpo $(DEPDIR)/libcurlu_la-tftp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tftp.c' object='libcurlu_la-tftp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c + +libcurlu_la-timediff.lo: timediff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-timediff.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-timediff.Tpo -c -o libcurlu_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-timediff.Tpo $(DEPDIR)/libcurlu_la-timediff.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timediff.c' object='libcurlu_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c + +libcurlu_la-timeval.lo: timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-timeval.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-timeval.Tpo -c -o libcurlu_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-timeval.Tpo $(DEPDIR)/libcurlu_la-timeval.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timeval.c' object='libcurlu_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c + +libcurlu_la-transfer.lo: transfer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-transfer.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-transfer.Tpo -c -o libcurlu_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-transfer.Tpo $(DEPDIR)/libcurlu_la-transfer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='transfer.c' object='libcurlu_la-transfer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c + +libcurlu_la-url.lo: url.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-url.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-url.Tpo -c -o libcurlu_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-url.Tpo $(DEPDIR)/libcurlu_la-url.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='url.c' object='libcurlu_la-url.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c + +libcurlu_la-urlapi.lo: urlapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-urlapi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-urlapi.Tpo -c -o libcurlu_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-urlapi.Tpo $(DEPDIR)/libcurlu_la-urlapi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='urlapi.c' object='libcurlu_la-urlapi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c + +libcurlu_la-version.lo: version.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-version.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-version.Tpo -c -o libcurlu_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-version.Tpo $(DEPDIR)/libcurlu_la-version.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version.c' object='libcurlu_la-version.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c + +libcurlu_la-version_win32.lo: version_win32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-version_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-version_win32.Tpo -c -o libcurlu_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-version_win32.Tpo $(DEPDIR)/libcurlu_la-version_win32.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version_win32.c' object='libcurlu_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c + +libcurlu_la-warnless.lo: warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-warnless.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-warnless.Tpo -c -o libcurlu_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-warnless.Tpo $(DEPDIR)/libcurlu_la-warnless.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warnless.c' object='libcurlu_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c + +libcurlu_la-ws.lo: ws.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ws.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ws.Tpo -c -o libcurlu_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ws.Tpo $(DEPDIR)/libcurlu_la-ws.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ws.c' object='libcurlu_la-ws.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c + +vauth/libcurlu_la-cleartext.lo: vauth/cleartext.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-cleartext.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-cleartext.Tpo -c -o vauth/libcurlu_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-cleartext.Tpo vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cleartext.c' object='vauth/libcurlu_la-cleartext.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c + +vauth/libcurlu_la-cram.lo: vauth/cram.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-cram.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-cram.Tpo -c -o vauth/libcurlu_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-cram.Tpo vauth/$(DEPDIR)/libcurlu_la-cram.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cram.c' object='vauth/libcurlu_la-cram.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c + +vauth/libcurlu_la-digest.lo: vauth/digest.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-digest.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-digest.Tpo -c -o vauth/libcurlu_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-digest.Tpo vauth/$(DEPDIR)/libcurlu_la-digest.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest.c' object='vauth/libcurlu_la-digest.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c + +vauth/libcurlu_la-digest_sspi.lo: vauth/digest_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-digest_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Tpo -c -o vauth/libcurlu_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest_sspi.c' object='vauth/libcurlu_la-digest_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c + +vauth/libcurlu_la-gsasl.lo: vauth/gsasl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-gsasl.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-gsasl.Tpo -c -o vauth/libcurlu_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-gsasl.Tpo vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/gsasl.c' object='vauth/libcurlu_la-gsasl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c + +vauth/libcurlu_la-krb5_gssapi.lo: vauth/krb5_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-krb5_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Tpo -c -o vauth/libcurlu_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Tpo vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_gssapi.c' object='vauth/libcurlu_la-krb5_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c + +vauth/libcurlu_la-krb5_sspi.lo: vauth/krb5_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-krb5_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Tpo -c -o vauth/libcurlu_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_sspi.c' object='vauth/libcurlu_la-krb5_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c + +vauth/libcurlu_la-ntlm.lo: vauth/ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-ntlm.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-ntlm.Tpo -c -o vauth/libcurlu_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-ntlm.Tpo vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm.c' object='vauth/libcurlu_la-ntlm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c + +vauth/libcurlu_la-ntlm_sspi.lo: vauth/ntlm_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-ntlm_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Tpo -c -o vauth/libcurlu_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm_sspi.c' object='vauth/libcurlu_la-ntlm_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c + +vauth/libcurlu_la-oauth2.lo: vauth/oauth2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-oauth2.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-oauth2.Tpo -c -o vauth/libcurlu_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-oauth2.Tpo vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/oauth2.c' object='vauth/libcurlu_la-oauth2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c + +vauth/libcurlu_la-spnego_gssapi.lo: vauth/spnego_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-spnego_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Tpo -c -o vauth/libcurlu_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Tpo vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_gssapi.c' object='vauth/libcurlu_la-spnego_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c + +vauth/libcurlu_la-spnego_sspi.lo: vauth/spnego_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-spnego_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Tpo -c -o vauth/libcurlu_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_sspi.c' object='vauth/libcurlu_la-spnego_sspi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c + +vauth/libcurlu_la-vauth.lo: vauth/vauth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-vauth.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-vauth.Tpo -c -o vauth/libcurlu_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-vauth.Tpo vauth/$(DEPDIR)/libcurlu_la-vauth.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/vauth.c' object='vauth/libcurlu_la-vauth.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c + +vtls/libcurlu_la-bearssl.lo: vtls/bearssl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-bearssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-bearssl.Tpo -c -o vtls/libcurlu_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-bearssl.Tpo vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/bearssl.c' object='vtls/libcurlu_la-bearssl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c + +vtls/libcurlu_la-cipher_suite.lo: vtls/cipher_suite.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-cipher_suite.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-cipher_suite.Tpo -c -o vtls/libcurlu_la-cipher_suite.lo `test -f 'vtls/cipher_suite.c' || echo '$(srcdir)/'`vtls/cipher_suite.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-cipher_suite.Tpo vtls/$(DEPDIR)/libcurlu_la-cipher_suite.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/cipher_suite.c' object='vtls/libcurlu_la-cipher_suite.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-cipher_suite.lo `test -f 'vtls/cipher_suite.c' || echo '$(srcdir)/'`vtls/cipher_suite.c + +vtls/libcurlu_la-gtls.lo: vtls/gtls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-gtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-gtls.Tpo -c -o vtls/libcurlu_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-gtls.Tpo vtls/$(DEPDIR)/libcurlu_la-gtls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/gtls.c' object='vtls/libcurlu_la-gtls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c + +vtls/libcurlu_la-hostcheck.lo: vtls/hostcheck.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-hostcheck.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-hostcheck.Tpo -c -o vtls/libcurlu_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-hostcheck.Tpo vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/hostcheck.c' object='vtls/libcurlu_la-hostcheck.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c + +vtls/libcurlu_la-keylog.lo: vtls/keylog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-keylog.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-keylog.Tpo -c -o vtls/libcurlu_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-keylog.Tpo vtls/$(DEPDIR)/libcurlu_la-keylog.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/keylog.c' object='vtls/libcurlu_la-keylog.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c + +vtls/libcurlu_la-mbedtls.lo: vtls/mbedtls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-mbedtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-mbedtls.Tpo -c -o vtls/libcurlu_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-mbedtls.Tpo vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls.c' object='vtls/libcurlu_la-mbedtls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c + +vtls/libcurlu_la-mbedtls_threadlock.lo: vtls/mbedtls_threadlock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-mbedtls_threadlock.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Tpo -c -o vtls/libcurlu_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Tpo vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls_threadlock.c' object='vtls/libcurlu_la-mbedtls_threadlock.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c + +vtls/libcurlu_la-openssl.lo: vtls/openssl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-openssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-openssl.Tpo -c -o vtls/libcurlu_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-openssl.Tpo vtls/$(DEPDIR)/libcurlu_la-openssl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/openssl.c' object='vtls/libcurlu_la-openssl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c + +vtls/libcurlu_la-rustls.lo: vtls/rustls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-rustls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-rustls.Tpo -c -o vtls/libcurlu_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-rustls.Tpo vtls/$(DEPDIR)/libcurlu_la-rustls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/rustls.c' object='vtls/libcurlu_la-rustls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c + +vtls/libcurlu_la-schannel.lo: vtls/schannel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-schannel.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-schannel.Tpo -c -o vtls/libcurlu_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-schannel.Tpo vtls/$(DEPDIR)/libcurlu_la-schannel.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel.c' object='vtls/libcurlu_la-schannel.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c + +vtls/libcurlu_la-schannel_verify.lo: vtls/schannel_verify.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-schannel_verify.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Tpo -c -o vtls/libcurlu_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Tpo vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel_verify.c' object='vtls/libcurlu_la-schannel_verify.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c + +vtls/libcurlu_la-sectransp.lo: vtls/sectransp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-sectransp.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-sectransp.Tpo -c -o vtls/libcurlu_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-sectransp.Tpo vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/sectransp.c' object='vtls/libcurlu_la-sectransp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c + +vtls/libcurlu_la-vtls.lo: vtls/vtls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-vtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-vtls.Tpo -c -o vtls/libcurlu_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-vtls.Tpo vtls/$(DEPDIR)/libcurlu_la-vtls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls.c' object='vtls/libcurlu_la-vtls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c + +vtls/libcurlu_la-wolfssl.lo: vtls/wolfssl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-wolfssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-wolfssl.Tpo -c -o vtls/libcurlu_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-wolfssl.Tpo vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/wolfssl.c' object='vtls/libcurlu_la-wolfssl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c + +vtls/libcurlu_la-x509asn1.lo: vtls/x509asn1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-x509asn1.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-x509asn1.Tpo -c -o vtls/libcurlu_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-x509asn1.Tpo vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/x509asn1.c' object='vtls/libcurlu_la-x509asn1.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c + +vquic/libcurlu_la-curl_msh3.lo: vquic/curl_msh3.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-curl_msh3.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Tpo -c -o vquic/libcurlu_la-curl_msh3.lo `test -f 'vquic/curl_msh3.c' || echo '$(srcdir)/'`vquic/curl_msh3.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Tpo vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_msh3.c' object='vquic/libcurlu_la-curl_msh3.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-curl_msh3.lo `test -f 'vquic/curl_msh3.c' || echo '$(srcdir)/'`vquic/curl_msh3.c + +vquic/libcurlu_la-curl_ngtcp2.lo: vquic/curl_ngtcp2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-curl_ngtcp2.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Tpo -c -o vquic/libcurlu_la-curl_ngtcp2.lo `test -f 'vquic/curl_ngtcp2.c' || echo '$(srcdir)/'`vquic/curl_ngtcp2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Tpo vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_ngtcp2.c' object='vquic/libcurlu_la-curl_ngtcp2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-curl_ngtcp2.lo `test -f 'vquic/curl_ngtcp2.c' || echo '$(srcdir)/'`vquic/curl_ngtcp2.c + +vquic/libcurlu_la-curl_osslq.lo: vquic/curl_osslq.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-curl_osslq.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Tpo -c -o vquic/libcurlu_la-curl_osslq.lo `test -f 'vquic/curl_osslq.c' || echo '$(srcdir)/'`vquic/curl_osslq.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Tpo vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_osslq.c' object='vquic/libcurlu_la-curl_osslq.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-curl_osslq.lo `test -f 'vquic/curl_osslq.c' || echo '$(srcdir)/'`vquic/curl_osslq.c + +vquic/libcurlu_la-curl_quiche.lo: vquic/curl_quiche.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-curl_quiche.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Tpo -c -o vquic/libcurlu_la-curl_quiche.lo `test -f 'vquic/curl_quiche.c' || echo '$(srcdir)/'`vquic/curl_quiche.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Tpo vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_quiche.c' object='vquic/libcurlu_la-curl_quiche.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-curl_quiche.lo `test -f 'vquic/curl_quiche.c' || echo '$(srcdir)/'`vquic/curl_quiche.c + +vquic/libcurlu_la-vquic.lo: vquic/vquic.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-vquic.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-vquic.Tpo -c -o vquic/libcurlu_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-vquic.Tpo vquic/$(DEPDIR)/libcurlu_la-vquic.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/vquic.c' object='vquic/libcurlu_la-vquic.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c + +vquic/libcurlu_la-vquic-tls.lo: vquic/vquic-tls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-vquic-tls.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Tpo -c -o vquic/libcurlu_la-vquic-tls.lo `test -f 'vquic/vquic-tls.c' || echo '$(srcdir)/'`vquic/vquic-tls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Tpo vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/vquic-tls.c' object='vquic/libcurlu_la-vquic-tls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-vquic-tls.lo `test -f 'vquic/vquic-tls.c' || echo '$(srcdir)/'`vquic/vquic-tls.c + +vssh/libcurlu_la-libssh.lo: vssh/libssh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vssh/libcurlu_la-libssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurlu_la-libssh.Tpo -c -o vssh/libcurlu_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurlu_la-libssh.Tpo vssh/$(DEPDIR)/libcurlu_la-libssh.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh.c' object='vssh/libcurlu_la-libssh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurlu_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c + +vssh/libcurlu_la-libssh2.lo: vssh/libssh2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vssh/libcurlu_la-libssh2.lo -MD -MP -MF vssh/$(DEPDIR)/libcurlu_la-libssh2.Tpo -c -o vssh/libcurlu_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurlu_la-libssh2.Tpo vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh2.c' object='vssh/libcurlu_la-libssh2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurlu_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c + +vssh/libcurlu_la-wolfssh.lo: vssh/wolfssh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vssh/libcurlu_la-wolfssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurlu_la-wolfssh.Tpo -c -o vssh/libcurlu_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurlu_la-wolfssh.Tpo vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/wolfssh.c' object='vssh/libcurlu_la-wolfssh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurlu_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf vauth/.libs vauth/_libs + -rm -rf vquic/.libs vquic/_libs + -rm -rf vssh/.libs vssh/_libs + -rm -rf vtls/.libs vtls/_libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +@DEBUGBUILD_FALSE@all-local: +all-am: Makefile $(LTLIBRARIES) curl_config.h all-local +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f vauth/$(DEPDIR)/$(am__dirstamp) + -rm -f vauth/$(am__dirstamp) + -rm -f vquic/$(DEPDIR)/$(am__dirstamp) + -rm -f vquic/$(am__dirstamp) + -rm -f vssh/$(DEPDIR)/$(am__dirstamp) + -rm -f vssh/$(am__dirstamp) + -rm -f vtls/$(DEPDIR)/$(am__dirstamp) + -rm -f vtls/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/libcurl_la-altsvc.Plo + -rm -f ./$(DEPDIR)/libcurl_la-amigaos.Plo + -rm -f ./$(DEPDIR)/libcurl_la-asyn-ares.Plo + -rm -f ./$(DEPDIR)/libcurl_la-asyn-thread.Plo + -rm -f ./$(DEPDIR)/libcurl_la-base64.Plo + -rm -f ./$(DEPDIR)/libcurl_la-bufq.Plo + -rm -f ./$(DEPDIR)/libcurl_la-bufref.Plo + -rm -f ./$(DEPDIR)/libcurl_la-c-hyper.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-h2-proxy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-haproxy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-https-connect.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-socket.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cfilters.Plo + -rm -f ./$(DEPDIR)/libcurl_la-conncache.Plo + -rm -f ./$(DEPDIR)/libcurl_la-connect.Plo + -rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_des.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_get_line.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_path.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_range.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_sasl.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_sha512_256.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_sspi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_threads.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_trc.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cw-out.Plo + -rm -f ./$(DEPDIR)/libcurl_la-dict.Plo + -rm -f ./$(DEPDIR)/libcurl_la-dllmain.Plo + -rm -f ./$(DEPDIR)/libcurl_la-doh.Plo + -rm -f ./$(DEPDIR)/libcurl_la-dynbuf.Plo + -rm -f ./$(DEPDIR)/libcurl_la-dynhds.Plo + -rm -f ./$(DEPDIR)/libcurl_la-easy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-easygetopt.Plo + -rm -f ./$(DEPDIR)/libcurl_la-easyoptions.Plo + -rm -f ./$(DEPDIR)/libcurl_la-escape.Plo + -rm -f ./$(DEPDIR)/libcurl_la-file.Plo + -rm -f ./$(DEPDIR)/libcurl_la-fileinfo.Plo + -rm -f ./$(DEPDIR)/libcurl_la-fopen.Plo + -rm -f ./$(DEPDIR)/libcurl_la-formdata.Plo + -rm -f ./$(DEPDIR)/libcurl_la-ftp.Plo + -rm -f ./$(DEPDIR)/libcurl_la-ftplistparser.Plo + -rm -f ./$(DEPDIR)/libcurl_la-getenv.Plo + -rm -f ./$(DEPDIR)/libcurl_la-getinfo.Plo + -rm -f ./$(DEPDIR)/libcurl_la-gopher.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hash.Plo + -rm -f ./$(DEPDIR)/libcurl_la-headers.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hmac.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hostasyn.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hostip.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hostip4.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hostip6.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hostsyn.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hsts.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http1.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http2.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_chunks.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_digest.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_negotiate.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_ntlm.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_proxy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-idn.Plo + -rm -f ./$(DEPDIR)/libcurl_la-if2ip.Plo + -rm -f ./$(DEPDIR)/libcurl_la-imap.Plo + -rm -f ./$(DEPDIR)/libcurl_la-inet_ntop.Plo + -rm -f ./$(DEPDIR)/libcurl_la-inet_pton.Plo + -rm -f ./$(DEPDIR)/libcurl_la-krb5.Plo + -rm -f ./$(DEPDIR)/libcurl_la-ldap.Plo + -rm -f ./$(DEPDIR)/libcurl_la-llist.Plo + -rm -f ./$(DEPDIR)/libcurl_la-macos.Plo + -rm -f ./$(DEPDIR)/libcurl_la-md4.Plo + -rm -f ./$(DEPDIR)/libcurl_la-md5.Plo + -rm -f ./$(DEPDIR)/libcurl_la-memdebug.Plo + -rm -f ./$(DEPDIR)/libcurl_la-mime.Plo + -rm -f ./$(DEPDIR)/libcurl_la-mprintf.Plo + -rm -f ./$(DEPDIR)/libcurl_la-mqtt.Plo + -rm -f ./$(DEPDIR)/libcurl_la-multi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo + -rm -f ./$(DEPDIR)/libcurl_la-nonblock.Plo + -rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo + -rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo + -rm -f ./$(DEPDIR)/libcurl_la-pingpong.Plo + -rm -f ./$(DEPDIR)/libcurl_la-pop3.Plo + -rm -f ./$(DEPDIR)/libcurl_la-progress.Plo + -rm -f ./$(DEPDIR)/libcurl_la-psl.Plo + -rm -f ./$(DEPDIR)/libcurl_la-rand.Plo + -rm -f ./$(DEPDIR)/libcurl_la-rename.Plo + -rm -f ./$(DEPDIR)/libcurl_la-request.Plo + -rm -f ./$(DEPDIR)/libcurl_la-rtsp.Plo + -rm -f ./$(DEPDIR)/libcurl_la-select.Plo + -rm -f ./$(DEPDIR)/libcurl_la-sendf.Plo + -rm -f ./$(DEPDIR)/libcurl_la-setopt.Plo + -rm -f ./$(DEPDIR)/libcurl_la-sha256.Plo + -rm -f ./$(DEPDIR)/libcurl_la-share.Plo + -rm -f ./$(DEPDIR)/libcurl_la-slist.Plo + -rm -f ./$(DEPDIR)/libcurl_la-smb.Plo + -rm -f ./$(DEPDIR)/libcurl_la-smtp.Plo + -rm -f ./$(DEPDIR)/libcurl_la-socketpair.Plo + -rm -f ./$(DEPDIR)/libcurl_la-socks.Plo + -rm -f ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-socks_sspi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-speedcheck.Plo + -rm -f ./$(DEPDIR)/libcurl_la-splay.Plo + -rm -f ./$(DEPDIR)/libcurl_la-strcase.Plo + -rm -f ./$(DEPDIR)/libcurl_la-strdup.Plo + -rm -f ./$(DEPDIR)/libcurl_la-strerror.Plo + -rm -f ./$(DEPDIR)/libcurl_la-strtok.Plo + -rm -f ./$(DEPDIR)/libcurl_la-strtoofft.Plo + -rm -f ./$(DEPDIR)/libcurl_la-system_win32.Plo + -rm -f ./$(DEPDIR)/libcurl_la-telnet.Plo + -rm -f ./$(DEPDIR)/libcurl_la-tftp.Plo + -rm -f ./$(DEPDIR)/libcurl_la-timediff.Plo + -rm -f ./$(DEPDIR)/libcurl_la-timeval.Plo + -rm -f ./$(DEPDIR)/libcurl_la-transfer.Plo + -rm -f ./$(DEPDIR)/libcurl_la-url.Plo + -rm -f ./$(DEPDIR)/libcurl_la-urlapi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-version.Plo + -rm -f ./$(DEPDIR)/libcurl_la-version_win32.Plo + -rm -f ./$(DEPDIR)/libcurl_la-warnless.Plo + -rm -f ./$(DEPDIR)/libcurl_la-ws.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-altsvc.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-amigaos.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-base64.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-bufq.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-bufref.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-c-hyper.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-h2-proxy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-https-connect.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-socket.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cfilters.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-conncache.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_des.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_get_line.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_path.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_range.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_sasl.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_sha512_256.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_sspi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_threads.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_trc.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cw-out.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-dict.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-dllmain.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-doh.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-dynbuf.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-dynhds.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-easy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-easygetopt.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-easyoptions.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-escape.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-file.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-fileinfo.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-fopen.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-formdata.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-ftp.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-ftplistparser.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-getenv.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-getinfo.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-gopher.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hash.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-headers.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hmac.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hostasyn.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hostip.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hostip4.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hostip6.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hostsyn.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hsts.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http1.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http2.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_chunks.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_digest.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_proxy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-idn.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-if2ip.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-imap.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-inet_pton.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-krb5.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-ldap.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-llist.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-macos.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-md4.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-md5.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-memdebug.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-mime.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-mprintf.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-mqtt.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-multi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-nonblock.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-pingpong.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-pop3.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-progress.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-psl.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-rand.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-rename.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-request.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-rtsp.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-select.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-sendf.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-setopt.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-sha256.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-share.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-slist.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-smb.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-smtp.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-socketpair.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-socks.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-speedcheck.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-splay.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-strcase.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-strdup.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-strerror.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-strtok.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-strtoofft.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-system_win32.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-telnet.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-tftp.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-timediff.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-timeval.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-transfer.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-url.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-urlapi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-version.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-version_win32.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-warnless.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-ws.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-cleartext.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-cram.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-digest.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-gsasl.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-oauth2.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-vauth.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-cram.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-digest.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-vauth.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-curl_msh3.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-curl_osslq.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-vquic.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-vquic.Plo + -rm -f vssh/$(DEPDIR)/libcurl_la-libssh.Plo + -rm -f vssh/$(DEPDIR)/libcurl_la-libssh2.Plo + -rm -f vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo + -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh.Plo + -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo + -rm -f vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-bearssl.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-cipher_suite.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-gtls.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-keylog.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-openssl.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-rustls.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-schannel.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-sectransp.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-vtls.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-cipher_suite.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-gtls.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-keylog.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-openssl.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-rustls.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/libcurl_la-altsvc.Plo + -rm -f ./$(DEPDIR)/libcurl_la-amigaos.Plo + -rm -f ./$(DEPDIR)/libcurl_la-asyn-ares.Plo + -rm -f ./$(DEPDIR)/libcurl_la-asyn-thread.Plo + -rm -f ./$(DEPDIR)/libcurl_la-base64.Plo + -rm -f ./$(DEPDIR)/libcurl_la-bufq.Plo + -rm -f ./$(DEPDIR)/libcurl_la-bufref.Plo + -rm -f ./$(DEPDIR)/libcurl_la-c-hyper.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-h2-proxy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-haproxy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-https-connect.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-socket.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cfilters.Plo + -rm -f ./$(DEPDIR)/libcurl_la-conncache.Plo + -rm -f ./$(DEPDIR)/libcurl_la-connect.Plo + -rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_des.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_get_line.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_path.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_range.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_sasl.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_sha512_256.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_sspi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_threads.Plo + -rm -f ./$(DEPDIR)/libcurl_la-curl_trc.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cw-out.Plo + -rm -f ./$(DEPDIR)/libcurl_la-dict.Plo + -rm -f ./$(DEPDIR)/libcurl_la-dllmain.Plo + -rm -f ./$(DEPDIR)/libcurl_la-doh.Plo + -rm -f ./$(DEPDIR)/libcurl_la-dynbuf.Plo + -rm -f ./$(DEPDIR)/libcurl_la-dynhds.Plo + -rm -f ./$(DEPDIR)/libcurl_la-easy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-easygetopt.Plo + -rm -f ./$(DEPDIR)/libcurl_la-easyoptions.Plo + -rm -f ./$(DEPDIR)/libcurl_la-escape.Plo + -rm -f ./$(DEPDIR)/libcurl_la-file.Plo + -rm -f ./$(DEPDIR)/libcurl_la-fileinfo.Plo + -rm -f ./$(DEPDIR)/libcurl_la-fopen.Plo + -rm -f ./$(DEPDIR)/libcurl_la-formdata.Plo + -rm -f ./$(DEPDIR)/libcurl_la-ftp.Plo + -rm -f ./$(DEPDIR)/libcurl_la-ftplistparser.Plo + -rm -f ./$(DEPDIR)/libcurl_la-getenv.Plo + -rm -f ./$(DEPDIR)/libcurl_la-getinfo.Plo + -rm -f ./$(DEPDIR)/libcurl_la-gopher.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hash.Plo + -rm -f ./$(DEPDIR)/libcurl_la-headers.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hmac.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hostasyn.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hostip.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hostip4.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hostip6.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hostsyn.Plo + -rm -f ./$(DEPDIR)/libcurl_la-hsts.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http1.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http2.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_chunks.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_digest.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_negotiate.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_ntlm.Plo + -rm -f ./$(DEPDIR)/libcurl_la-http_proxy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-idn.Plo + -rm -f ./$(DEPDIR)/libcurl_la-if2ip.Plo + -rm -f ./$(DEPDIR)/libcurl_la-imap.Plo + -rm -f ./$(DEPDIR)/libcurl_la-inet_ntop.Plo + -rm -f ./$(DEPDIR)/libcurl_la-inet_pton.Plo + -rm -f ./$(DEPDIR)/libcurl_la-krb5.Plo + -rm -f ./$(DEPDIR)/libcurl_la-ldap.Plo + -rm -f ./$(DEPDIR)/libcurl_la-llist.Plo + -rm -f ./$(DEPDIR)/libcurl_la-macos.Plo + -rm -f ./$(DEPDIR)/libcurl_la-md4.Plo + -rm -f ./$(DEPDIR)/libcurl_la-md5.Plo + -rm -f ./$(DEPDIR)/libcurl_la-memdebug.Plo + -rm -f ./$(DEPDIR)/libcurl_la-mime.Plo + -rm -f ./$(DEPDIR)/libcurl_la-mprintf.Plo + -rm -f ./$(DEPDIR)/libcurl_la-mqtt.Plo + -rm -f ./$(DEPDIR)/libcurl_la-multi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo + -rm -f ./$(DEPDIR)/libcurl_la-nonblock.Plo + -rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo + -rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo + -rm -f ./$(DEPDIR)/libcurl_la-pingpong.Plo + -rm -f ./$(DEPDIR)/libcurl_la-pop3.Plo + -rm -f ./$(DEPDIR)/libcurl_la-progress.Plo + -rm -f ./$(DEPDIR)/libcurl_la-psl.Plo + -rm -f ./$(DEPDIR)/libcurl_la-rand.Plo + -rm -f ./$(DEPDIR)/libcurl_la-rename.Plo + -rm -f ./$(DEPDIR)/libcurl_la-request.Plo + -rm -f ./$(DEPDIR)/libcurl_la-rtsp.Plo + -rm -f ./$(DEPDIR)/libcurl_la-select.Plo + -rm -f ./$(DEPDIR)/libcurl_la-sendf.Plo + -rm -f ./$(DEPDIR)/libcurl_la-setopt.Plo + -rm -f ./$(DEPDIR)/libcurl_la-sha256.Plo + -rm -f ./$(DEPDIR)/libcurl_la-share.Plo + -rm -f ./$(DEPDIR)/libcurl_la-slist.Plo + -rm -f ./$(DEPDIR)/libcurl_la-smb.Plo + -rm -f ./$(DEPDIR)/libcurl_la-smtp.Plo + -rm -f ./$(DEPDIR)/libcurl_la-socketpair.Plo + -rm -f ./$(DEPDIR)/libcurl_la-socks.Plo + -rm -f ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-socks_sspi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-speedcheck.Plo + -rm -f ./$(DEPDIR)/libcurl_la-splay.Plo + -rm -f ./$(DEPDIR)/libcurl_la-strcase.Plo + -rm -f ./$(DEPDIR)/libcurl_la-strdup.Plo + -rm -f ./$(DEPDIR)/libcurl_la-strerror.Plo + -rm -f ./$(DEPDIR)/libcurl_la-strtok.Plo + -rm -f ./$(DEPDIR)/libcurl_la-strtoofft.Plo + -rm -f ./$(DEPDIR)/libcurl_la-system_win32.Plo + -rm -f ./$(DEPDIR)/libcurl_la-telnet.Plo + -rm -f ./$(DEPDIR)/libcurl_la-tftp.Plo + -rm -f ./$(DEPDIR)/libcurl_la-timediff.Plo + -rm -f ./$(DEPDIR)/libcurl_la-timeval.Plo + -rm -f ./$(DEPDIR)/libcurl_la-transfer.Plo + -rm -f ./$(DEPDIR)/libcurl_la-url.Plo + -rm -f ./$(DEPDIR)/libcurl_la-urlapi.Plo + -rm -f ./$(DEPDIR)/libcurl_la-version.Plo + -rm -f ./$(DEPDIR)/libcurl_la-version_win32.Plo + -rm -f ./$(DEPDIR)/libcurl_la-warnless.Plo + -rm -f ./$(DEPDIR)/libcurl_la-ws.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-altsvc.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-amigaos.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-base64.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-bufq.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-bufref.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-c-hyper.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-h2-proxy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-https-connect.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-socket.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cfilters.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-conncache.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_des.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_get_line.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_path.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_range.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_sasl.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_sha512_256.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_sspi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_threads.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-curl_trc.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cw-out.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-dict.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-dllmain.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-doh.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-dynbuf.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-dynhds.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-easy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-easygetopt.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-easyoptions.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-escape.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-file.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-fileinfo.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-fopen.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-formdata.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-ftp.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-ftplistparser.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-getenv.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-getinfo.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-gopher.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hash.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-headers.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hmac.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hostasyn.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hostip.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hostip4.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hostip6.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hostsyn.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-hsts.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http1.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http2.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_chunks.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_digest.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-http_proxy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-idn.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-if2ip.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-imap.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-inet_pton.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-krb5.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-ldap.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-llist.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-macos.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-md4.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-md5.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-memdebug.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-mime.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-mprintf.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-mqtt.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-multi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-nonblock.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-pingpong.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-pop3.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-progress.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-psl.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-rand.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-rename.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-request.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-rtsp.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-select.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-sendf.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-setopt.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-sha256.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-share.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-slist.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-smb.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-smtp.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-socketpair.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-socks.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-speedcheck.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-splay.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-strcase.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-strdup.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-strerror.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-strtok.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-strtoofft.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-system_win32.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-telnet.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-tftp.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-timediff.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-timeval.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-transfer.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-url.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-urlapi.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-version.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-version_win32.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-warnless.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-ws.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-cleartext.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-cram.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-digest.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-gsasl.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-oauth2.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurl_la-vauth.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-cram.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-digest.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo + -rm -f vauth/$(DEPDIR)/libcurlu_la-vauth.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-curl_msh3.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-curl_osslq.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-vquic.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-vquic.Plo + -rm -f vssh/$(DEPDIR)/libcurl_la-libssh.Plo + -rm -f vssh/$(DEPDIR)/libcurl_la-libssh2.Plo + -rm -f vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo + -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh.Plo + -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo + -rm -f vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-bearssl.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-cipher_suite.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-gtls.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-keylog.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-openssl.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-rustls.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-schannel.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-sectransp.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-vtls.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-cipher_suite.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-gtls.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-keylog.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-openssl.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-rustls.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES + +.MAKE: all install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \ + check-am clean clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-hdr distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-libLTLIBRARIES + +.PRECIOUS: Makefile + +# This flag accepts an argument of the form current[:revision[:age]]. So, +# passing -version-info 3:12:1 sets current to 3, revision to 12, and age to +# 1. +# +# Here's the simplified rule guide on how to change -version-info: +# (current version is C:R:A) +# +# 1. if there are only source changes, use C:R+1:A +# 2. if interfaces were added use C+1:0:A+1 +# 3. if interfaces were removed, then use C+1:0:0 +# +# For the full guide on libcurl ABI rules, see docs/libcurl/ABI +@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@$(LIB_RCFILES): $(top_srcdir)/include/curl/curlver.h + +checksrc: + $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \ + -W$(srcdir)/curl_config.h $(srcdir)/*.[ch] $(srcdir)/vauth/*.[ch] \ + $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch] $(srcdir)/vssh/*.[ch]) + +# for debug builds, we scan the sources on all regular make invokes +@DEBUGBUILD_TRUE@all-local: checksrc + +tidy: + $(TIDY) $(CSOURCES) $(TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H + +optiontable: + perl optiontable.pl < $(top_srcdir)/include/curl/curl.h > easyoptions.c + +@HAVE_WINDRES_TRUE@.rc.lo: +@HAVE_WINDRES_TRUE@ $(LIBTOOL) --tag=RC --mode=compile $(RC) -I$(top_srcdir)/include $(RCFLAGS) -i $< -o $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/third_party/curl/lib/Makefile.inc b/third_party/curl/lib/Makefile.inc new file mode 100644 index 0000000000..66680f3adb --- /dev/null +++ b/third_party/curl/lib/Makefile.inc @@ -0,0 +1,381 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +LIB_VAUTH_CFILES = \ + vauth/cleartext.c \ + vauth/cram.c \ + vauth/digest.c \ + vauth/digest_sspi.c \ + vauth/gsasl.c \ + vauth/krb5_gssapi.c \ + vauth/krb5_sspi.c \ + vauth/ntlm.c \ + vauth/ntlm_sspi.c \ + vauth/oauth2.c \ + vauth/spnego_gssapi.c \ + vauth/spnego_sspi.c \ + vauth/vauth.c + +LIB_VAUTH_HFILES = \ + vauth/digest.h \ + vauth/ntlm.h \ + vauth/vauth.h + +LIB_VTLS_CFILES = \ + vtls/bearssl.c \ + vtls/cipher_suite.c \ + vtls/gtls.c \ + vtls/hostcheck.c \ + vtls/keylog.c \ + vtls/mbedtls.c \ + vtls/mbedtls_threadlock.c \ + vtls/openssl.c \ + vtls/rustls.c \ + vtls/schannel.c \ + vtls/schannel_verify.c \ + vtls/sectransp.c \ + vtls/vtls.c \ + vtls/wolfssl.c \ + vtls/x509asn1.c + +LIB_VTLS_HFILES = \ + vtls/bearssl.h \ + vtls/cipher_suite.h \ + vtls/gtls.h \ + vtls/hostcheck.h \ + vtls/keylog.h \ + vtls/mbedtls.h \ + vtls/mbedtls_threadlock.h \ + vtls/openssl.h \ + vtls/rustls.h \ + vtls/schannel.h \ + vtls/schannel_int.h \ + vtls/sectransp.h \ + vtls/vtls.h \ + vtls/vtls_int.h \ + vtls/wolfssl.h \ + vtls/x509asn1.h + +LIB_VQUIC_CFILES = \ + vquic/curl_msh3.c \ + vquic/curl_ngtcp2.c \ + vquic/curl_osslq.c \ + vquic/curl_quiche.c \ + vquic/vquic.c \ + vquic/vquic-tls.c + +LIB_VQUIC_HFILES = \ + vquic/curl_msh3.h \ + vquic/curl_ngtcp2.h \ + vquic/curl_osslq.h \ + vquic/curl_quiche.h \ + vquic/vquic.h \ + vquic/vquic_int.h \ + vquic/vquic-tls.h + +LIB_VSSH_CFILES = \ + vssh/libssh.c \ + vssh/libssh2.c \ + vssh/wolfssh.c + +LIB_VSSH_HFILES = \ + vssh/ssh.h + +LIB_CFILES = \ + altsvc.c \ + amigaos.c \ + asyn-ares.c \ + asyn-thread.c \ + base64.c \ + bufq.c \ + bufref.c \ + c-hyper.c \ + cf-h1-proxy.c \ + cf-h2-proxy.c \ + cf-haproxy.c \ + cf-https-connect.c \ + cf-socket.c \ + cfilters.c \ + conncache.c \ + connect.c \ + content_encoding.c \ + cookie.c \ + curl_addrinfo.c \ + curl_des.c \ + curl_endian.c \ + curl_fnmatch.c \ + curl_get_line.c \ + curl_gethostname.c \ + curl_gssapi.c \ + curl_memrchr.c \ + curl_multibyte.c \ + curl_ntlm_core.c \ + curl_path.c \ + curl_range.c \ + curl_rtmp.c \ + curl_sasl.c \ + curl_sha512_256.c \ + curl_sspi.c \ + curl_threads.c \ + curl_trc.c \ + cw-out.c \ + dict.c \ + dllmain.c \ + doh.c \ + dynbuf.c \ + dynhds.c \ + easy.c \ + easygetopt.c \ + easyoptions.c \ + escape.c \ + file.c \ + fileinfo.c \ + fopen.c \ + formdata.c \ + ftp.c \ + ftplistparser.c \ + getenv.c \ + getinfo.c \ + gopher.c \ + hash.c \ + headers.c \ + hmac.c \ + hostasyn.c \ + hostip.c \ + hostip4.c \ + hostip6.c \ + hostsyn.c \ + hsts.c \ + http.c \ + http1.c \ + http2.c \ + http_aws_sigv4.c \ + http_chunks.c \ + http_digest.c \ + http_negotiate.c \ + http_ntlm.c \ + http_proxy.c \ + idn.c \ + if2ip.c \ + imap.c \ + inet_ntop.c \ + inet_pton.c \ + krb5.c \ + ldap.c \ + llist.c \ + macos.c \ + md4.c \ + md5.c \ + memdebug.c \ + mime.c \ + mprintf.c \ + mqtt.c \ + multi.c \ + netrc.c \ + nonblock.c \ + noproxy.c \ + openldap.c \ + parsedate.c \ + pingpong.c \ + pop3.c \ + progress.c \ + psl.c \ + rand.c \ + rename.c \ + request.c \ + rtsp.c \ + select.c \ + sendf.c \ + setopt.c \ + sha256.c \ + share.c \ + slist.c \ + smb.c \ + smtp.c \ + socketpair.c \ + socks.c \ + socks_gssapi.c \ + socks_sspi.c \ + speedcheck.c \ + splay.c \ + strcase.c \ + strdup.c \ + strerror.c \ + strtok.c \ + strtoofft.c \ + system_win32.c \ + telnet.c \ + tftp.c \ + timediff.c \ + timeval.c \ + transfer.c \ + url.c \ + urlapi.c \ + version.c \ + version_win32.c \ + warnless.c \ + ws.c + +LIB_HFILES = \ + altsvc.h \ + amigaos.h \ + arpa_telnet.h \ + asyn.h \ + bufq.h \ + bufref.h \ + c-hyper.h \ + cf-h1-proxy.h \ + cf-h2-proxy.h \ + cf-haproxy.h \ + cf-https-connect.h \ + cf-socket.h \ + cfilters.h \ + conncache.h \ + connect.h \ + content_encoding.h \ + cookie.h \ + curl_addrinfo.h \ + curl_base64.h \ + curl_ctype.h \ + curl_des.h \ + curl_endian.h \ + curl_fnmatch.h \ + curl_get_line.h \ + curl_gethostname.h \ + curl_gssapi.h \ + curl_hmac.h \ + curl_krb5.h \ + curl_ldap.h \ + curl_md4.h \ + curl_md5.h \ + curl_memory.h \ + curl_memrchr.h \ + curl_multibyte.h \ + curl_ntlm_core.h \ + curl_path.h \ + curl_printf.h \ + curl_range.h \ + curl_rtmp.h \ + curl_sasl.h \ + curl_setup.h \ + curl_setup_once.h \ + curl_sha256.h \ + curl_sha512_256.h \ + curl_sspi.h \ + curl_threads.h \ + curl_trc.h \ + curlx.h \ + cw-out.h \ + dict.h \ + doh.h \ + dynbuf.h \ + dynhds.h \ + easy_lock.h \ + easyif.h \ + easyoptions.h \ + escape.h \ + file.h \ + fileinfo.h \ + fopen.h \ + formdata.h \ + ftp.h \ + ftplistparser.h \ + functypes.h \ + getinfo.h \ + gopher.h \ + hash.h \ + headers.h \ + hostip.h \ + hsts.h \ + http.h \ + http1.h \ + http2.h \ + http_aws_sigv4.h \ + http_chunks.h \ + http_digest.h \ + http_negotiate.h \ + http_ntlm.h \ + http_proxy.h \ + idn.h \ + if2ip.h \ + imap.h \ + inet_ntop.h \ + inet_pton.h \ + llist.h \ + macos.h \ + memdebug.h \ + mime.h \ + mqtt.h \ + multihandle.h \ + multiif.h \ + netrc.h \ + nonblock.h \ + noproxy.h \ + parsedate.h \ + pingpong.h \ + pop3.h \ + progress.h \ + psl.h \ + rand.h \ + rename.h \ + request.h \ + rtsp.h \ + select.h \ + sendf.h \ + setopt.h \ + setup-vms.h \ + share.h \ + sigpipe.h \ + slist.h \ + smb.h \ + smtp.h \ + sockaddr.h \ + socketpair.h \ + socks.h \ + speedcheck.h \ + splay.h \ + strcase.h \ + strdup.h \ + strerror.h \ + strtok.h \ + strtoofft.h \ + system_win32.h \ + telnet.h \ + tftp.h \ + timediff.h \ + timeval.h \ + transfer.h \ + url.h \ + urlapi-int.h \ + urldata.h \ + version_win32.h \ + warnless.h \ + ws.h + +LIB_RCFILES = libcurl.rc + +CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \ + $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) +HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \ + $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) diff --git a/third_party/curl/lib/Makefile.mk b/third_party/curl/lib/Makefile.mk new file mode 100644 index 0000000000..bb6873cc45 --- /dev/null +++ b/third_party/curl/lib/Makefile.mk @@ -0,0 +1,334 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +#*************************************************************************** + +# Makefile to build curl parts with GCC-like toolchains and optional features. +# +# Usage: make -f Makefile.mk CFG=-feat1[-feat2][-feat3][...] +# Example: make -f Makefile.mk CFG=-zlib-ssl-libssh2-ipv6 +# +# Look for ' ?=' to find all accepted customization variables. + +# This script is reused by 'src' and 'docs/examples' Makefile.mk scripts. + +ifndef PROOT + PROOT := .. + LOCAL := 1 +endif + +### Common + +CFLAGS ?= +CPPFLAGS ?= +LDFLAGS ?= +LIBS ?= + +CROSSPREFIX ?= + +ifeq ($(CC),cc) + CC := gcc +endif +CC := $(CROSSPREFIX)$(CC) +AR := $(CROSSPREFIX)$(AR) + +TRIPLET ?= $(shell $(CC) -dumpmachine) + +BIN_EXT := + +ifneq ($(findstring msdos,$(TRIPLET)),) + # Cross-tools: https://github.com/andrewwutw/build-djgpp + MSDOS := 1 + BIN_EXT := .exe +else ifneq ($(findstring amigaos,$(TRIPLET)),) + # Cross-tools: https://github.com/bebbo/amiga-gcc + AMIGA := 1 +endif + +CPPFLAGS += -I. -I$(PROOT)/include + +### Deprecated settings. For compatibility. + +ifdef WATT_ROOT + WATT_PATH := $(realpath $(WATT_ROOT)) +endif + +### Optional features + +ifneq ($(findstring -debug,$(CFG)),) + CFLAGS += -g + CPPFLAGS += -DDEBUGBUILD +else + CPPFLAGS += -DNDEBUG +endif +ifneq ($(findstring -trackmem,$(CFG)),) + CPPFLAGS += -DCURLDEBUG +endif +ifneq ($(findstring -map,$(CFG)),) + MAP := 1 +endif + +# CPPFLAGS below are only necessary when building libcurl via 'lib' (see +# comments below about exceptions). Always include them anyway to match +# behavior of other build systems. + +ifneq ($(findstring -sync,$(CFG)),) + CPPFLAGS += -DUSE_SYNC_DNS +else ifneq ($(findstring -ares,$(CFG)),) + LIBCARES_PATH ?= $(PROOT)/../c-ares + CPPFLAGS += -DUSE_ARES + CPPFLAGS += -I"$(LIBCARES_PATH)/include" + LDFLAGS += -L"$(LIBCARES_PATH)/lib" + LIBS += -lcares +endif + +ifneq ($(findstring -rtmp,$(CFG)),) + LIBRTMP_PATH ?= $(PROOT)/../librtmp + CPPFLAGS += -DUSE_LIBRTMP + CPPFLAGS += -I"$(LIBRTMP_PATH)" + LDFLAGS += -L"$(LIBRTMP_PATH)/librtmp" + LIBS += -lrtmp + ZLIB := 1 +endif + +ifneq ($(findstring -ssh2,$(CFG)),) + LIBSSH2_PATH ?= $(PROOT)/../libssh2 + CPPFLAGS += -DUSE_LIBSSH2 + CPPFLAGS += -I"$(LIBSSH2_PATH)/include" + LDFLAGS += -L"$(LIBSSH2_PATH)/lib" + LIBS += -lssh2 +else ifneq ($(findstring -libssh,$(CFG)),) + LIBSSH_PATH ?= $(PROOT)/../libssh + CPPFLAGS += -DUSE_LIBSSH + CPPFLAGS += -I"$(LIBSSH_PATH)/include" + LDFLAGS += -L"$(LIBSSH_PATH)/lib" + LIBS += -lssh +else ifneq ($(findstring -wolfssh,$(CFG)),) + WOLFSSH_PATH ?= $(PROOT)/../wolfssh + CPPFLAGS += -DUSE_WOLFSSH + CPPFLAGS += -I"$(WOLFSSH_PATH)/include" + LDFLAGS += -L"$(WOLFSSH_PATH)/lib" + LIBS += -lwolfssh +endif + +ifneq ($(findstring -ssl,$(CFG)),) + OPENSSL_PATH ?= $(PROOT)/../openssl + CPPFLAGS += -DUSE_OPENSSL + CPPFLAGS += -DCURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG + OPENSSL_INCLUDE ?= $(OPENSSL_PATH)/include + OPENSSL_LIBPATH ?= $(OPENSSL_PATH)/lib + CPPFLAGS += -I"$(OPENSSL_INCLUDE)" + LDFLAGS += -L"$(OPENSSL_LIBPATH)" + OPENSSL_LIBS ?= -lssl -lcrypto + LIBS += $(OPENSSL_LIBS) + + ifneq ($(findstring -srp,$(CFG)),) + ifneq ($(wildcard $(OPENSSL_INCLUDE)/openssl/srp.h),) + # OpenSSL 1.0.1 and later. + CPPFLAGS += -DHAVE_OPENSSL_SRP -DUSE_TLS_SRP + endif + endif + SSLLIBS += 1 +endif +ifneq ($(findstring -wolfssl,$(CFG)),) + WOLFSSL_PATH ?= $(PROOT)/../wolfssl + CPPFLAGS += -DUSE_WOLFSSL + CPPFLAGS += -DSIZEOF_LONG_LONG=8 + CPPFLAGS += -I"$(WOLFSSL_PATH)/include" + LDFLAGS += -L"$(WOLFSSL_PATH)/lib" + LIBS += -lwolfssl + SSLLIBS += 1 +endif +ifneq ($(findstring -mbedtls,$(CFG)),) + MBEDTLS_PATH ?= $(PROOT)/../mbedtls + CPPFLAGS += -DUSE_MBEDTLS + CPPFLAGS += -I"$(MBEDTLS_PATH)/include" + LDFLAGS += -L"$(MBEDTLS_PATH)/lib" + LIBS += -lmbedtls -lmbedx509 -lmbedcrypto + SSLLIBS += 1 +endif + +ifneq ($(findstring -nghttp2,$(CFG)),) + NGHTTP2_PATH ?= $(PROOT)/../nghttp2 + CPPFLAGS += -DUSE_NGHTTP2 + CPPFLAGS += -I"$(NGHTTP2_PATH)/include" + LDFLAGS += -L"$(NGHTTP2_PATH)/lib" + LIBS += -lnghttp2 +endif + +ifeq ($(findstring -nghttp3,$(CFG))$(findstring -ngtcp2,$(CFG)),-nghttp3-ngtcp2) + NGHTTP3_PATH ?= $(PROOT)/../nghttp3 + CPPFLAGS += -DUSE_NGHTTP3 + CPPFLAGS += -I"$(NGHTTP3_PATH)/include" + LDFLAGS += -L"$(NGHTTP3_PATH)/lib" + LIBS += -lnghttp3 + + NGTCP2_PATH ?= $(PROOT)/../ngtcp2 + CPPFLAGS += -DUSE_NGTCP2 + CPPFLAGS += -I"$(NGTCP2_PATH)/include" + LDFLAGS += -L"$(NGTCP2_PATH)/lib" + + NGTCP2_LIBS ?= + ifeq ($(NGTCP2_LIBS),) + ifneq ($(findstring -ssl,$(CFG)),) + ifneq ($(wildcard $(OPENSSL_INCLUDE)/openssl/aead.h),) + NGTCP2_LIBS := -lngtcp2_crypto_boringssl + else # including libressl + NGTCP2_LIBS := -lngtcp2_crypto_quictls + endif + else ifneq ($(findstring -wolfssl,$(CFG)),) + NGTCP2_LIBS := -lngtcp2_crypto_wolfssl + endif + endif + + LIBS += -lngtcp2 $(NGTCP2_LIBS) +endif + +ifneq ($(findstring -zlib,$(CFG))$(ZLIB),) + ZLIB_PATH ?= $(PROOT)/../zlib + # These CPPFLAGS are also required when compiling the curl tool via 'src'. + CPPFLAGS += -DHAVE_LIBZ + CPPFLAGS += -I"$(ZLIB_PATH)/include" + LDFLAGS += -L"$(ZLIB_PATH)/lib" + ZLIB_LIBS ?= -lz + LIBS += $(ZLIB_LIBS) + ZLIB := 1 +endif +ifneq ($(findstring -zstd,$(CFG)),) + ZSTD_PATH ?= $(PROOT)/../zstd + CPPFLAGS += -DHAVE_ZSTD + CPPFLAGS += -I"$(ZSTD_PATH)/include" + LDFLAGS += -L"$(ZSTD_PATH)/lib" + ZSTD_LIBS ?= -lzstd + LIBS += $(ZSTD_LIBS) +endif +ifneq ($(findstring -brotli,$(CFG)),) + BROTLI_PATH ?= $(PROOT)/../brotli + CPPFLAGS += -DHAVE_BROTLI + CPPFLAGS += -I"$(BROTLI_PATH)/include" + LDFLAGS += -L"$(BROTLI_PATH)/lib" + BROTLI_LIBS ?= -lbrotlidec -lbrotlicommon + LIBS += $(BROTLI_LIBS) +endif +ifneq ($(findstring -gsasl,$(CFG)),) + LIBGSASL_PATH ?= $(PROOT)/../gsasl + CPPFLAGS += -DUSE_GSASL + CPPFLAGS += -I"$(LIBGSASL_PATH)/include" + LDFLAGS += -L"$(LIBGSASL_PATH)/lib" + LIBS += -lgsasl +endif + +ifneq ($(findstring -idn2,$(CFG)),) + LIBIDN2_PATH ?= $(PROOT)/../libidn2 + CPPFLAGS += -DUSE_LIBIDN2 + CPPFLAGS += -I"$(LIBIDN2_PATH)/include" + LDFLAGS += -L"$(LIBIDN2_PATH)/lib" + LIBS += -lidn2 + +ifneq ($(findstring -psl,$(CFG)),) + LIBPSL_PATH ?= $(PROOT)/../libpsl + CPPFLAGS += -DUSE_LIBPSL + CPPFLAGS += -I"$(LIBPSL_PATH)/include" + LDFLAGS += -L"$(LIBPSL_PATH)/lib" + LIBS += -lpsl +endif +endif + +ifneq ($(findstring -ipv6,$(CFG)),) + CPPFLAGS += -DUSE_IPV6 +endif + +ifneq ($(findstring -watt,$(CFG))$(MSDOS),) + WATT_PATH ?= $(PROOT)/../watt + CPPFLAGS += -I"$(WATT_PATH)/inc" + LDFLAGS += -L"$(WATT_PATH)/lib" + LIBS += -lwatt +endif + +ifneq ($(findstring 11,$(subst $(subst ,, ),,$(SSLLIBS))),) + CPPFLAGS += -DCURL_WITH_MULTI_SSL +endif + +### Common rules + +OBJ_DIR := $(TRIPLET) + +ifneq ($(findstring /sh,$(SHELL)),) +DEL = rm -f $1 +COPY = -cp -afv $1 $2 +MKDIR = mkdir -p $1 +RMDIR = rm -fr $1 +WHICH = $(SHELL) -c "command -v $1" +else +DEL = -del 2>NUL /q /f $(subst /,\,$1) +COPY = -copy 2>NUL /y $(subst /,\,$1) $(subst /,\,$2) +MKDIR = -md 2>NUL $(subst /,\,$1) +RMDIR = -rd 2>NUL /q /s $(subst /,\,$1) +WHICH = where $1 +endif + +all: $(TARGETS) + +$(OBJ_DIR): + -$(call MKDIR, $(OBJ_DIR)) + +$(OBJ_DIR)/%.o: %.c + $(CC) -W -Wall $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + +clean: + @$(call DEL, $(TOCLEAN)) + @$(RMDIR) $(OBJ_DIR) + +distclean vclean: clean + @$(call DEL, $(TARGETS) $(TOVCLEAN)) + +### Local + +ifdef LOCAL + +CPPFLAGS += -DBUILDING_LIBCURL + +### Sources and targets + +# Provides CSOURCES, HHEADERS +include Makefile.inc + +vpath %.c vauth vquic vssh vtls + +libcurl_a_LIBRARY := libcurl.a + +TARGETS := $(libcurl_a_LIBRARY) + +libcurl_a_OBJECTS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(notdir $(strip $(CSOURCES)))) +libcurl_a_DEPENDENCIES := $(strip $(CSOURCES) $(HHEADERS)) + +TOCLEAN := +TOVCLEAN := + +### Rules + +$(libcurl_a_LIBRARY): $(libcurl_a_OBJECTS) $(libcurl_a_DEPENDENCIES) + @$(call DEL, $@) + $(AR) rcs $@ $(libcurl_a_OBJECTS) + +all: $(OBJ_DIR) $(TARGETS) +endif diff --git a/third_party/curl/lib/Makefile.soname b/third_party/curl/lib/Makefile.soname new file mode 100644 index 0000000000..02e003a8aa --- /dev/null +++ b/third_party/curl/lib/Makefile.soname @@ -0,0 +1,42 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +VERSIONCHANGE=12 +VERSIONADD=0 +VERSIONDEL=8 + +# libtool version: +VERSIONINFO=-version-info $(VERSIONCHANGE):$(VERSIONADD):$(VERSIONDEL) +# This flag accepts an argument of the form current[:revision[:age]]. So, +# passing -version-info 3:12:1 sets current to 3, revision to 12, and age to +# 1. +# +# Here's the simplified rule guide on how to change -version-info: +# (current version is C:R:A) +# +# 1. if there are only source changes, use C:R+1:A +# 2. if interfaces were added use C+1:0:A+1 +# 3. if interfaces were removed, then use C+1:0:0 +# +# For the full guide on libcurl ABI rules, see docs/libcurl/ABI diff --git a/third_party/curl/lib/altsvc.c b/third_party/curl/lib/altsvc.c new file mode 100644 index 0000000000..db9749c258 --- /dev/null +++ b/third_party/curl/lib/altsvc.c @@ -0,0 +1,708 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* + * The Alt-Svc: header is defined in RFC 7838: + * https://datatracker.ietf.org/doc/html/rfc7838 + */ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC) +#include +#include "urldata.h" +#include "altsvc.h" +#include "curl_get_line.h" +#include "strcase.h" +#include "parsedate.h" +#include "sendf.h" +#include "warnless.h" +#include "fopen.h" +#include "rename.h" +#include "strdup.h" +#include "inet_pton.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define MAX_ALTSVC_LINE 4095 +#define MAX_ALTSVC_DATELENSTR "64" +#define MAX_ALTSVC_DATELEN 64 +#define MAX_ALTSVC_HOSTLENSTR "512" +#define MAX_ALTSVC_HOSTLEN 512 +#define MAX_ALTSVC_ALPNLENSTR "10" +#define MAX_ALTSVC_ALPNLEN 10 + +#define H3VERSION "h3" + +static enum alpnid alpn2alpnid(char *name) +{ + if(strcasecompare(name, "h1")) + return ALPN_h1; + if(strcasecompare(name, "h2")) + return ALPN_h2; + if(strcasecompare(name, H3VERSION)) + return ALPN_h3; + return ALPN_none; /* unknown, probably rubbish input */ +} + +/* Given the ALPN ID, return the name */ +const char *Curl_alpnid2str(enum alpnid id) +{ + switch(id) { + case ALPN_h1: + return "h1"; + case ALPN_h2: + return "h2"; + case ALPN_h3: + return H3VERSION; + default: + return ""; /* bad */ + } +} + + +static void altsvc_free(struct altsvc *as) +{ + free(as->src.host); + free(as->dst.host); + free(as); +} + +static struct altsvc *altsvc_createid(const char *srchost, + const char *dsthost, + enum alpnid srcalpnid, + enum alpnid dstalpnid, + unsigned int srcport, + unsigned int dstport) +{ + struct altsvc *as = calloc(1, sizeof(struct altsvc)); + size_t hlen; + size_t dlen; + if(!as) + return NULL; + hlen = strlen(srchost); + dlen = strlen(dsthost); + DEBUGASSERT(hlen); + DEBUGASSERT(dlen); + if(!hlen || !dlen) { + /* bad input */ + free(as); + return NULL; + } + if((hlen > 2) && srchost[0] == '[') { + /* IPv6 address, strip off brackets */ + srchost++; + hlen -= 2; + } + else if(srchost[hlen - 1] == '.') + /* strip off trailing dot */ + hlen--; + if((dlen > 2) && dsthost[0] == '[') { + /* IPv6 address, strip off brackets */ + dsthost++; + dlen -= 2; + } + + as->src.host = Curl_memdup0(srchost, hlen); + if(!as->src.host) + goto error; + + as->dst.host = Curl_memdup0(dsthost, dlen); + if(!as->dst.host) + goto error; + + as->src.alpnid = srcalpnid; + as->dst.alpnid = dstalpnid; + as->src.port = curlx_ultous(srcport); + as->dst.port = curlx_ultous(dstport); + + return as; +error: + altsvc_free(as); + return NULL; +} + +static struct altsvc *altsvc_create(char *srchost, + char *dsthost, + char *srcalpn, + char *dstalpn, + unsigned int srcport, + unsigned int dstport) +{ + enum alpnid dstalpnid = alpn2alpnid(dstalpn); + enum alpnid srcalpnid = alpn2alpnid(srcalpn); + if(!srcalpnid || !dstalpnid) + return NULL; + return altsvc_createid(srchost, dsthost, srcalpnid, dstalpnid, + srcport, dstport); +} + +/* only returns SERIOUS errors */ +static CURLcode altsvc_add(struct altsvcinfo *asi, char *line) +{ + /* Example line: + h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1 + */ + char srchost[MAX_ALTSVC_HOSTLEN + 1]; + char dsthost[MAX_ALTSVC_HOSTLEN + 1]; + char srcalpn[MAX_ALTSVC_ALPNLEN + 1]; + char dstalpn[MAX_ALTSVC_ALPNLEN + 1]; + char date[MAX_ALTSVC_DATELEN + 1]; + unsigned int srcport; + unsigned int dstport; + unsigned int prio; + unsigned int persist; + int rc; + + rc = sscanf(line, + "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u " + "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u " + "\"%" MAX_ALTSVC_DATELENSTR "[^\"]\" %u %u", + srcalpn, srchost, &srcport, + dstalpn, dsthost, &dstport, + date, &persist, &prio); + if(9 == rc) { + struct altsvc *as; + time_t expires = Curl_getdate_capped(date); + as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport); + if(as) { + as->expires = expires; + as->prio = prio; + as->persist = persist ? 1 : 0; + Curl_llist_append(&asi->list, as, &as->node); + } + } + + return CURLE_OK; +} + +/* + * Load alt-svc entries from the given file. The text based line-oriented file + * format is documented here: https://curl.se/docs/alt-svc.html + * + * This function only returns error on major problems that prevent alt-svc + * handling to work completely. It will ignore individual syntactical errors + * etc. + */ +static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) +{ + CURLcode result = CURLE_OK; + FILE *fp; + + /* we need a private copy of the filename so that the altsvc cache file + name survives an easy handle reset */ + free(asi->filename); + asi->filename = strdup(file); + if(!asi->filename) + return CURLE_OUT_OF_MEMORY; + + fp = fopen(file, FOPEN_READTEXT); + if(fp) { + struct dynbuf buf; + Curl_dyn_init(&buf, MAX_ALTSVC_LINE); + while(Curl_get_line(&buf, fp)) { + char *lineptr = Curl_dyn_ptr(&buf); + while(*lineptr && ISBLANK(*lineptr)) + lineptr++; + if(*lineptr == '#') + /* skip commented lines */ + continue; + + altsvc_add(asi, lineptr); + } + Curl_dyn_free(&buf); /* free the line buffer */ + fclose(fp); + } + return result; +} + +/* + * Write this single altsvc entry to a single output line + */ + +static CURLcode altsvc_out(struct altsvc *as, FILE *fp) +{ + struct tm stamp; + const char *dst6_pre = ""; + const char *dst6_post = ""; + const char *src6_pre = ""; + const char *src6_post = ""; + CURLcode result = Curl_gmtime(as->expires, &stamp); + if(result) + return result; +#ifdef USE_IPV6 + else { + char ipv6_unused[16]; + if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) { + dst6_pre = "["; + dst6_post = "]"; + } + if(1 == Curl_inet_pton(AF_INET6, as->src.host, ipv6_unused)) { + src6_pre = "["; + src6_post = "]"; + } + } +#endif + fprintf(fp, + "%s %s%s%s %u " + "%s %s%s%s %u " + "\"%d%02d%02d " + "%02d:%02d:%02d\" " + "%u %u\n", + Curl_alpnid2str(as->src.alpnid), + src6_pre, as->src.host, src6_post, + as->src.port, + + Curl_alpnid2str(as->dst.alpnid), + dst6_pre, as->dst.host, dst6_post, + as->dst.port, + + stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, + stamp.tm_hour, stamp.tm_min, stamp.tm_sec, + as->persist, as->prio); + return CURLE_OK; +} + +/* ---- library-wide functions below ---- */ + +/* + * Curl_altsvc_init() creates a new altsvc cache. + * It returns the new instance or NULL if something goes wrong. + */ +struct altsvcinfo *Curl_altsvc_init(void) +{ + struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo)); + if(!asi) + return NULL; + Curl_llist_init(&asi->list, NULL); + + /* set default behavior */ + asi->flags = CURLALTSVC_H1 +#ifdef USE_HTTP2 + | CURLALTSVC_H2 +#endif +#ifdef USE_HTTP3 + | CURLALTSVC_H3 +#endif + ; + return asi; +} + +/* + * Curl_altsvc_load() loads alt-svc from file. + */ +CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file) +{ + CURLcode result; + DEBUGASSERT(asi); + result = altsvc_load(asi, file); + return result; +} + +/* + * Curl_altsvc_ctrl() passes on the external bitmask. + */ +CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl) +{ + DEBUGASSERT(asi); + asi->flags = ctrl; + return CURLE_OK; +} + +/* + * Curl_altsvc_cleanup() frees an altsvc cache instance and all associated + * resources. + */ +void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp) +{ + struct Curl_llist_element *e; + struct Curl_llist_element *n; + if(*altsvcp) { + struct altsvcinfo *altsvc = *altsvcp; + for(e = altsvc->list.head; e; e = n) { + struct altsvc *as = e->ptr; + n = e->next; + altsvc_free(as); + } + free(altsvc->filename); + free(altsvc); + *altsvcp = NULL; /* clear the pointer */ + } +} + +/* + * Curl_altsvc_save() writes the altsvc cache to a file. + */ +CURLcode Curl_altsvc_save(struct Curl_easy *data, + struct altsvcinfo *altsvc, const char *file) +{ + struct Curl_llist_element *e; + struct Curl_llist_element *n; + CURLcode result = CURLE_OK; + FILE *out; + char *tempstore = NULL; + + if(!altsvc) + /* no cache activated */ + return CURLE_OK; + + /* if not new name is given, use the one we stored from the load */ + if(!file && altsvc->filename) + file = altsvc->filename; + + if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0]) + /* marked as read-only, no file or zero length filename */ + return CURLE_OK; + + result = Curl_fopen(data, file, &out, &tempstore); + if(!result) { + fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n" + "# This file was generated by libcurl! Edit at your own risk.\n", + out); + for(e = altsvc->list.head; e; e = n) { + struct altsvc *as = e->ptr; + n = e->next; + result = altsvc_out(as, out); + if(result) + break; + } + fclose(out); + if(!result && tempstore && Curl_rename(tempstore, file)) + result = CURLE_WRITE_ERROR; + + if(result && tempstore) + unlink(tempstore); + } + free(tempstore); + return result; +} + +static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen) +{ + size_t len; + const char *protop; + const char *p = *ptr; + while(*p && ISBLANK(*p)) + p++; + protop = p; + while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '=')) + p++; + len = p - protop; + *ptr = p; + + if(!len || (len >= buflen)) + return CURLE_BAD_FUNCTION_ARGUMENT; + memcpy(alpnbuf, protop, len); + alpnbuf[len] = 0; + return CURLE_OK; +} + +/* hostcompare() returns true if 'host' matches 'check'. The first host + * argument may have a trailing dot present that will be ignored. + */ +static bool hostcompare(const char *host, const char *check) +{ + size_t hlen = strlen(host); + size_t clen = strlen(check); + + if(hlen && (host[hlen - 1] == '.')) + hlen--; + if(hlen != clen) + /* they cannot match if they have different lengths */ + return FALSE; + return strncasecompare(host, check, hlen); +} + +/* altsvc_flush() removes all alternatives for this source origin from the + list */ +static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid, + const char *srchost, unsigned short srcport) +{ + struct Curl_llist_element *e; + struct Curl_llist_element *n; + for(e = asi->list.head; e; e = n) { + struct altsvc *as = e->ptr; + n = e->next; + if((srcalpnid == as->src.alpnid) && + (srcport == as->src.port) && + hostcompare(srchost, as->src.host)) { + Curl_llist_remove(&asi->list, e, NULL); + altsvc_free(as); + } + } +} + +#ifdef DEBUGBUILD +/* to play well with debug builds, we can *set* a fixed time this will + return */ +static time_t altsvc_debugtime(void *unused) +{ + char *timestr = getenv("CURL_TIME"); + (void)unused; + if(timestr) { + long val = strtol(timestr, NULL, 10); + return (time_t)val; + } + return time(NULL); +} +#undef time +#define time(x) altsvc_debugtime(x) +#endif + +#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') + +/* + * Curl_altsvc_parse() takes an incoming alt-svc response header and stores + * the data correctly in the cache. + * + * 'value' points to the header *value*. That is contents to the right of the + * header name. + * + * Currently this function rejects invalid data without returning an error. + * Invalid hostname, port number will result in the specific alternative + * being rejected. Unknown protocols are skipped. + */ +CURLcode Curl_altsvc_parse(struct Curl_easy *data, + struct altsvcinfo *asi, const char *value, + enum alpnid srcalpnid, const char *srchost, + unsigned short srcport) +{ + const char *p = value; + size_t len; + char namebuf[MAX_ALTSVC_HOSTLEN] = ""; + char alpnbuf[MAX_ALTSVC_ALPNLEN] = ""; + struct altsvc *as; + unsigned short dstport = srcport; /* the same by default */ + CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf)); + size_t entries = 0; +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)data; +#endif + if(result) { + infof(data, "Excessive alt-svc header, ignoring."); + return CURLE_OK; + } + + DEBUGASSERT(asi); + + /* "clear" is a magic keyword */ + if(strcasecompare(alpnbuf, "clear")) { + /* Flush cached alternatives for this source origin */ + altsvc_flush(asi, srcalpnid, srchost, srcport); + return CURLE_OK; + } + + do { + if(*p == '=') { + /* [protocol]="[host][:port]" */ + enum alpnid dstalpnid = alpn2alpnid(alpnbuf); /* the same by default */ + p++; + if(*p == '\"') { + const char *dsthost = ""; + const char *value_ptr; + char option[32]; + unsigned long num; + char *end_ptr; + bool quoted = FALSE; + time_t maxage = 24 * 3600; /* default is 24 hours */ + bool persist = FALSE; + bool valid = TRUE; + p++; + if(*p != ':') { + /* hostname starts here */ + const char *hostp = p; + if(*p == '[') { + /* pass all valid IPv6 letters - does not handle zone id */ + len = strspn(++p, "0123456789abcdefABCDEF:."); + if(p[len] != ']') + /* invalid host syntax, bail out */ + break; + /* we store the IPv6 numerical address *with* brackets */ + len += 2; + p = &p[len-1]; + } + else { + while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) + p++; + len = p - hostp; + } + if(!len || (len >= MAX_ALTSVC_HOSTLEN)) { + infof(data, "Excessive alt-svc hostname, ignoring."); + valid = FALSE; + } + else { + memcpy(namebuf, hostp, len); + namebuf[len] = 0; + dsthost = namebuf; + } + } + else { + /* no destination name, use source host */ + dsthost = srchost; + } + if(*p == ':') { + unsigned long port = 0; + p++; + if(ISDIGIT(*p)) + /* a port number */ + port = strtoul(p, &end_ptr, 10); + else + end_ptr = (char *)p; /* not left uninitialized */ + if(!port || port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') { + infof(data, "Unknown alt-svc port number, ignoring."); + valid = FALSE; + } + else { + dstport = curlx_ultous(port); + p = end_ptr; + } + } + if(*p++ != '\"') + break; + /* Handle the optional 'ma' and 'persist' flags. Unknown flags + are skipped. */ + for(;;) { + while(ISBLANK(*p)) + p++; + if(*p != ';') + break; + p++; /* pass the semicolon */ + if(!*p || ISNEWLINE(*p)) + break; + result = getalnum(&p, option, sizeof(option)); + if(result) { + /* skip option if name is too long */ + option[0] = '\0'; + } + while(*p && ISBLANK(*p)) + p++; + if(*p != '=') + return CURLE_OK; + p++; + while(*p && ISBLANK(*p)) + p++; + if(!*p) + return CURLE_OK; + if(*p == '\"') { + /* quoted value */ + p++; + quoted = TRUE; + } + value_ptr = p; + if(quoted) { + while(*p && *p != '\"') + p++; + if(!*p++) + return CURLE_OK; + } + else { + while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',') + p++; + } + num = strtoul(value_ptr, &end_ptr, 10); + if((end_ptr != value_ptr) && (num < ULONG_MAX)) { + if(strcasecompare("ma", option)) + maxage = (time_t)num; + else if(strcasecompare("persist", option) && (num == 1)) + persist = TRUE; + } + } + if(dstalpnid && valid) { + if(!entries++) + /* Flush cached alternatives for this source origin, if any - when + this is the first entry of the line. */ + altsvc_flush(asi, srcalpnid, srchost, srcport); + + as = altsvc_createid(srchost, dsthost, + srcalpnid, dstalpnid, + srcport, dstport); + if(as) { + /* The expires time also needs to take the Age: value (if any) into + account. [See RFC 7838 section 3.1] */ + as->expires = maxage + time(NULL); + as->persist = persist; + Curl_llist_append(&asi->list, as, &as->node); + infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport, + Curl_alpnid2str(dstalpnid)); + } + } + } + else + break; + /* after the double quote there can be a comma if there is another + string or a semicolon if no more */ + if(*p == ',') { + /* comma means another alternative is presented */ + p++; + result = getalnum(&p, alpnbuf, sizeof(alpnbuf)); + if(result) + break; + } + } + else + break; + } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r')); + + return CURLE_OK; +} + +/* + * Return TRUE on a match + */ +bool Curl_altsvc_lookup(struct altsvcinfo *asi, + enum alpnid srcalpnid, const char *srchost, + int srcport, + struct altsvc **dstentry, + const int versions) /* one or more bits */ +{ + struct Curl_llist_element *e; + struct Curl_llist_element *n; + time_t now = time(NULL); + DEBUGASSERT(asi); + DEBUGASSERT(srchost); + DEBUGASSERT(dstentry); + + for(e = asi->list.head; e; e = n) { + struct altsvc *as = e->ptr; + n = e->next; + if(as->expires < now) { + /* an expired entry, remove */ + Curl_llist_remove(&asi->list, e, NULL); + altsvc_free(as); + continue; + } + if((as->src.alpnid == srcalpnid) && + hostcompare(srchost, as->src.host) && + (as->src.port == srcport) && + (versions & (int)as->dst.alpnid)) { + /* match */ + *dstentry = as; + return TRUE; + } + } + return FALSE; +} + +#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */ diff --git a/third_party/curl/lib/altsvc.h b/third_party/curl/lib/altsvc.h new file mode 100644 index 0000000000..58f1905da6 --- /dev/null +++ b/third_party/curl/lib/altsvc.h @@ -0,0 +1,81 @@ +#ifndef HEADER_CURL_ALTSVC_H +#define HEADER_CURL_ALTSVC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC) +#include +#include "llist.h" + +enum alpnid { + ALPN_none = 0, + ALPN_h1 = CURLALTSVC_H1, + ALPN_h2 = CURLALTSVC_H2, + ALPN_h3 = CURLALTSVC_H3 +}; + +struct althost { + char *host; + unsigned short port; + enum alpnid alpnid; +}; + +struct altsvc { + struct althost src; + struct althost dst; + time_t expires; + bool persist; + unsigned int prio; + struct Curl_llist_element node; +}; + +struct altsvcinfo { + char *filename; + struct Curl_llist list; /* list of entries */ + long flags; /* the publicly set bitmask */ +}; + +const char *Curl_alpnid2str(enum alpnid id); +struct altsvcinfo *Curl_altsvc_init(void); +CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file); +CURLcode Curl_altsvc_save(struct Curl_easy *data, + struct altsvcinfo *asi, const char *file); +CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl); +void Curl_altsvc_cleanup(struct altsvcinfo **altsvc); +CURLcode Curl_altsvc_parse(struct Curl_easy *data, + struct altsvcinfo *altsvc, const char *value, + enum alpnid srcalpn, const char *srchost, + unsigned short srcport); +bool Curl_altsvc_lookup(struct altsvcinfo *asi, + enum alpnid srcalpnid, const char *srchost, + int srcport, + struct altsvc **dstentry, + const int versions); /* CURLALTSVC_H* bits */ +#else +/* disabled */ +#define Curl_altsvc_save(a,b,c) +#define Curl_altsvc_cleanup(x) +#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */ +#endif /* HEADER_CURL_ALTSVC_H */ diff --git a/third_party/curl/lib/amigaos.c b/third_party/curl/lib/amigaos.c new file mode 100644 index 0000000000..1321c53c48 --- /dev/null +++ b/third_party/curl/lib/amigaos.c @@ -0,0 +1,247 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef __AMIGA__ + +#include + +#include "hostip.h" +#include "amigaos.h" + +#ifdef HAVE_PROTO_BSDSOCKET_H +# if defined(__amigaos4__) +# include +# elif !defined(USE_AMISSL) +# include +# endif +# ifdef __libnix__ +# include +# endif +#endif + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +#ifdef HAVE_PROTO_BSDSOCKET_H + +#ifdef __amigaos4__ +/* + * AmigaOS 4.x specific code + */ + +/* + * hostip4.c - Curl_ipv4_resolve_r() replacement code + * + * Logic that needs to be considered are the following build cases: + * - newlib networking + * - clib2 networking + * - direct bsdsocket.library networking (usually AmiSSL builds) + * Each with the threaded resolver enabled or not. + * + * With the threaded resolver enabled, try to use gethostbyname_r() where + * available, otherwise (re)open bsdsocket.library and fallback to + * gethostbyname(). + */ + +#include + +static struct SocketIFace *__CurlISocket = NULL; +static uint32 SocketFeatures = 0; + +#define HAVE_BSDSOCKET_GETHOSTBYNAME_R 0x01 +#define HAVE_BSDSOCKET_GETADDRINFO 0x02 + +CURLcode Curl_amiga_init(void) +{ + struct SocketIFace *ISocket; + struct Library *base = OpenLibrary("bsdsocket.library", 4); + + if(base) { + ISocket = (struct SocketIFace *)GetInterface(base, "main", 1, NULL); + if(ISocket) { + ULONG enabled = 0; + + SocketBaseTags(SBTM_SETVAL(SBTC_CAN_SHARE_LIBRARY_BASES), TRUE, + SBTM_GETREF(SBTC_HAVE_GETHOSTADDR_R_API), (ULONG)&enabled, + TAG_DONE); + + if(enabled) { + SocketFeatures |= HAVE_BSDSOCKET_GETHOSTBYNAME_R; + } + + __CurlISocket = ISocket; + + atexit(Curl_amiga_cleanup); + + return CURLE_OK; + } + CloseLibrary(base); + } + + return CURLE_FAILED_INIT; +} + +void Curl_amiga_cleanup(void) +{ + if(__CurlISocket) { + struct Library *base = __CurlISocket->Data.LibBase; + DropInterface((struct Interface *)__CurlISocket); + CloseLibrary(base); + __CurlISocket = NULL; + } +} + +#ifdef CURLRES_AMIGA +/* + * Because we need to handle the different cases in hostip4.c at runtime, + * not at compile-time, based on what was detected in Curl_amiga_init(), + * we replace it completely with our own as to not complicate the baseline + * code. Assumes malloc/calloc/free are thread safe because Curl_he2ai() + * allocates memory also. + */ + +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, + int port) +{ + struct Curl_addrinfo *ai = NULL; + struct hostent *h; + struct SocketIFace *ISocket = __CurlISocket; + + if(SocketFeatures & HAVE_BSDSOCKET_GETHOSTBYNAME_R) { + LONG h_errnop = 0; + struct hostent *buf; + + buf = calloc(1, CURL_HOSTENT_SIZE); + if(buf) { + h = gethostbyname_r((STRPTR)hostname, buf, + (char *)buf + sizeof(struct hostent), + CURL_HOSTENT_SIZE - sizeof(struct hostent), + &h_errnop); + if(h) { + ai = Curl_he2ai(h, port); + } + free(buf); + } + } + else { + #ifdef CURLRES_THREADED + /* gethostbyname() is not thread safe, so we need to reopen bsdsocket + * on the thread's context + */ + struct Library *base = OpenLibrary("bsdsocket.library", 4); + if(base) { + ISocket = (struct SocketIFace *)GetInterface(base, "main", 1, NULL); + if(ISocket) { + h = gethostbyname((STRPTR)hostname); + if(h) { + ai = Curl_he2ai(h, port); + } + DropInterface((struct Interface *)ISocket); + } + CloseLibrary(base); + } + #else + /* not using threaded resolver - safe to use this as-is */ + h = gethostbyname(hostname); + if(h) { + ai = Curl_he2ai(h, port); + } + #endif + } + + return ai; +} +#endif /* CURLRES_AMIGA */ + +#ifdef USE_AMISSL +#include +int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *errorfds, struct timeval *timeout) +{ + int r = WaitSelect(nfds, readfds, writefds, errorfds, timeout, 0); + /* Ensure Ctrl-C signal is actioned */ + if((r == -1) && (SOCKERRNO == EINTR)) + raise(SIGINT); + return r; +} +#endif /* USE_AMISSL */ + +#elif !defined(USE_AMISSL) /* __amigaos4__ */ +/* + * Amiga OS3 specific code + */ + +struct Library *SocketBase = NULL; +extern int errno, h_errno; + +#ifdef __libnix__ +void __request(const char *msg); +#else +# define __request(msg) Printf(msg "\n\a") +#endif + +void Curl_amiga_cleanup(void) +{ + if(SocketBase) { + CloseLibrary(SocketBase); + SocketBase = NULL; + } +} + +CURLcode Curl_amiga_init(void) +{ + if(!SocketBase) + SocketBase = OpenLibrary("bsdsocket.library", 4); + + if(!SocketBase) { + __request("No TCP/IP Stack running!"); + return CURLE_FAILED_INIT; + } + + if(SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG) &errno, + SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG) "curl", + TAG_DONE)) { + __request("SocketBaseTags ERROR"); + return CURLE_FAILED_INIT; + } + +#ifndef __libnix__ + atexit(Curl_amiga_cleanup); +#endif + + return CURLE_OK; +} + +#ifdef __libnix__ +ADD2EXIT(Curl_amiga_cleanup, -50); +#endif + +#endif /* !USE_AMISSL */ + +#endif /* HAVE_PROTO_BSDSOCKET_H */ + +#endif /* __AMIGA__ */ diff --git a/third_party/curl/lib/amigaos.h b/third_party/curl/lib/amigaos.h new file mode 100644 index 0000000000..c99d963ece --- /dev/null +++ b/third_party/curl/lib/amigaos.h @@ -0,0 +1,41 @@ +#ifndef HEADER_CURL_AMIGAOS_H +#define HEADER_CURL_AMIGAOS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if defined(__AMIGA__) && defined(HAVE_PROTO_BSDSOCKET_H) && \ + (!defined(USE_AMISSL) || defined(__amigaos4__)) + +CURLcode Curl_amiga_init(void); +void Curl_amiga_cleanup(void); + +#else + +#define Curl_amiga_init() CURLE_OK +#define Curl_amiga_cleanup() Curl_nop_stmt + +#endif + +#endif /* HEADER_CURL_AMIGAOS_H */ diff --git a/third_party/curl/lib/arpa_telnet.h b/third_party/curl/lib/arpa_telnet.h new file mode 100644 index 0000000000..d641a01da8 --- /dev/null +++ b/third_party/curl/lib/arpa_telnet.h @@ -0,0 +1,117 @@ +#ifndef HEADER_CURL_ARPA_TELNET_H +#define HEADER_CURL_ARPA_TELNET_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#ifndef CURL_DISABLE_TELNET +/* + * Telnet option defines. Add more here if in need. + */ +#define CURL_TELOPT_BINARY 0 /* binary 8bit data */ +#define CURL_TELOPT_ECHO 1 /* just echo! */ +#define CURL_TELOPT_SGA 3 /* Suppress Go Ahead */ +#define CURL_TELOPT_EXOPL 255 /* EXtended OPtions List */ +#define CURL_TELOPT_TTYPE 24 /* Terminal TYPE */ +#define CURL_TELOPT_NAWS 31 /* Negotiate About Window Size */ +#define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */ + +#define CURL_TELOPT_NEW_ENVIRON 39 /* NEW ENVIRONment variables */ +#define CURL_NEW_ENV_VAR 0 +#define CURL_NEW_ENV_VALUE 1 + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +/* + * The telnet options represented as strings + */ +static const char * const telnetoptions[]= +{ + "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", + "NAME", "STATUS", "TIMING MARK", "RCTE", + "NAOL", "NAOP", "NAOCRD", "NAOHTS", + "NAOHTD", "NAOFFD", "NAOVTS", "NAOVTD", + "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO", + "DE TERMINAL", "SUPDUP", "SUPDUP OUTPUT", "SEND LOCATION", + "TERM TYPE", "END OF RECORD", "TACACS UID", "OUTPUT MARKING", + "TTYLOC", "3270 REGIME", "X3 PAD", "NAWS", + "TERM SPEED", "LFLOW", "LINEMODE", "XDISPLOC", + "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON" +}; +#define CURL_TELOPT(x) telnetoptions[x] +#else +#define CURL_TELOPT(x) "" +#endif + +#define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON + +#define CURL_TELOPT_OK(x) ((x) <= CURL_TELOPT_MAXIMUM) + +#define CURL_NTELOPTS 40 + +/* + * First some defines + */ +#define CURL_xEOF 236 /* End Of File */ +#define CURL_SE 240 /* Sub negotiation End */ +#define CURL_NOP 241 /* No OPeration */ +#define CURL_DM 242 /* Data Mark */ +#define CURL_GA 249 /* Go Ahead, reverse the line */ +#define CURL_SB 250 /* SuBnegotiation */ +#define CURL_WILL 251 /* Our side WILL use this option */ +#define CURL_WONT 252 /* Our side will not use this option */ +#define CURL_DO 253 /* DO use this option! */ +#define CURL_DONT 254 /* DON'T use this option! */ +#define CURL_IAC 255 /* Interpret As Command */ + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +/* + * Then those numbers represented as strings: + */ +static const char * const telnetcmds[]= +{ + "EOF", "SUSP", "ABORT", "EOR", "SE", + "NOP", "DMARK", "BRK", "IP", "AO", + "AYT", "EC", "EL", "GA", "SB", + "WILL", "WONT", "DO", "DONT", "IAC" +}; +#endif + +#define CURL_TELCMD_MINIMUM CURL_xEOF /* the first one */ +#define CURL_TELCMD_MAXIMUM CURL_IAC /* surprise, 255 is the last one! ;-) */ + +#define CURL_TELQUAL_IS 0 +#define CURL_TELQUAL_SEND 1 +#define CURL_TELQUAL_INFO 2 +#define CURL_TELQUAL_NAME 3 + +#define CURL_TELCMD_OK(x) ( ((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \ + ((unsigned int)(x) <= CURL_TELCMD_MAXIMUM) ) + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +#define CURL_TELCMD(x) telnetcmds[(x)-CURL_TELCMD_MINIMUM] +#else +#define CURL_TELCMD(x) "" +#endif + +#endif /* CURL_DISABLE_TELNET */ + +#endif /* HEADER_CURL_ARPA_TELNET_H */ diff --git a/third_party/curl/lib/asyn-ares.c b/third_party/curl/lib/asyn-ares.c new file mode 100644 index 0000000000..782e3ac659 --- /dev/null +++ b/third_party/curl/lib/asyn-ares.c @@ -0,0 +1,958 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +/*********************************************************************** + * Only for ares-enabled builds + * And only for functions that fulfill the asynch resolver backend API + * as defined in asyn.h, nothing else belongs in this file! + **********************************************************************/ + +#ifdef CURLRES_ARES + +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "hash.h" +#include "share.h" +#include "url.h" +#include "multiif.h" +#include "inet_pton.h" +#include "connect.h" +#include "select.h" +#include "progress.h" +#include "timediff.h" + +#if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ + defined(_WIN32) +# define CARES_STATICLIB +#endif +#include +#include /* really old c-ares did not include this by + itself */ + +#if ARES_VERSION >= 0x010500 +/* c-ares 1.5.0 or later, the callback proto is modified */ +#define HAVE_CARES_CALLBACK_TIMEOUTS 1 +#endif + +#if ARES_VERSION >= 0x010601 +/* IPv6 supported since 1.6.1 */ +#define HAVE_CARES_IPV6 1 +#endif + +#if ARES_VERSION >= 0x010704 +#define HAVE_CARES_SERVERS_CSV 1 +#define HAVE_CARES_LOCAL_DEV 1 +#define HAVE_CARES_SET_LOCAL 1 +#endif + +#if ARES_VERSION >= 0x010b00 +#define HAVE_CARES_PORTS_CSV 1 +#endif + +#if ARES_VERSION >= 0x011000 +/* 1.16.0 or later has ares_getaddrinfo */ +#define HAVE_CARES_GETADDRINFO 1 +#endif + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +struct thread_data { + int num_pending; /* number of outstanding c-ares requests */ + struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares + parts */ + int last_status; +#ifndef HAVE_CARES_GETADDRINFO + struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ +#endif + char hostname[1]; +}; + +/* How long we are willing to wait for additional parallel responses after + obtaining a "definitive" one. For old c-ares without getaddrinfo. + + This is intended to equal the c-ares default timeout. cURL always uses that + default value. Unfortunately, c-ares does not expose its default timeout in + its API, but it is officially documented as 5 seconds. + + See query_completed_cb() for an explanation of how this is used. + */ +#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000 + +#define CARES_TIMEOUT_PER_ATTEMPT 2000 + +static int ares_ver = 0; + +/* + * Curl_resolver_global_init() - the generic low-level asynchronous name + * resolve API. Called from curl_global_init() to initialize global resolver + * environment. Initializes ares library. + */ +int Curl_resolver_global_init(void) +{ +#ifdef CARES_HAVE_ARES_LIBRARY_INIT + if(ares_library_init(ARES_LIB_INIT_ALL)) { + return CURLE_FAILED_INIT; + } +#endif + ares_version(&ares_ver); + return CURLE_OK; +} + +/* + * Curl_resolver_global_cleanup() + * + * Called from curl_global_cleanup() to destroy global resolver environment. + * Deinitializes ares library. + */ +void Curl_resolver_global_cleanup(void) +{ +#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP + ares_library_cleanup(); +#endif +} + + +static void sock_state_cb(void *data, ares_socket_t socket_fd, + int readable, int writable) +{ + struct Curl_easy *easy = data; + if(!readable && !writable) { + DEBUGASSERT(easy); + Curl_multi_closed(easy, socket_fd); + } +} + +/* + * Curl_resolver_init() + * + * Called from curl_easy_init() -> Curl_open() to initialize resolver + * URL-state specific environment ('resolver' member of the UrlState + * structure). Fills the passed pointer by the initialized ares_channel. + */ +CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) +{ + int status; + struct ares_options options; + int optmask = ARES_OPT_SOCK_STATE_CB; + options.sock_state_cb = sock_state_cb; + options.sock_state_cb_data = easy; + + /* + if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s) + + if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need + to set the timeout value; + + if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to + overwrite c-ares' timeout. + */ + DEBUGASSERT(ares_ver); + if(ares_ver < 0x011400) { + options.timeout = CARES_TIMEOUT_PER_ATTEMPT; + optmask |= ARES_OPT_TIMEOUTMS; + } + + status = ares_init_options((ares_channel*)resolver, &options, optmask); + if(status != ARES_SUCCESS) { + if(status == ARES_ENOMEM) + return CURLE_OUT_OF_MEMORY; + else + return CURLE_FAILED_INIT; + } + return CURLE_OK; + /* make sure that all other returns from this function should destroy the + ares channel before returning error! */ +} + +/* + * Curl_resolver_cleanup() + * + * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver + * URL-state specific environment ('resolver' member of the UrlState + * structure). Destroys the ares channel. + */ +void Curl_resolver_cleanup(void *resolver) +{ + ares_destroy((ares_channel)resolver); +} + +/* + * Curl_resolver_duphandle() + * + * Called from curl_easy_duphandle() to duplicate resolver URL-state specific + * environment ('resolver' member of the UrlState structure). Duplicates the + * 'from' ares channel and passes the resulting channel to the 'to' pointer. + */ +CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from) +{ + (void)from; + /* + * it would be better to call ares_dup instead, but right now + * it is not possible to set 'sock_state_cb_data' outside of + * ares_init_options + */ + return Curl_resolver_init(easy, to); +} + +static void destroy_async_data(struct Curl_async *async); + +/* + * Cancel all possibly still on-going resolves for this connection. + */ +void Curl_resolver_cancel(struct Curl_easy *data) +{ + DEBUGASSERT(data); + if(data->state.async.resolver) + ares_cancel((ares_channel)data->state.async.resolver); + destroy_async_data(&data->state.async); +} + +/* + * We are equivalent to Curl_resolver_cancel() for the c-ares resolver. We + * never block. + */ +void Curl_resolver_kill(struct Curl_easy *data) +{ + /* We do not need to check the resolver state because we can be called safely + at any time and we always do the same thing. */ + Curl_resolver_cancel(data); +} + +/* + * destroy_async_data() cleans up async resolver data. + */ +static void destroy_async_data(struct Curl_async *async) +{ + if(async->tdata) { + struct thread_data *res = async->tdata; + if(res) { + if(res->temp_ai) { + Curl_freeaddrinfo(res->temp_ai); + res->temp_ai = NULL; + } + free(res); + } + async->tdata = NULL; + } +} + +/* + * Curl_resolver_getsock() is called when someone from the outside world + * (using curl_multi_fdset()) wants to get our fd_set setup and we are talking + * with ares. The caller must make sure that this function is only called when + * we have a working ares channel. + * + * Returns: sockets-in-use-bitmap + */ + +int Curl_resolver_getsock(struct Curl_easy *data, + curl_socket_t *socks) +{ + struct timeval maxtime; + struct timeval timebuf; + struct timeval *timeout; + long milli; + int max = ares_getsock((ares_channel)data->state.async.resolver, + (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE); + + maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; + maxtime.tv_usec = 0; + + timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime, + &timebuf); + milli = (long)curlx_tvtoms(timeout); + if(milli == 0) + milli += 10; + Curl_expire(data, milli, EXPIRE_ASYNC_NAME); + + return max; +} + +/* + * waitperform() + * + * 1) Ask ares what sockets it currently plays with, then + * 2) wait for the timeout period to check for action on ares' sockets. + * 3) tell ares to act on all the sockets marked as "with action" + * + * return number of sockets it worked on, or -1 on error + */ + +static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) +{ + int nfds; + int bitmask; + ares_socket_t socks[ARES_GETSOCK_MAXNUM]; + struct pollfd pfd[ARES_GETSOCK_MAXNUM]; + int i; + int num = 0; + + bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks, + ARES_GETSOCK_MAXNUM); + + for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) { + pfd[i].events = 0; + pfd[i].revents = 0; + if(ARES_GETSOCK_READABLE(bitmask, i)) { + pfd[i].fd = socks[i]; + pfd[i].events |= POLLRDNORM|POLLIN; + } + if(ARES_GETSOCK_WRITABLE(bitmask, i)) { + pfd[i].fd = socks[i]; + pfd[i].events |= POLLWRNORM|POLLOUT; + } + if(pfd[i].events) + num++; + else + break; + } + + if(num) { + nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms); + if(nfds < 0) + return -1; + } + else + nfds = 0; + + if(!nfds) + /* Call ares_process() unconditionally here, even if we simply timed out + above, as otherwise the ares name resolve will not timeout! */ + ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD, + ARES_SOCKET_BAD); + else { + /* move through the descriptors and ask for processing on them */ + for(i = 0; i < num; i++) + ares_process_fd((ares_channel)data->state.async.resolver, + (pfd[i].revents & (POLLRDNORM|POLLIN))? + pfd[i].fd:ARES_SOCKET_BAD, + (pfd[i].revents & (POLLWRNORM|POLLOUT))? + pfd[i].fd:ARES_SOCKET_BAD); + } + return nfds; +} + +/* + * Curl_resolver_is_resolved() is called repeatedly to check if a previous + * name resolve request has completed. It should also make sure to time-out if + * the operation seems to take too long. + * + * Returns normal CURLcode errors. + */ +CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, + struct Curl_dns_entry **dns) +{ + struct thread_data *res = data->state.async.tdata; + CURLcode result = CURLE_OK; + + DEBUGASSERT(dns); + *dns = NULL; + + if(waitperform(data, 0) < 0) + return CURLE_UNRECOVERABLE_POLL; + +#ifndef HAVE_CARES_GETADDRINFO + /* Now that we have checked for any last minute results above, see if there + are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer + expires. */ + if(res + && res->num_pending + /* This is only set to non-zero if the timer was started. */ + && (res->happy_eyeballs_dns_time.tv_sec + || res->happy_eyeballs_dns_time.tv_usec) + && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time) + >= HAPPY_EYEBALLS_DNS_TIMEOUT)) { + /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer + running. */ + memset( + &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time)); + + /* Cancel the raw c-ares request, which will fire query_completed_cb() with + ARES_ECANCELLED synchronously for all pending responses. This will + leave us with res->num_pending == 0, which is perfect for the next + block. */ + ares_cancel((ares_channel)data->state.async.resolver); + DEBUGASSERT(res->num_pending == 0); + } +#endif + + if(res && !res->num_pending) { + (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai); + /* temp_ai ownership is moved to the connection, so we need not free-up + them */ + res->temp_ai = NULL; + + if(!data->state.async.dns) + result = Curl_resolver_error(data); + else + *dns = data->state.async.dns; + + destroy_async_data(&data->state.async); + } + + return result; +} + +/* + * Curl_resolver_wait_resolv() + * + * Waits for a resolve to finish. This function should be avoided since using + * this risk getting the multi interface to "hang". + * + * 'entry' MUST be non-NULL. + * + * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, + * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. + */ +CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, + struct Curl_dns_entry **entry) +{ + CURLcode result = CURLE_OK; + timediff_t timeout; + struct curltime now = Curl_now(); + + DEBUGASSERT(entry); + *entry = NULL; /* clear on entry */ + + timeout = Curl_timeleft(data, &now, TRUE); + if(timeout < 0) { + /* already expired! */ + connclose(data->conn, "Timed out before name resolve started"); + return CURLE_OPERATION_TIMEDOUT; + } + if(!timeout) + timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ + + /* Wait for the name resolve query to complete. */ + while(!result) { + struct timeval *tvp, tv, store; + int itimeout; + timediff_t timeout_ms; + +#if TIMEDIFF_T_MAX > INT_MAX + itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout; +#else + itimeout = (int)timeout; +#endif + + store.tv_sec = itimeout/1000; + store.tv_usec = (itimeout%1000)*1000; + + tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv); + + /* use the timeout period ares returned to us above if less than one + second is left, otherwise just use 1000ms to make sure the progress + callback gets called frequent enough */ + if(!tvp->tv_sec) + timeout_ms = (timediff_t)(tvp->tv_usec/1000); + else + timeout_ms = 1000; + + if(waitperform(data, timeout_ms) < 0) + return CURLE_UNRECOVERABLE_POLL; + result = Curl_resolver_is_resolved(data, entry); + + if(result || data->state.async.done) + break; + + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + else { + struct curltime now2 = Curl_now(); + timediff_t timediff = Curl_timediff(now2, now); /* spent time */ + if(timediff <= 0) + timeout -= 1; /* always deduct at least 1 */ + else if(timediff > timeout) + timeout = -1; + else + timeout -= timediff; + now = now2; /* for next loop */ + } + if(timeout < 0) + result = CURLE_OPERATION_TIMEDOUT; + } + if(result) + /* failure, so we cancel the ares operation */ + ares_cancel((ares_channel)data->state.async.resolver); + + /* Operation complete, if the lookup was successful we now have the entry + in the cache. */ + if(entry) + *entry = data->state.async.dns; + + if(result) + /* close the connection, since we cannot return failure here without + cleaning up this connection properly. */ + connclose(data->conn, "c-ares resolve failed"); + + return result; +} + +#ifndef HAVE_CARES_GETADDRINFO + +/* Connects results to the list */ +static void compound_results(struct thread_data *res, + struct Curl_addrinfo *ai) +{ + if(!ai) + return; + +#ifdef USE_IPV6 /* CURLRES_IPV6 */ + if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) { + /* We have results already, put the new IPv6 entries at the head of the + list. */ + struct Curl_addrinfo *temp_ai_tail = res->temp_ai; + + while(temp_ai_tail->ai_next) + temp_ai_tail = temp_ai_tail->ai_next; + + temp_ai_tail->ai_next = ai; + } + else +#endif /* CURLRES_IPV6 */ + { + /* Add the new results to the list of old results. */ + struct Curl_addrinfo *ai_tail = ai; + while(ai_tail->ai_next) + ai_tail = ai_tail->ai_next; + + ai_tail->ai_next = res->temp_ai; + res->temp_ai = ai; + } +} + +/* + * ares_query_completed_cb() is the callback that ares will call when + * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(), + * when using ares, is completed either successfully or with failure. + */ +static void query_completed_cb(void *arg, /* (struct connectdata *) */ + int status, +#ifdef HAVE_CARES_CALLBACK_TIMEOUTS + int timeouts, +#endif + struct hostent *hostent) +{ + struct Curl_easy *data = (struct Curl_easy *)arg; + struct thread_data *res; + +#ifdef HAVE_CARES_CALLBACK_TIMEOUTS + (void)timeouts; /* ignored */ +#endif + + if(ARES_EDESTRUCTION == status) + /* when this ares handle is getting destroyed, the 'arg' pointer may not + be valid so only defer it when we know the 'status' says its fine! */ + return; + + res = data->state.async.tdata; + if(res) { + res->num_pending--; + + if(CURL_ASYNC_SUCCESS == status) { + struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port); + if(ai) { + compound_results(res, ai); + } + } + /* A successful result overwrites any previous error */ + if(res->last_status != ARES_SUCCESS) + res->last_status = status; + + /* If there are responses still pending, we presume they must be the + complementary IPv4 or IPv6 lookups that we started in parallel in + Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we have got a + "definitive" response from one of a set of parallel queries, we need to + think about how long we are willing to wait for more responses. */ + if(res->num_pending + /* Only these c-ares status values count as "definitive" for these + purposes. For example, ARES_ENODATA is what we expect when there is + no IPv6 entry for a domain name, and that is not a reason to get more + aggressive in our timeouts for the other response. Other errors are + either a result of bad input (which should affect all parallel + requests), local or network conditions, non-definitive server + responses, or us cancelling the request. */ + && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) { + /* Right now, there can only be up to two parallel queries, so do not + bother handling any other cases. */ + DEBUGASSERT(res->num_pending == 1); + + /* it is possible that one of these parallel queries could succeed + quickly, but the other could always fail or timeout (when we are + talking to a pool of DNS servers that can only successfully resolve + IPv4 address, for example). + + it is also possible that the other request could always just take + longer because it needs more time or only the second DNS server can + fulfill it successfully. But, to align with the philosophy of Happy + Eyeballs, we do not want to wait _too_ long or users will think + requests are slow when IPv6 lookups do not actually work (but IPv4 + ones do). + + So, now that we have a usable answer (some IPv4 addresses, some IPv6 + addresses, or "no such domain"), we start a timeout for the remaining + pending responses. Even though it is typical that this resolved + request came back quickly, that needn't be the case. It might be that + this completing request did not get a result from the first DNS + server or even the first round of the whole DNS server pool. So it + could already be quite some time after we issued the DNS queries in + the first place. Without modifying c-ares, we cannot know exactly + where in its retry cycle we are. We could guess based on how much + time has gone by, but it does not really matter. Happy Eyeballs tells + us that, given usable information in hand, we simply do not want to + wait "too much longer" after we get a result. + + We simply wait an additional amount of time equal to the default + c-ares query timeout. That is enough time for a typical parallel + response to arrive without being "too long". Even on a network + where one of the two types of queries is failing or timing out + constantly, this will usually mean we wait a total of the default + c-ares timeout (5 seconds) plus the round trip time for the successful + request, which seems bearable. The downside is that c-ares might race + with us to issue one more retry just before we give up, but it seems + better to "waste" that request instead of trying to guess the perfect + timeout to prevent it. After all, we do not even know where in the + c-ares retry cycle each request is. + */ + res->happy_eyeballs_dns_time = Curl_now(); + Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT, + EXPIRE_HAPPY_EYEBALLS_DNS); + } + } +} +#else +/* c-ares 1.16.0 or later */ + +/* + * ares2addr() converts an address list provided by c-ares to an internal + * libcurl compatible list + */ +static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node) +{ + /* traverse the ares_addrinfo_node list */ + struct ares_addrinfo_node *ai; + struct Curl_addrinfo *cafirst = NULL; + struct Curl_addrinfo *calast = NULL; + int error = 0; + + for(ai = node; ai != NULL; ai = ai->ai_next) { + size_t ss_size; + struct Curl_addrinfo *ca; + /* ignore elements with unsupported address family, */ + /* settle family-specific sockaddr structure size. */ + if(ai->ai_family == AF_INET) + ss_size = sizeof(struct sockaddr_in); +#ifdef USE_IPV6 + else if(ai->ai_family == AF_INET6) + ss_size = sizeof(struct sockaddr_in6); +#endif + else + continue; + + /* ignore elements without required address info */ + if(!ai->ai_addr || !(ai->ai_addrlen > 0)) + continue; + + /* ignore elements with bogus address size */ + if((size_t)ai->ai_addrlen < ss_size) + continue; + + ca = malloc(sizeof(struct Curl_addrinfo) + ss_size); + if(!ca) { + error = EAI_MEMORY; + break; + } + + /* copy each structure member individually, member ordering, */ + /* size, or padding might be different for each platform. */ + + ca->ai_flags = ai->ai_flags; + ca->ai_family = ai->ai_family; + ca->ai_socktype = ai->ai_socktype; + ca->ai_protocol = ai->ai_protocol; + ca->ai_addrlen = (curl_socklen_t)ss_size; + ca->ai_addr = NULL; + ca->ai_canonname = NULL; + ca->ai_next = NULL; + + ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); + memcpy(ca->ai_addr, ai->ai_addr, ss_size); + + /* if the return list is empty, this becomes the first element */ + if(!cafirst) + cafirst = ca; + + /* add this element last in the return list */ + if(calast) + calast->ai_next = ca; + calast = ca; + } + + /* if we failed, destroy the Curl_addrinfo list */ + if(error) { + Curl_freeaddrinfo(cafirst); + cafirst = NULL; + } + + return cafirst; +} + +static void addrinfo_cb(void *arg, int status, int timeouts, + struct ares_addrinfo *result) +{ + struct Curl_easy *data = (struct Curl_easy *)arg; + struct thread_data *res = data->state.async.tdata; + (void)timeouts; + if(ARES_SUCCESS == status) { + res->temp_ai = ares2addr(result->nodes); + res->last_status = CURL_ASYNC_SUCCESS; + ares_freeaddrinfo(result); + } + res->num_pending--; +} + +#endif +/* + * Curl_resolver_getaddrinfo() - when using ares + * + * Returns name information about the given hostname and port number. If + * successful, the 'hostent' is returned and the fourth argument will point to + * memory we need to free after use. That memory *MUST* be freed with + * Curl_freeaddrinfo(), nothing else. + */ +struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, + const char *hostname, + int port, + int *waitp) +{ + struct thread_data *res = NULL; + size_t namelen = strlen(hostname); + *waitp = 0; /* default to synchronous response */ + + res = calloc(1, sizeof(struct thread_data) + namelen); + if(res) { + strcpy(res->hostname, hostname); + data->state.async.hostname = res->hostname; + data->state.async.port = port; + data->state.async.done = FALSE; /* not done */ + data->state.async.status = 0; /* clear */ + data->state.async.dns = NULL; /* clear */ + data->state.async.tdata = res; + + /* initial status - failed */ + res->last_status = ARES_ENOTFOUND; + +#ifdef HAVE_CARES_GETADDRINFO + { + struct ares_addrinfo_hints hints; + char service[12]; + int pf = PF_INET; + memset(&hints, 0, sizeof(hints)); +#ifdef CURLRES_IPV6 + if((data->conn->ip_version != CURL_IPRESOLVE_V4) && + Curl_ipv6works(data)) { + /* The stack seems to be IPv6-enabled */ + if(data->conn->ip_version == CURL_IPRESOLVE_V6) + pf = PF_INET6; + else + pf = PF_UNSPEC; + } +#endif /* CURLRES_IPV6 */ + hints.ai_family = pf; + hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)? + SOCK_STREAM : SOCK_DGRAM; + /* Since the service is a numerical one, set the hint flags + * accordingly to save a call to getservbyname in inside C-Ares + */ + hints.ai_flags = ARES_AI_NUMERICSERV; + msnprintf(service, sizeof(service), "%d", port); + res->num_pending = 1; + ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname, + service, &hints, addrinfo_cb, data); + } +#else + +#ifdef HAVE_CARES_IPV6 + if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { + /* The stack seems to be IPv6-enabled */ + res->num_pending = 2; + + /* areschannel is already setup in the Curl_open() function */ + ares_gethostbyname((ares_channel)data->state.async.resolver, hostname, + PF_INET, query_completed_cb, data); + ares_gethostbyname((ares_channel)data->state.async.resolver, hostname, + PF_INET6, query_completed_cb, data); + } + else +#endif + { + res->num_pending = 1; + + /* areschannel is already setup in the Curl_open() function */ + ares_gethostbyname((ares_channel)data->state.async.resolver, + hostname, PF_INET, + query_completed_cb, data); + } +#endif + *waitp = 1; /* expect asynchronous response */ + } + return NULL; /* no struct yet */ +} + +CURLcode Curl_set_dns_servers(struct Curl_easy *data, + char *servers) +{ + CURLcode result = CURLE_NOT_BUILT_IN; + int ares_result; + + /* If server is NULL or empty, this would purge all DNS servers + * from ares library, which will cause any and all queries to fail. + * So, just return OK if none are configured and do not actually make + * any changes to c-ares. This lets c-ares use its defaults, which + * it gets from the OS (for instance from /etc/resolv.conf on Linux). + */ + if(!(servers && servers[0])) + return CURLE_OK; + +#ifdef HAVE_CARES_SERVERS_CSV +#ifdef HAVE_CARES_PORTS_CSV + ares_result = ares_set_servers_ports_csv(data->state.async.resolver, + servers); +#else + ares_result = ares_set_servers_csv(data->state.async.resolver, servers); +#endif + switch(ares_result) { + case ARES_SUCCESS: + result = CURLE_OK; + break; + case ARES_ENOMEM: + result = CURLE_OUT_OF_MEMORY; + break; + case ARES_ENOTINITIALIZED: + case ARES_ENODATA: + case ARES_EBADSTR: + default: + DEBUGF(infof(data, "bad servers set")); + result = CURLE_BAD_FUNCTION_ARGUMENT; + break; + } +#else /* too old c-ares version! */ + (void)data; + (void)(ares_result); +#endif + return result; +} + +CURLcode Curl_set_dns_interface(struct Curl_easy *data, + const char *interf) +{ +#ifdef HAVE_CARES_LOCAL_DEV + if(!interf) + interf = ""; + + ares_set_local_dev((ares_channel)data->state.async.resolver, interf); + + return CURLE_OK; +#else /* c-ares version too old! */ + (void)data; + (void)interf; + return CURLE_NOT_BUILT_IN; +#endif +} + +CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, + const char *local_ip4) +{ +#ifdef HAVE_CARES_SET_LOCAL + struct in_addr a4; + + if((!local_ip4) || (local_ip4[0] == 0)) { + a4.s_addr = 0; /* disabled: do not bind to a specific address */ + } + else { + if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) { + DEBUGF(infof(data, "bad DNS IPv4 address")); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + } + + ares_set_local_ip4((ares_channel)data->state.async.resolver, + ntohl(a4.s_addr)); + + return CURLE_OK; +#else /* c-ares version too old! */ + (void)data; + (void)local_ip4; + return CURLE_NOT_BUILT_IN; +#endif +} + +CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, + const char *local_ip6) +{ +#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6) + unsigned char a6[INET6_ADDRSTRLEN]; + + if((!local_ip6) || (local_ip6[0] == 0)) { + /* disabled: do not bind to a specific address */ + memset(a6, 0, sizeof(a6)); + } + else { + if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) { + DEBUGF(infof(data, "bad DNS IPv6 address")); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + } + + ares_set_local_ip6((ares_channel)data->state.async.resolver, a6); + + return CURLE_OK; +#else /* c-ares version too old! */ + (void)data; + (void)local_ip6; + return CURLE_NOT_BUILT_IN; +#endif +} +#endif /* CURLRES_ARES */ diff --git a/third_party/curl/lib/asyn-thread.c b/third_party/curl/lib/asyn-thread.c new file mode 100644 index 0000000000..d91152fdfa --- /dev/null +++ b/third_party/curl/lib/asyn-thread.c @@ -0,0 +1,1031 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "socketpair.h" + +/*********************************************************************** + * Only for threaded name resolves builds + **********************************************************************/ +#ifdef CURLRES_THREADED + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) +# include +#endif + +#ifdef HAVE_GETADDRINFO +# define RESOLVER_ENOMEM EAI_MEMORY +#else +# define RESOLVER_ENOMEM ENOMEM +#endif + +#include "system_win32.h" +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "hash.h" +#include "share.h" +#include "url.h" +#include "multiif.h" +#include "inet_ntop.h" +#include "curl_threads.h" +#include "connect.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +struct resdata { + struct curltime start; +}; + +/* + * Curl_resolver_global_init() + * Called from curl_global_init() to initialize global resolver environment. + * Does nothing here. + */ +int Curl_resolver_global_init(void) +{ + return CURLE_OK; +} + +/* + * Curl_resolver_global_cleanup() + * Called from curl_global_cleanup() to destroy global resolver environment. + * Does nothing here. + */ +void Curl_resolver_global_cleanup(void) +{ +} + +/* + * Curl_resolver_init() + * Called from curl_easy_init() -> Curl_open() to initialize resolver + * URL-state specific environment ('resolver' member of the UrlState + * structure). + */ +CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) +{ + (void)easy; + *resolver = calloc(1, sizeof(struct resdata)); + if(!*resolver) + return CURLE_OUT_OF_MEMORY; + return CURLE_OK; +} + +/* + * Curl_resolver_cleanup() + * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver + * URL-state specific environment ('resolver' member of the UrlState + * structure). + */ +void Curl_resolver_cleanup(void *resolver) +{ + free(resolver); +} + +/* + * Curl_resolver_duphandle() + * Called from curl_easy_duphandle() to duplicate resolver URL state-specific + * environment ('resolver' member of the UrlState structure). + */ +CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from) +{ + (void)from; + return Curl_resolver_init(easy, to); +} + +static void destroy_async_data(struct Curl_async *); + +/* + * Cancel all possibly still on-going resolves for this connection. + */ +void Curl_resolver_cancel(struct Curl_easy *data) +{ + destroy_async_data(&data->state.async); +} + +/* This function is used to init a threaded resolve */ +static bool init_resolve_thread(struct Curl_easy *data, + const char *hostname, int port, + const struct addrinfo *hints); + +#ifdef _WIN32 +/* Thread sync data used by GetAddrInfoExW for win8+ */ +struct thread_sync_data_w8 +{ + OVERLAPPED overlapped; + ADDRINFOEXW_ *res; + HANDLE cancel_ev; + ADDRINFOEXW_ hints; +}; +#endif + +/* Data for synchronization between resolver thread and its parent */ +struct thread_sync_data { +#ifdef _WIN32 + struct thread_sync_data_w8 w8; +#endif + curl_mutex_t *mtx; + int done; + int port; + char *hostname; /* hostname to resolve, Curl_async.hostname + duplicate */ +#ifndef CURL_DISABLE_SOCKETPAIR + struct Curl_easy *data; + curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */ +#endif + int sock_error; + struct Curl_addrinfo *res; +#ifdef HAVE_GETADDRINFO + struct addrinfo hints; +#endif + struct thread_data *td; /* for thread-self cleanup */ +}; + +struct thread_data { +#ifdef _WIN32 + HANDLE complete_ev; +#endif + curl_thread_t thread_hnd; + unsigned int poll_interval; + timediff_t interval_end; + struct thread_sync_data tsd; +}; + +static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data) +{ + return &(data->state.async.tdata->tsd); +} + +/* Destroy resolver thread synchronization data */ +static +void destroy_thread_sync_data(struct thread_sync_data *tsd) +{ + if(tsd->mtx) { + Curl_mutex_destroy(tsd->mtx); + free(tsd->mtx); + } + + free(tsd->hostname); + + if(tsd->res) + Curl_freeaddrinfo(tsd->res); + +#ifndef CURL_DISABLE_SOCKETPAIR + /* + * close one end of the socket pair (may be done in resolver thread); + * the other end (for reading) is always closed in the parent thread. + */ + if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { + wakeup_close(tsd->sock_pair[1]); + } +#endif + memset(tsd, 0, sizeof(*tsd)); +} + +/* Initialize resolver thread synchronization data */ +static +int init_thread_sync_data(struct thread_data *td, + const char *hostname, + int port, + const struct addrinfo *hints) +{ + struct thread_sync_data *tsd = &td->tsd; + + memset(tsd, 0, sizeof(*tsd)); + + tsd->td = td; + tsd->port = port; + /* Treat the request as done until the thread actually starts so any early + * cleanup gets done properly. + */ + tsd->done = 1; +#ifdef HAVE_GETADDRINFO + DEBUGASSERT(hints); + tsd->hints = *hints; +#else + (void) hints; +#endif + + tsd->mtx = malloc(sizeof(curl_mutex_t)); + if(!tsd->mtx) + goto err_exit; + + Curl_mutex_init(tsd->mtx); + +#ifndef CURL_DISABLE_SOCKETPAIR + /* create socket pair or pipe */ + if(wakeup_create(tsd->sock_pair, FALSE) < 0) { + tsd->sock_pair[0] = CURL_SOCKET_BAD; + tsd->sock_pair[1] = CURL_SOCKET_BAD; + goto err_exit; + } +#endif + tsd->sock_error = CURL_ASYNC_SUCCESS; + + /* Copying hostname string because original can be destroyed by parent + * thread during gethostbyname execution. + */ + tsd->hostname = strdup(hostname); + if(!tsd->hostname) + goto err_exit; + + return 1; + +err_exit: +#ifndef CURL_DISABLE_SOCKETPAIR + if(tsd->sock_pair[0] != CURL_SOCKET_BAD) { + wakeup_close(tsd->sock_pair[0]); + tsd->sock_pair[0] = CURL_SOCKET_BAD; + } +#endif + destroy_thread_sync_data(tsd); + return 0; +} + +static CURLcode getaddrinfo_complete(struct Curl_easy *data) +{ + struct thread_sync_data *tsd = conn_thread_sync_data(data); + CURLcode result; + + result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res); + /* The tsd->res structure has been copied to async.dns and perhaps the DNS + cache. Set our copy to NULL so destroy_thread_sync_data does not free it. + */ + tsd->res = NULL; + + return result; +} + +#ifdef _WIN32 +static VOID WINAPI +query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped) +{ + size_t ss_size; + const ADDRINFOEXW_ *ai; + struct Curl_addrinfo *ca; + struct Curl_addrinfo *cafirst = NULL; + struct Curl_addrinfo *calast = NULL; +#ifndef CURL_DISABLE_SOCKETPAIR +#ifdef USE_EVENTFD + const void *buf; + const uint64_t val = 1; +#else + char buf[1]; +#endif +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-align" +#endif + struct thread_sync_data *tsd = + CONTAINING_RECORD(overlapped, struct thread_sync_data, w8.overlapped); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + struct thread_data *td = tsd->td; + const ADDRINFOEXW_ *res = tsd->w8.res; + int error = (int)err; + (void)bytes; + + if(error == ERROR_SUCCESS) { + /* traverse the addrinfo list */ + + for(ai = res; ai != NULL; ai = ai->ai_next) { + size_t namelen = ai->ai_canonname ? wcslen(ai->ai_canonname) + 1 : 0; + /* ignore elements with unsupported address family, */ + /* settle family-specific sockaddr structure size. */ + if(ai->ai_family == AF_INET) + ss_size = sizeof(struct sockaddr_in); +#ifdef USE_IPV6 + else if(ai->ai_family == AF_INET6) + ss_size = sizeof(struct sockaddr_in6); +#endif + else + continue; + + /* ignore elements without required address info */ + if(!ai->ai_addr || !(ai->ai_addrlen > 0)) + continue; + + /* ignore elements with bogus address size */ + if((size_t)ai->ai_addrlen < ss_size) + continue; + + ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen); + if(!ca) { + error = EAI_MEMORY; + break; + } + + /* copy each structure member individually, member ordering, */ + /* size, or padding might be different for each platform. */ + ca->ai_flags = ai->ai_flags; + ca->ai_family = ai->ai_family; + ca->ai_socktype = ai->ai_socktype; + ca->ai_protocol = ai->ai_protocol; + ca->ai_addrlen = (curl_socklen_t)ss_size; + ca->ai_addr = NULL; + ca->ai_canonname = NULL; + ca->ai_next = NULL; + + ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); + memcpy(ca->ai_addr, ai->ai_addr, ss_size); + + if(namelen) { + size_t i; + ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size); + for(i = 0; i < namelen; ++i) /* convert wide string to ascii */ + ca->ai_canonname[i] = (char)ai->ai_canonname[i]; + ca->ai_canonname[namelen] = '\0'; + } + + /* if the return list is empty, this becomes the first element */ + if(!cafirst) + cafirst = ca; + + /* add this element last in the return list */ + if(calast) + calast->ai_next = ca; + calast = ca; + } + + /* if we failed, also destroy the Curl_addrinfo list */ + if(error) { + Curl_freeaddrinfo(cafirst); + cafirst = NULL; + } + else if(!cafirst) { +#ifdef EAI_NONAME + /* rfc3493 conformant */ + error = EAI_NONAME; +#else + /* rfc3493 obsoleted */ + error = EAI_NODATA; +#endif +#ifdef USE_WINSOCK + SET_SOCKERRNO(error); +#endif + } + tsd->res = cafirst; + } + + if(tsd->w8.res) { + Curl_FreeAddrInfoExW(tsd->w8.res); + tsd->w8.res = NULL; + } + + if(error) { + tsd->sock_error = SOCKERRNO?SOCKERRNO:error; + if(tsd->sock_error == 0) + tsd->sock_error = RESOLVER_ENOMEM; + } + else { + Curl_addrinfo_set_port(tsd->res, tsd->port); + } + + Curl_mutex_acquire(tsd->mtx); + if(tsd->done) { + /* too late, gotta clean up the mess */ + Curl_mutex_release(tsd->mtx); + destroy_thread_sync_data(tsd); + free(td); + } + else { +#ifndef CURL_DISABLE_SOCKETPAIR + if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { +#ifdef USE_EVENTFD + buf = &val; +#else + buf[0] = 1; +#endif + /* DNS has been resolved, signal client task */ + if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { + /* update sock_erro to errno */ + tsd->sock_error = SOCKERRNO; + } + } +#endif + tsd->done = 1; + Curl_mutex_release(tsd->mtx); + if(td->complete_ev) + SetEvent(td->complete_ev); /* Notify caller that the query completed */ + } +} +#endif + +#ifdef HAVE_GETADDRINFO + +/* + * getaddrinfo_thread() resolves a name and then exits. + * + * For builds without ARES, but with USE_IPV6, create a resolver thread + * and wait on it. + */ +static +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) +DWORD +#else +unsigned int +#endif +CURL_STDCALL getaddrinfo_thread(void *arg) +{ + struct thread_sync_data *tsd = (struct thread_sync_data *)arg; + struct thread_data *td = tsd->td; + char service[12]; + int rc; +#ifndef CURL_DISABLE_SOCKETPAIR +#ifdef USE_EVENTFD + const void *buf; + const uint64_t val = 1; +#else + char buf[1]; +#endif +#endif + + msnprintf(service, sizeof(service), "%d", tsd->port); + + rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res); + + if(rc) { + tsd->sock_error = SOCKERRNO?SOCKERRNO:rc; + if(tsd->sock_error == 0) + tsd->sock_error = RESOLVER_ENOMEM; + } + else { + Curl_addrinfo_set_port(tsd->res, tsd->port); + } + + Curl_mutex_acquire(tsd->mtx); + if(tsd->done) { + /* too late, gotta clean up the mess */ + Curl_mutex_release(tsd->mtx); + destroy_thread_sync_data(tsd); + free(td); + } + else { +#ifndef CURL_DISABLE_SOCKETPAIR + if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { +#ifdef USE_EVENTFD + buf = &val; +#else + buf[0] = 1; +#endif + /* DNS has been resolved, signal client task */ + if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { + /* update sock_erro to errno */ + tsd->sock_error = SOCKERRNO; + } + } +#endif + tsd->done = 1; + Curl_mutex_release(tsd->mtx); + } + + return 0; +} + +#else /* HAVE_GETADDRINFO */ + +/* + * gethostbyname_thread() resolves a name and then exits. + */ +static +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) +DWORD +#else +unsigned int +#endif +CURL_STDCALL gethostbyname_thread(void *arg) +{ + struct thread_sync_data *tsd = (struct thread_sync_data *)arg; + struct thread_data *td = tsd->td; + + tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port); + + if(!tsd->res) { + tsd->sock_error = SOCKERRNO; + if(tsd->sock_error == 0) + tsd->sock_error = RESOLVER_ENOMEM; + } + + Curl_mutex_acquire(tsd->mtx); + if(tsd->done) { + /* too late, gotta clean up the mess */ + Curl_mutex_release(tsd->mtx); + destroy_thread_sync_data(tsd); + free(td); + } + else { + tsd->done = 1; + Curl_mutex_release(tsd->mtx); + } + + return 0; +} + +#endif /* HAVE_GETADDRINFO */ + +/* + * destroy_async_data() cleans up async resolver data and thread handle. + */ +static void destroy_async_data(struct Curl_async *async) +{ + if(async->tdata) { + struct thread_data *td = async->tdata; + int done; +#ifndef CURL_DISABLE_SOCKETPAIR + curl_socket_t sock_rd = td->tsd.sock_pair[0]; + struct Curl_easy *data = td->tsd.data; +#endif + + /* + * if the thread is still blocking in the resolve syscall, detach it and + * let the thread do the cleanup... + */ + Curl_mutex_acquire(td->tsd.mtx); + done = td->tsd.done; + td->tsd.done = 1; + Curl_mutex_release(td->tsd.mtx); + + if(!done) { +#ifdef _WIN32 + if(td->complete_ev) { + CloseHandle(td->complete_ev); + td->complete_ev = NULL; + } +#endif + if(td->thread_hnd != curl_thread_t_null) { + Curl_thread_destroy(td->thread_hnd); + td->thread_hnd = curl_thread_t_null; + } + } + else { +#ifdef _WIN32 + if(td->complete_ev) { + Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev); + WaitForSingleObject(td->complete_ev, INFINITE); + CloseHandle(td->complete_ev); + td->complete_ev = NULL; + } +#endif + if(td->thread_hnd != curl_thread_t_null) + Curl_thread_join(&td->thread_hnd); + + destroy_thread_sync_data(&td->tsd); + + free(async->tdata); + } +#ifndef CURL_DISABLE_SOCKETPAIR + /* + * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE + * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL + */ + Curl_multi_closed(data, sock_rd); + wakeup_close(sock_rd); +#endif + } + async->tdata = NULL; + + free(async->hostname); + async->hostname = NULL; +} + +/* + * init_resolve_thread() starts a new thread that performs the actual + * resolve. This function returns before the resolve is done. + * + * Returns FALSE in case of failure, otherwise TRUE. + */ +static bool init_resolve_thread(struct Curl_easy *data, + const char *hostname, int port, + const struct addrinfo *hints) +{ + struct thread_data *td = calloc(1, sizeof(struct thread_data)); + int err = ENOMEM; + struct Curl_async *asp = &data->state.async; + + data->state.async.tdata = td; + if(!td) + goto errno_exit; + + asp->port = port; + asp->done = FALSE; + asp->status = 0; + asp->dns = NULL; + td->thread_hnd = curl_thread_t_null; +#ifdef _WIN32 + td->complete_ev = NULL; +#endif + + if(!init_thread_sync_data(td, hostname, port, hints)) { + asp->tdata = NULL; + free(td); + goto errno_exit; + } + + free(asp->hostname); + asp->hostname = strdup(hostname); + if(!asp->hostname) + goto err_exit; + + /* The thread will set this to 1 when complete. */ + td->tsd.done = 0; + +#ifdef _WIN32 + if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW && + Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW && + !Curl_win32_impersonating()) { +#define MAX_NAME_LEN 256 /* max domain name is 253 chars */ +#define MAX_PORT_LEN 8 + WCHAR namebuf[MAX_NAME_LEN]; + WCHAR portbuf[MAX_PORT_LEN]; + /* calculate required length */ + int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname, + -1, NULL, 0); + if((w_len > 0) && (w_len < MAX_NAME_LEN)) { + /* do utf8 conversion */ + w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len); + if((w_len > 0) && (w_len < MAX_NAME_LEN)) { + swprintf(portbuf, MAX_PORT_LEN, L"%d", port); + td->tsd.w8.hints.ai_family = hints->ai_family; + td->tsd.w8.hints.ai_socktype = hints->ai_socktype; + td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!td->complete_ev) { + /* failed to start, mark it as done here for proper cleanup. */ + td->tsd.done = 1; + goto err_exit; + } + err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS, + NULL, &td->tsd.w8.hints, &td->tsd.w8.res, + NULL, &td->tsd.w8.overlapped, + &query_complete, &td->tsd.w8.cancel_ev); + if(err != WSA_IO_PENDING) + query_complete((DWORD)err, 0, &td->tsd.w8.overlapped); + return TRUE; + } + } + } +#endif + +#ifdef HAVE_GETADDRINFO + td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd); +#else + td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd); +#endif + + if(td->thread_hnd == curl_thread_t_null) { + /* The thread never started, so mark it as done here for proper cleanup. */ + td->tsd.done = 1; + err = errno; + goto err_exit; + } + + return TRUE; + +err_exit: + destroy_async_data(asp); + +errno_exit: + errno = err; + return FALSE; +} + +/* + * 'entry' may be NULL and then no data is returned + */ +static CURLcode thread_wait_resolv(struct Curl_easy *data, + struct Curl_dns_entry **entry, + bool report) +{ + struct thread_data *td; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + td = data->state.async.tdata; + DEBUGASSERT(td); +#ifdef _WIN32 + DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null); +#else + DEBUGASSERT(td->thread_hnd != curl_thread_t_null); +#endif + + /* wait for the thread to resolve the name */ +#ifdef _WIN32 + if(td->complete_ev) { + WaitForSingleObject(td->complete_ev, INFINITE); + CloseHandle(td->complete_ev); + td->complete_ev = NULL; + if(entry) + result = getaddrinfo_complete(data); + } + else +#endif + if(Curl_thread_join(&td->thread_hnd)) { + if(entry) + result = getaddrinfo_complete(data); + } + else + DEBUGASSERT(0); + + data->state.async.done = TRUE; + + if(entry) + *entry = data->state.async.dns; + + if(!data->state.async.dns && report) + /* a name was not resolved, report error */ + result = Curl_resolver_error(data); + + destroy_async_data(&data->state.async); + + if(!data->state.async.dns && report) + connclose(data->conn, "asynch resolve failed"); + + return result; +} + + +/* + * Until we gain a way to signal the resolver threads to stop early, we must + * simply wait for them and ignore their results. + */ +void Curl_resolver_kill(struct Curl_easy *data) +{ + struct thread_data *td = data->state.async.tdata; + + /* If we are still resolving, we must wait for the threads to fully clean up, + unfortunately. Otherwise, we can simply cancel to clean up any resolver + data. */ +#ifdef _WIN32 + if(td && td->complete_ev) { + Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev); + (void)thread_wait_resolv(data, NULL, FALSE); + } + else +#endif + if(td && td->thread_hnd != curl_thread_t_null + && (data->set.quick_exit != 1L)) + (void)thread_wait_resolv(data, NULL, FALSE); + else + Curl_resolver_cancel(data); +} + +/* + * Curl_resolver_wait_resolv() + * + * Waits for a resolve to finish. This function should be avoided since using + * this risk getting the multi interface to "hang". + * + * If 'entry' is non-NULL, make it point to the resolved dns entry + * + * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, + * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. + * + * This is the version for resolves-in-a-thread. + */ +CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, + struct Curl_dns_entry **entry) +{ + return thread_wait_resolv(data, entry, TRUE); +} + +/* + * Curl_resolver_is_resolved() is called repeatedly to check if a previous + * name resolve request has completed. It should also make sure to time-out if + * the operation seems to take too long. + */ +CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, + struct Curl_dns_entry **entry) +{ + struct thread_data *td = data->state.async.tdata; + int done = 0; + + DEBUGASSERT(entry); + *entry = NULL; + + if(!td) { + DEBUGASSERT(td); + return CURLE_COULDNT_RESOLVE_HOST; + } + + Curl_mutex_acquire(td->tsd.mtx); + done = td->tsd.done; + Curl_mutex_release(td->tsd.mtx); + + if(done) { + getaddrinfo_complete(data); + + if(!data->state.async.dns) { + CURLcode result = Curl_resolver_error(data); + destroy_async_data(&data->state.async); + return result; + } + destroy_async_data(&data->state.async); + *entry = data->state.async.dns; + } + else { + /* poll for name lookup done with exponential backoff up to 250ms */ + /* should be fine even if this converts to 32-bit */ + timediff_t elapsed = Curl_timediff(Curl_now(), + data->progress.t_startsingle); + if(elapsed < 0) + elapsed = 0; + + if(td->poll_interval == 0) + /* Start at 1ms poll interval */ + td->poll_interval = 1; + else if(elapsed >= td->interval_end) + /* Back-off exponentially if last interval expired */ + td->poll_interval *= 2; + + if(td->poll_interval > 250) + td->poll_interval = 250; + + td->interval_end = elapsed + td->poll_interval; + Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME); + } + + return CURLE_OK; +} + +int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks) +{ + int ret_val = 0; + timediff_t milli; + timediff_t ms; + struct resdata *reslv = (struct resdata *)data->state.async.resolver; +#ifndef CURL_DISABLE_SOCKETPAIR + struct thread_data *td = data->state.async.tdata; +#else + (void)socks; +#endif + +#ifndef CURL_DISABLE_SOCKETPAIR + if(td) { + /* return read fd to client for polling the DNS resolution status */ + socks[0] = td->tsd.sock_pair[0]; + td->tsd.data = data; + ret_val = GETSOCK_READSOCK(0); + } + else { +#endif + ms = Curl_timediff(Curl_now(), reslv->start); + if(ms < 3) + milli = 0; + else if(ms <= 50) + milli = ms/3; + else if(ms <= 250) + milli = 50; + else + milli = 200; + Curl_expire(data, milli, EXPIRE_ASYNC_NAME); +#ifndef CURL_DISABLE_SOCKETPAIR + } +#endif + + + return ret_val; +} + +#ifndef HAVE_GETADDRINFO +/* + * Curl_getaddrinfo() - for platforms without getaddrinfo + */ +struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, + const char *hostname, + int port, + int *waitp) +{ + struct resdata *reslv = (struct resdata *)data->state.async.resolver; + + *waitp = 0; /* default to synchronous response */ + + reslv->start = Curl_now(); + + /* fire up a new resolver thread! */ + if(init_resolve_thread(data, hostname, port, NULL)) { + *waitp = 1; /* expect asynchronous response */ + return NULL; + } + + failf(data, "getaddrinfo() thread failed"); + + return NULL; +} + +#else /* !HAVE_GETADDRINFO */ + +/* + * Curl_resolver_getaddrinfo() - for getaddrinfo + */ +struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, + const char *hostname, + int port, + int *waitp) +{ + struct addrinfo hints; + int pf = PF_INET; + struct resdata *reslv = (struct resdata *)data->state.async.resolver; + + *waitp = 0; /* default to synchronous response */ + +#ifdef CURLRES_IPV6 + if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { + /* The stack seems to be IPv6-enabled */ + if(data->conn->ip_version == CURL_IPRESOLVE_V6) + pf = PF_INET6; + else + pf = PF_UNSPEC; + } +#endif /* CURLRES_IPV6 */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = pf; + hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)? + SOCK_STREAM : SOCK_DGRAM; + + reslv->start = Curl_now(); + /* fire up a new resolver thread! */ + if(init_resolve_thread(data, hostname, port, &hints)) { + *waitp = 1; /* expect asynchronous response */ + return NULL; + } + + failf(data, "getaddrinfo() thread failed to start"); + return NULL; + +} + +#endif /* !HAVE_GETADDRINFO */ + +CURLcode Curl_set_dns_servers(struct Curl_easy *data, + char *servers) +{ + (void)data; + (void)servers; + return CURLE_NOT_BUILT_IN; + +} + +CURLcode Curl_set_dns_interface(struct Curl_easy *data, + const char *interf) +{ + (void)data; + (void)interf; + return CURLE_NOT_BUILT_IN; +} + +CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, + const char *local_ip4) +{ + (void)data; + (void)local_ip4; + return CURLE_NOT_BUILT_IN; +} + +CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, + const char *local_ip6) +{ + (void)data; + (void)local_ip6; + return CURLE_NOT_BUILT_IN; +} + +#endif /* CURLRES_THREADED */ diff --git a/third_party/curl/lib/asyn.h b/third_party/curl/lib/asyn.h new file mode 100644 index 0000000000..0ff2048845 --- /dev/null +++ b/third_party/curl/lib/asyn.h @@ -0,0 +1,184 @@ +#ifndef HEADER_CURL_ASYN_H +#define HEADER_CURL_ASYN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "curl_addrinfo.h" + +struct addrinfo; +struct hostent; +struct Curl_easy; +struct connectdata; +struct Curl_dns_entry; + +/* + * This header defines all functions in the internal asynch resolver interface. + * All asynch resolvers need to provide these functions. + * asyn-ares.c and asyn-thread.c are the current implementations of asynch + * resolver backends. + */ + +/* + * Curl_resolver_global_init() + * + * Called from curl_global_init() to initialize global resolver environment. + * Returning anything else than CURLE_OK fails curl_global_init(). + */ +int Curl_resolver_global_init(void); + +/* + * Curl_resolver_global_cleanup() + * Called from curl_global_cleanup() to destroy global resolver environment. + */ +void Curl_resolver_global_cleanup(void); + +/* + * Curl_resolver_init() + * Called from curl_easy_init() -> Curl_open() to initialize resolver + * URL-state specific environment ('resolver' member of the UrlState + * structure). Should fill the passed pointer by the initialized handler. + * Returning anything else than CURLE_OK fails curl_easy_init() with the + * correspondent code. + */ +CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver); + +/* + * Curl_resolver_cleanup() + * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver + * URL-state specific environment ('resolver' member of the UrlState + * structure). Should destroy the handler and free all resources connected to + * it. + */ +void Curl_resolver_cleanup(void *resolver); + +/* + * Curl_resolver_duphandle() + * Called from curl_easy_duphandle() to duplicate resolver URL-state specific + * environment ('resolver' member of the UrlState structure). Should + * duplicate the 'from' handle and pass the resulting handle to the 'to' + * pointer. Returning anything else than CURLE_OK causes failed + * curl_easy_duphandle() call. + */ +CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, + void *from); + +/* + * Curl_resolver_cancel(). + * + * It is called from inside other functions to cancel currently performing + * resolver request. Should also free any temporary resources allocated to + * perform a request. This never waits for resolver threads to complete. + * + * It is safe to call this when conn is in any state. + */ +void Curl_resolver_cancel(struct Curl_easy *data); + +/* + * Curl_resolver_kill(). + * + * This acts like Curl_resolver_cancel() except it will block until any threads + * associated with the resolver are complete. This never blocks for resolvers + * that do not use threads. This is intended to be the "last chance" function + * that cleans up an in-progress resolver completely (before its owner is about + * to die). + * + * It is safe to call this when conn is in any state. + */ +void Curl_resolver_kill(struct Curl_easy *data); + +/* Curl_resolver_getsock() + * + * This function is called from the multi_getsock() function. 'sock' is a + * pointer to an array to hold the file descriptors, with 'numsock' being the + * size of that array (in number of entries). This function is supposed to + * return bitmask indicating what file descriptors (referring to array indexes + * in the 'sock' array) to wait for, read/write. + */ +int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *sock); + +/* + * Curl_resolver_is_resolved() + * + * Called repeatedly to check if a previous name resolve request has + * completed. It should also make sure to time-out if the operation seems to + * take too long. + * + * Returns normal CURLcode errors. + */ +CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, + struct Curl_dns_entry **dns); + +/* + * Curl_resolver_wait_resolv() + * + * Waits for a resolve to finish. This function should be avoided since using + * this risk getting the multi interface to "hang". + * + * If 'entry' is non-NULL, make it point to the resolved dns entry + * + * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, + * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. + */ +CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, + struct Curl_dns_entry **dnsentry); + +/* + * Curl_resolver_getaddrinfo() - when using this resolver + * + * Returns name information about the given hostname and port number. If + * successful, the 'hostent' is returned and the fourth argument will point to + * memory we need to free after use. That memory *MUST* be freed with + * Curl_freeaddrinfo(), nothing else. + * + * Each resolver backend must of course make sure to return data in the + * correct format to comply with this. + */ +struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, + const char *hostname, + int port, + int *waitp); + +#ifndef CURLRES_ASYNCH +/* convert these functions if an asynch resolver is not used */ +#define Curl_resolver_cancel(x) Curl_nop_stmt +#define Curl_resolver_kill(x) Curl_nop_stmt +#define Curl_resolver_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST +#define Curl_resolver_wait_resolv(x,y) CURLE_COULDNT_RESOLVE_HOST +#define Curl_resolver_duphandle(x,y,z) CURLE_OK +#define Curl_resolver_init(x,y) CURLE_OK +#define Curl_resolver_global_init() CURLE_OK +#define Curl_resolver_global_cleanup() Curl_nop_stmt +#define Curl_resolver_cleanup(x) Curl_nop_stmt +#endif + +#ifdef CURLRES_ASYNCH +#define Curl_resolver_asynch() 1 +#else +#define Curl_resolver_asynch() 0 +#endif + + +/********** end of generic resolver interface functions *****************/ +#endif /* HEADER_CURL_ASYN_H */ diff --git a/third_party/curl/lib/base64.c b/third_party/curl/lib/base64.c new file mode 100644 index 0000000000..8373115d20 --- /dev/null +++ b/third_party/curl/lib/base64.c @@ -0,0 +1,293 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* Base64 encoding/decoding */ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \ + !defined(CURL_DISABLE_LDAP) || \ + !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_POP3) || \ + !defined(CURL_DISABLE_IMAP) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ + !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || defined(BUILDING_CURL) +#include "curl/curl.h" +#include "warnless.h" +#include "curl_base64.h" + +/* The last 2 #include files should be in this order */ +#ifdef BUILDING_LIBCURL +#include "curl_memory.h" +#endif +#include "memdebug.h" + +/* ---- Base64 Encoding/Decoding Table --- */ +/* Padding character string starts at offset 64. */ +static const char base64encdec[]= + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +/* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648 + section 5 */ +static const char base64url[]= + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +static const unsigned char decodetable[] = +{ 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, + 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51 }; +/* + * Curl_base64_decode() + * + * Given a base64 NUL-terminated string at src, decode it and return a + * pointer in *outptr to a newly allocated memory area holding decoded + * data. Size of decoded data is returned in variable pointed by outlen. + * + * Returns CURLE_OK on success, otherwise specific error code. Function + * output shall not be considered valid unless CURLE_OK is returned. + * + * When decoded data length is 0, returns NULL in *outptr. + * + * @unittest: 1302 + */ +CURLcode Curl_base64_decode(const char *src, + unsigned char **outptr, size_t *outlen) +{ + size_t srclen = 0; + size_t padding = 0; + size_t i; + size_t numQuantums; + size_t fullQuantums; + size_t rawlen = 0; + unsigned char *pos; + unsigned char *newstr; + unsigned char lookup[256]; + + *outptr = NULL; + *outlen = 0; + srclen = strlen(src); + + /* Check the length of the input string is valid */ + if(!srclen || srclen % 4) + return CURLE_BAD_CONTENT_ENCODING; + + /* srclen is at least 4 here */ + while(src[srclen - 1 - padding] == '=') { + /* count padding characters */ + padding++; + /* A maximum of two = padding characters is allowed */ + if(padding > 2) + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Calculate the number of quantums */ + numQuantums = srclen / 4; + fullQuantums = numQuantums - (padding ? 1 : 0); + + /* Calculate the size of the decoded string */ + rawlen = (numQuantums * 3) - padding; + + /* Allocate our buffer including room for a null-terminator */ + newstr = malloc(rawlen + 1); + if(!newstr) + return CURLE_OUT_OF_MEMORY; + + pos = newstr; + + memset(lookup, 0xff, sizeof(lookup)); + memcpy(&lookup['+'], decodetable, sizeof(decodetable)); + /* replaces + { + unsigned char c; + const unsigned char *p = (const unsigned char *)base64encdec; + for(c = 0; *p; c++, p++) + lookup[*p] = c; + } + */ + + /* Decode the complete quantums first */ + for(i = 0; i < fullQuantums; i++) { + unsigned char val; + unsigned int x = 0; + int j; + + for(j = 0; j < 4; j++) { + val = lookup[(unsigned char)*src++]; + if(val == 0xff) /* bad symbol */ + goto bad; + x = (x << 6) | val; + } + pos[2] = x & 0xff; + pos[1] = (x >> 8) & 0xff; + pos[0] = (x >> 16) & 0xff; + pos += 3; + } + if(padding) { + /* this means either 8 or 16 bits output */ + unsigned char val; + unsigned int x = 0; + int j; + size_t padc = 0; + for(j = 0; j < 4; j++) { + if(*src == '=') { + x <<= 6; + src++; + if(++padc > padding) + /* this is a badly placed '=' symbol! */ + goto bad; + } + else { + val = lookup[(unsigned char)*src++]; + if(val == 0xff) /* bad symbol */ + goto bad; + x = (x << 6) | val; + } + } + if(padding == 1) + pos[1] = (x >> 8) & 0xff; + pos[0] = (x >> 16) & 0xff; + pos += 3 - padding; + } + + /* Zero terminate */ + *pos = '\0'; + + /* Return the decoded data */ + *outptr = newstr; + *outlen = rawlen; + + return CURLE_OK; +bad: + free(newstr); + return CURLE_BAD_CONTENT_ENCODING; +} + +static CURLcode base64_encode(const char *table64, + const char *inputbuff, size_t insize, + char **outptr, size_t *outlen) +{ + char *output; + char *base64data; + const unsigned char *in = (unsigned char *)inputbuff; + const char *padstr = &table64[64]; /* Point to padding string. */ + + *outptr = NULL; + *outlen = 0; + + if(!insize) + insize = strlen(inputbuff); + +#if SIZEOF_SIZE_T == 4 + if(insize > UINT_MAX/4) + return CURLE_OUT_OF_MEMORY; +#endif + + base64data = output = malloc((insize + 2) / 3 * 4 + 1); + if(!output) + return CURLE_OUT_OF_MEMORY; + + while(insize >= 3) { + *output++ = table64[ in[0] >> 2 ]; + *output++ = table64[ ((in[0] & 0x03) << 4) | (in[1] >> 4) ]; + *output++ = table64[ ((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6) ]; + *output++ = table64[ in[2] & 0x3F ]; + insize -= 3; + in += 3; + } + if(insize) { + /* this is only one or two bytes now */ + *output++ = table64[ in[0] >> 2 ]; + if(insize == 1) { + *output++ = table64[ ((in[0] & 0x03) << 4) ]; + if(*padstr) { + *output++ = *padstr; + *output++ = *padstr; + } + } + else { + /* insize == 2 */ + *output++ = table64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4) ]; + *output++ = table64[ ((in[1] & 0x0F) << 2) ]; + if(*padstr) + *output++ = *padstr; + } + } + + /* Zero terminate */ + *output = '\0'; + + /* Return the pointer to the new data (allocated memory) */ + *outptr = base64data; + + /* Return the length of the new data */ + *outlen = (size_t)(output - base64data); + + return CURLE_OK; +} + +/* + * Curl_base64_encode() + * + * Given a pointer to an input buffer and an input size, encode it and + * return a pointer in *outptr to a newly allocated memory area holding + * encoded data. Size of encoded data is returned in variable pointed by + * outlen. + * + * Input length of 0 indicates input buffer holds a NUL-terminated string. + * + * Returns CURLE_OK on success, otherwise specific error code. Function + * output shall not be considered valid unless CURLE_OK is returned. + * + * @unittest: 1302 + */ +CURLcode Curl_base64_encode(const char *inputbuff, size_t insize, + char **outptr, size_t *outlen) +{ + return base64_encode(base64encdec, inputbuff, insize, outptr, outlen); +} + +/* + * Curl_base64url_encode() + * + * Given a pointer to an input buffer and an input size, encode it and + * return a pointer in *outptr to a newly allocated memory area holding + * encoded data. Size of encoded data is returned in variable pointed by + * outlen. + * + * Input length of 0 indicates input buffer holds a NUL-terminated string. + * + * Returns CURLE_OK on success, otherwise specific error code. Function + * output shall not be considered valid unless CURLE_OK is returned. + * + * @unittest: 1302 + */ +CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize, + char **outptr, size_t *outlen) +{ + return base64_encode(base64url, inputbuff, insize, outptr, outlen); +} + +#endif /* no users so disabled */ diff --git a/third_party/curl/lib/bufq.c b/third_party/curl/lib/bufq.c new file mode 100644 index 0000000000..c3245516c9 --- /dev/null +++ b/third_party/curl/lib/bufq.c @@ -0,0 +1,677 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "bufq.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +static bool chunk_is_empty(const struct buf_chunk *chunk) +{ + return chunk->r_offset >= chunk->w_offset; +} + +static bool chunk_is_full(const struct buf_chunk *chunk) +{ + return chunk->w_offset >= chunk->dlen; +} + +static size_t chunk_len(const struct buf_chunk *chunk) +{ + return chunk->w_offset - chunk->r_offset; +} + +static size_t chunk_space(const struct buf_chunk *chunk) +{ + return chunk->dlen - chunk->w_offset; +} + +static void chunk_reset(struct buf_chunk *chunk) +{ + chunk->next = NULL; + chunk->r_offset = chunk->w_offset = 0; +} + +static size_t chunk_append(struct buf_chunk *chunk, + const unsigned char *buf, size_t len) +{ + unsigned char *p = &chunk->x.data[chunk->w_offset]; + size_t n = chunk->dlen - chunk->w_offset; + DEBUGASSERT(chunk->dlen >= chunk->w_offset); + if(n) { + n = CURLMIN(n, len); + memcpy(p, buf, n); + chunk->w_offset += n; + } + return n; +} + +static size_t chunk_read(struct buf_chunk *chunk, + unsigned char *buf, size_t len) +{ + unsigned char *p = &chunk->x.data[chunk->r_offset]; + size_t n = chunk->w_offset - chunk->r_offset; + DEBUGASSERT(chunk->w_offset >= chunk->r_offset); + if(!n) { + return 0; + } + else if(n <= len) { + memcpy(buf, p, n); + chunk->r_offset = chunk->w_offset = 0; + return n; + } + else { + memcpy(buf, p, len); + chunk->r_offset += len; + return len; + } +} + +static ssize_t chunk_slurpn(struct buf_chunk *chunk, size_t max_len, + Curl_bufq_reader *reader, + void *reader_ctx, CURLcode *err) +{ + unsigned char *p = &chunk->x.data[chunk->w_offset]; + size_t n = chunk->dlen - chunk->w_offset; /* free amount */ + ssize_t nread; + + DEBUGASSERT(chunk->dlen >= chunk->w_offset); + if(!n) { + *err = CURLE_AGAIN; + return -1; + } + if(max_len && n > max_len) + n = max_len; + nread = reader(reader_ctx, p, n, err); + if(nread > 0) { + DEBUGASSERT((size_t)nread <= n); + chunk->w_offset += nread; + } + return nread; +} + +static void chunk_peek(const struct buf_chunk *chunk, + const unsigned char **pbuf, size_t *plen) +{ + DEBUGASSERT(chunk->w_offset >= chunk->r_offset); + *pbuf = &chunk->x.data[chunk->r_offset]; + *plen = chunk->w_offset - chunk->r_offset; +} + +static void chunk_peek_at(const struct buf_chunk *chunk, size_t offset, + const unsigned char **pbuf, size_t *plen) +{ + offset += chunk->r_offset; + DEBUGASSERT(chunk->w_offset >= offset); + *pbuf = &chunk->x.data[offset]; + *plen = chunk->w_offset - offset; +} + +static size_t chunk_skip(struct buf_chunk *chunk, size_t amount) +{ + size_t n = chunk->w_offset - chunk->r_offset; + DEBUGASSERT(chunk->w_offset >= chunk->r_offset); + if(n) { + n = CURLMIN(n, amount); + chunk->r_offset += n; + if(chunk->r_offset == chunk->w_offset) + chunk->r_offset = chunk->w_offset = 0; + } + return n; +} + +static void chunk_list_free(struct buf_chunk **anchor) +{ + struct buf_chunk *chunk; + while(*anchor) { + chunk = *anchor; + *anchor = chunk->next; + free(chunk); + } +} + + + +void Curl_bufcp_init(struct bufc_pool *pool, + size_t chunk_size, size_t spare_max) +{ + DEBUGASSERT(chunk_size > 0); + DEBUGASSERT(spare_max > 0); + memset(pool, 0, sizeof(*pool)); + pool->chunk_size = chunk_size; + pool->spare_max = spare_max; +} + +static CURLcode bufcp_take(struct bufc_pool *pool, + struct buf_chunk **pchunk) +{ + struct buf_chunk *chunk = NULL; + + if(pool->spare) { + chunk = pool->spare; + pool->spare = chunk->next; + --pool->spare_count; + chunk_reset(chunk); + *pchunk = chunk; + return CURLE_OK; + } + + chunk = calloc(1, sizeof(*chunk) + pool->chunk_size); + if(!chunk) { + *pchunk = NULL; + return CURLE_OUT_OF_MEMORY; + } + chunk->dlen = pool->chunk_size; + *pchunk = chunk; + return CURLE_OK; +} + +static void bufcp_put(struct bufc_pool *pool, + struct buf_chunk *chunk) +{ + if(pool->spare_count >= pool->spare_max) { + free(chunk); + } + else { + chunk_reset(chunk); + chunk->next = pool->spare; + pool->spare = chunk; + ++pool->spare_count; + } +} + +void Curl_bufcp_free(struct bufc_pool *pool) +{ + chunk_list_free(&pool->spare); + pool->spare_count = 0; +} + +static void bufq_init(struct bufq *q, struct bufc_pool *pool, + size_t chunk_size, size_t max_chunks, int opts) +{ + DEBUGASSERT(chunk_size > 0); + DEBUGASSERT(max_chunks > 0); + memset(q, 0, sizeof(*q)); + q->chunk_size = chunk_size; + q->max_chunks = max_chunks; + q->pool = pool; + q->opts = opts; +} + +void Curl_bufq_init2(struct bufq *q, size_t chunk_size, size_t max_chunks, + int opts) +{ + bufq_init(q, NULL, chunk_size, max_chunks, opts); +} + +void Curl_bufq_init(struct bufq *q, size_t chunk_size, size_t max_chunks) +{ + bufq_init(q, NULL, chunk_size, max_chunks, BUFQ_OPT_NONE); +} + +void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool, + size_t max_chunks, int opts) +{ + bufq_init(q, pool, pool->chunk_size, max_chunks, opts); +} + +void Curl_bufq_free(struct bufq *q) +{ + chunk_list_free(&q->head); + chunk_list_free(&q->spare); + q->tail = NULL; + q->chunk_count = 0; +} + +void Curl_bufq_reset(struct bufq *q) +{ + struct buf_chunk *chunk; + while(q->head) { + chunk = q->head; + q->head = chunk->next; + chunk->next = q->spare; + q->spare = chunk; + } + q->tail = NULL; +} + +size_t Curl_bufq_len(const struct bufq *q) +{ + const struct buf_chunk *chunk = q->head; + size_t len = 0; + while(chunk) { + len += chunk_len(chunk); + chunk = chunk->next; + } + return len; +} + +size_t Curl_bufq_space(const struct bufq *q) +{ + size_t space = 0; + if(q->tail) + space += chunk_space(q->tail); + if(q->spare) { + struct buf_chunk *chunk = q->spare; + while(chunk) { + space += chunk->dlen; + chunk = chunk->next; + } + } + if(q->chunk_count < q->max_chunks) { + space += (q->max_chunks - q->chunk_count) * q->chunk_size; + } + return space; +} + +bool Curl_bufq_is_empty(const struct bufq *q) +{ + return !q->head || chunk_is_empty(q->head); +} + +bool Curl_bufq_is_full(const struct bufq *q) +{ + if(!q->tail || q->spare) + return FALSE; + if(q->chunk_count < q->max_chunks) + return FALSE; + if(q->chunk_count > q->max_chunks) + return TRUE; + /* we have no spares and cannot make more, is the tail full? */ + return chunk_is_full(q->tail); +} + +static struct buf_chunk *get_spare(struct bufq *q) +{ + struct buf_chunk *chunk = NULL; + + if(q->spare) { + chunk = q->spare; + q->spare = chunk->next; + chunk_reset(chunk); + return chunk; + } + + if(q->chunk_count >= q->max_chunks && (!(q->opts & BUFQ_OPT_SOFT_LIMIT))) + return NULL; + + if(q->pool) { + if(bufcp_take(q->pool, &chunk)) + return NULL; + ++q->chunk_count; + return chunk; + } + else { + chunk = calloc(1, sizeof(*chunk) + q->chunk_size); + if(!chunk) + return NULL; + chunk->dlen = q->chunk_size; + ++q->chunk_count; + return chunk; + } +} + +static void prune_head(struct bufq *q) +{ + struct buf_chunk *chunk; + + while(q->head && chunk_is_empty(q->head)) { + chunk = q->head; + q->head = chunk->next; + if(q->tail == chunk) + q->tail = q->head; + if(q->pool) { + bufcp_put(q->pool, chunk); + --q->chunk_count; + } + else if((q->chunk_count > q->max_chunks) || + (q->opts & BUFQ_OPT_NO_SPARES)) { + /* SOFT_LIMIT allowed us more than max. free spares until + * we are at max again. Or free them if we are configured + * to not use spares. */ + free(chunk); + --q->chunk_count; + } + else { + chunk->next = q->spare; + q->spare = chunk; + } + } +} + +static struct buf_chunk *get_non_full_tail(struct bufq *q) +{ + struct buf_chunk *chunk; + + if(q->tail && !chunk_is_full(q->tail)) + return q->tail; + chunk = get_spare(q); + if(chunk) { + /* new tail, and possibly new head */ + if(q->tail) { + q->tail->next = chunk; + q->tail = chunk; + } + else { + DEBUGASSERT(!q->head); + q->head = q->tail = chunk; + } + } + return chunk; +} + +ssize_t Curl_bufq_write(struct bufq *q, + const unsigned char *buf, size_t len, + CURLcode *err) +{ + struct buf_chunk *tail; + ssize_t nwritten = 0; + size_t n; + + DEBUGASSERT(q->max_chunks > 0); + while(len) { + tail = get_non_full_tail(q); + if(!tail) { + if((q->chunk_count < q->max_chunks) || (q->opts & BUFQ_OPT_SOFT_LIMIT)) { + *err = CURLE_OUT_OF_MEMORY; + return -1; + } + break; + } + n = chunk_append(tail, buf, len); + if(!n) + break; + nwritten += n; + buf += n; + len -= n; + } + if(nwritten == 0 && len) { + *err = CURLE_AGAIN; + return -1; + } + *err = CURLE_OK; + return nwritten; +} + +CURLcode Curl_bufq_cwrite(struct bufq *q, + const char *buf, size_t len, + size_t *pnwritten) +{ + ssize_t n; + CURLcode result; + n = Curl_bufq_write(q, (const unsigned char *)buf, len, &result); + *pnwritten = (n < 0)? 0 : (size_t)n; + return result; +} + +ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, + CURLcode *err) +{ + ssize_t nread = 0; + size_t n; + + *err = CURLE_OK; + while(len && q->head) { + n = chunk_read(q->head, buf, len); + if(n) { + nread += n; + buf += n; + len -= n; + } + prune_head(q); + } + if(nread == 0) { + *err = CURLE_AGAIN; + return -1; + } + return nread; +} + +CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len, + size_t *pnread) +{ + ssize_t n; + CURLcode result; + n = Curl_bufq_read(q, (unsigned char *)buf, len, &result); + *pnread = (n < 0)? 0 : (size_t)n; + return result; +} + +bool Curl_bufq_peek(struct bufq *q, + const unsigned char **pbuf, size_t *plen) +{ + if(q->head && chunk_is_empty(q->head)) { + prune_head(q); + } + if(q->head && !chunk_is_empty(q->head)) { + chunk_peek(q->head, pbuf, plen); + return TRUE; + } + *pbuf = NULL; + *plen = 0; + return FALSE; +} + +bool Curl_bufq_peek_at(struct bufq *q, size_t offset, + const unsigned char **pbuf, size_t *plen) +{ + struct buf_chunk *c = q->head; + size_t clen; + + while(c) { + clen = chunk_len(c); + if(!clen) + break; + if(offset >= clen) { + offset -= clen; + c = c->next; + continue; + } + chunk_peek_at(c, offset, pbuf, plen); + return TRUE; + } + *pbuf = NULL; + *plen = 0; + return FALSE; +} + +void Curl_bufq_skip(struct bufq *q, size_t amount) +{ + size_t n; + + while(amount && q->head) { + n = chunk_skip(q->head, amount); + amount -= n; + prune_head(q); + } +} + +ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, + void *writer_ctx, CURLcode *err) +{ + const unsigned char *buf; + size_t blen; + ssize_t nwritten = 0; + + while(Curl_bufq_peek(q, &buf, &blen)) { + ssize_t chunk_written; + + chunk_written = writer(writer_ctx, buf, blen, err); + if(chunk_written < 0) { + if(!nwritten || *err != CURLE_AGAIN) { + /* blocked on first write or real error, fail */ + nwritten = -1; + } + break; + } + if(!chunk_written) { + if(!nwritten) { + /* treat as blocked */ + *err = CURLE_AGAIN; + nwritten = -1; + } + break; + } + Curl_bufq_skip(q, (size_t)chunk_written); + nwritten += chunk_written; + } + return nwritten; +} + +ssize_t Curl_bufq_write_pass(struct bufq *q, + const unsigned char *buf, size_t len, + Curl_bufq_writer *writer, void *writer_ctx, + CURLcode *err) +{ + ssize_t nwritten = 0, n; + + *err = CURLE_OK; + while(len) { + if(Curl_bufq_is_full(q)) { + /* try to make room in case we are full */ + n = Curl_bufq_pass(q, writer, writer_ctx, err); + if(n < 0) { + if(*err != CURLE_AGAIN) { + /* real error, fail */ + return -1; + } + /* would block, bufq is full, give up */ + break; + } + } + + /* Add whatever is remaining now to bufq */ + n = Curl_bufq_write(q, buf, len, err); + if(n < 0) { + if(*err != CURLE_AGAIN) { + /* real error, fail */ + return -1; + } + /* no room in bufq */ + break; + } + /* edge case of writer returning 0 (and len is >0) + * break or we might enter an infinite loop here */ + if(n == 0) + break; + + /* Maybe only part of `data` has been added, continue to loop */ + buf += (size_t)n; + len -= (size_t)n; + nwritten += (size_t)n; + } + + if(!nwritten && len) { + *err = CURLE_AGAIN; + return -1; + } + *err = CURLE_OK; + return nwritten; +} + +ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len, + Curl_bufq_reader *reader, void *reader_ctx, + CURLcode *err) +{ + struct buf_chunk *tail = NULL; + ssize_t nread; + + *err = CURLE_AGAIN; + tail = get_non_full_tail(q); + if(!tail) { + if(q->chunk_count < q->max_chunks) { + *err = CURLE_OUT_OF_MEMORY; + return -1; + } + /* full, blocked */ + *err = CURLE_AGAIN; + return -1; + } + + nread = chunk_slurpn(tail, max_len, reader, reader_ctx, err); + if(nread < 0) { + return -1; + } + else if(nread == 0) { + /* eof */ + *err = CURLE_OK; + } + return nread; +} + +/** + * Read up to `max_len` bytes and append it to the end of the buffer queue. + * if `max_len` is 0, no limit is imposed and the call behaves exactly + * the same as `Curl_bufq_slurp()`. + * Returns the total amount of buf read (may be 0) or -1 on other + * reader errors. + * Note that even in case of a -1 chunks may have been read and + * the buffer queue will have different length than before. + */ +static ssize_t bufq_slurpn(struct bufq *q, size_t max_len, + Curl_bufq_reader *reader, void *reader_ctx, + CURLcode *err) +{ + ssize_t nread = 0, n; + + *err = CURLE_AGAIN; + while(1) { + + n = Curl_bufq_sipn(q, max_len, reader, reader_ctx, err); + if(n < 0) { + if(!nread || *err != CURLE_AGAIN) { + /* blocked on first read or real error, fail */ + nread = -1; + } + else + *err = CURLE_OK; + break; + } + else if(n == 0) { + /* eof */ + *err = CURLE_OK; + break; + } + nread += (size_t)n; + if(max_len) { + DEBUGASSERT((size_t)n <= max_len); + max_len -= (size_t)n; + if(!max_len) + break; + } + /* give up slurping when we get less bytes than we asked for */ + if(q->tail && !chunk_is_full(q->tail)) + break; + } + return nread; +} + +ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, + void *reader_ctx, CURLcode *err) +{ + return bufq_slurpn(q, 0, reader, reader_ctx, err); +} diff --git a/third_party/curl/lib/bufq.h b/third_party/curl/lib/bufq.h new file mode 100644 index 0000000000..87ffa45da2 --- /dev/null +++ b/third_party/curl/lib/bufq.h @@ -0,0 +1,272 @@ +#ifndef HEADER_CURL_BUFQ_H +#define HEADER_CURL_BUFQ_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include + +/** + * A chunk of bytes for reading and writing. + * The size is fixed a creation with read and write offset + * for where unread content is. + */ +struct buf_chunk { + struct buf_chunk *next; /* to keep it in a list */ + size_t dlen; /* the amount of allocated x.data[] */ + size_t r_offset; /* first unread bytes */ + size_t w_offset; /* one after last written byte */ + union { + unsigned char data[1]; /* the buffer for `dlen` bytes */ + void *dummy; /* alignment */ + } x; +}; + +/** + * A pool for providing/keeping a number of chunks of the same size + * + * The same pool can be shared by many `bufq` instances. However, a pool + * is not thread safe. All bufqs using it are supposed to operate in the + * same thread. + */ +struct bufc_pool { + struct buf_chunk *spare; /* list of available spare chunks */ + size_t chunk_size; /* the size of chunks in this pool */ + size_t spare_count; /* current number of spare chunks in list */ + size_t spare_max; /* max number of spares to keep */ +}; + +void Curl_bufcp_init(struct bufc_pool *pool, + size_t chunk_size, size_t spare_max); + +void Curl_bufcp_free(struct bufc_pool *pool); + +/** + * A queue of byte chunks for reading and writing. + * Reading is done from `head`, writing is done to `tail`. + * + * `bufq`s can be empty or full or neither. Its `len` is the number + * of bytes that can be read. For an empty bufq, `len` will be 0. + * + * By default, a bufq can hold up to `max_chunks * chunk_size` number + * of bytes. When `max_chunks` are used (in the `head` list) and the + * `tail` chunk is full, the bufq will report that it is full. + * + * On a full bufq, `len` may be less than the maximum number of bytes, + * e.g. when the head chunk is partially read. `len` may also become + * larger than the max when option `BUFQ_OPT_SOFT_LIMIT` is used. + * + * By default, writing to a full bufq will return (-1, CURLE_AGAIN). Same + * as reading from an empty bufq. + * With `BUFQ_OPT_SOFT_LIMIT` set, a bufq will allow writing becond this + * limit and use more than `max_chunks`. However it will report that it + * is full nevertheless. This is provided for situation where writes + * preferably never fail (except for memory exhaustion). + * + * By default and without a pool, a bufq will keep chunks that read + * empty in its `spare` list. Option `BUFQ_OPT_NO_SPARES` will + * disable that and free chunks once they become empty. + * + * When providing a pool to a bufq, all chunk creation and spare handling + * will be delegated to that pool. + */ +struct bufq { + struct buf_chunk *head; /* chunk with bytes to read from */ + struct buf_chunk *tail; /* chunk to write to */ + struct buf_chunk *spare; /* list of free chunks, unless `pool` */ + struct bufc_pool *pool; /* optional pool for free chunks */ + size_t chunk_count; /* current number of chunks in `head+spare` */ + size_t max_chunks; /* max `head` chunks to use */ + size_t chunk_size; /* size of chunks to manage */ + int opts; /* options for handling queue, see below */ +}; + +/** + * Default behaviour: chunk limit is "hard", meaning attempts to write + * more bytes than can be hold in `max_chunks` is refused and will return + * -1, CURLE_AGAIN. */ +#define BUFQ_OPT_NONE (0) +/** + * Make `max_chunks` a "soft" limit. A bufq will report that it is "full" + * when `max_chunks` are used, but allows writing beyond this limit. + */ +#define BUFQ_OPT_SOFT_LIMIT (1 << 0) +/** + * Do not keep spare chunks. + */ +#define BUFQ_OPT_NO_SPARES (1 << 1) + +/** + * Initialize a buffer queue that can hold up to `max_chunks` buffers + * each of size `chunk_size`. The bufq will not allow writing of + * more bytes than can be held in `max_chunks`. + */ +void Curl_bufq_init(struct bufq *q, size_t chunk_size, size_t max_chunks); + +/** + * Initialize a buffer queue that can hold up to `max_chunks` buffers + * each of size `chunk_size` with the given options. See `BUFQ_OPT_*`. + */ +void Curl_bufq_init2(struct bufq *q, size_t chunk_size, + size_t max_chunks, int opts); + +void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool, + size_t max_chunks, int opts); + +/** + * Reset the buffer queue to be empty. Will keep any allocated buffer + * chunks around. + */ +void Curl_bufq_reset(struct bufq *q); + +/** + * Free all resources held by the buffer queue. + */ +void Curl_bufq_free(struct bufq *q); + +/** + * Return the total amount of data in the queue. + */ +size_t Curl_bufq_len(const struct bufq *q); + +/** + * Return the total amount of free space in the queue. + * The returned length is the number of bytes that can + * be expected to be written successfully to the bufq, + * providing no memory allocations fail. + */ +size_t Curl_bufq_space(const struct bufq *q); + +/** + * Returns TRUE iff there is no data in the buffer queue. + */ +bool Curl_bufq_is_empty(const struct bufq *q); + +/** + * Returns TRUE iff there is no space left in the buffer queue. + */ +bool Curl_bufq_is_full(const struct bufq *q); + +/** + * Write buf to the end of the buffer queue. The buf is copied + * and the amount of copied bytes is returned. + * A return code of -1 indicates an error, setting `err` to the + * cause. An err of CURLE_AGAIN is returned if the buffer queue is full. + */ +ssize_t Curl_bufq_write(struct bufq *q, + const unsigned char *buf, size_t len, + CURLcode *err); + +CURLcode Curl_bufq_cwrite(struct bufq *q, + const char *buf, size_t len, + size_t *pnwritten); + +/** + * Read buf from the start of the buffer queue. The buf is copied + * and the amount of copied bytes is returned. + * A return code of -1 indicates an error, setting `err` to the + * cause. An err of CURLE_AGAIN is returned if the buffer queue is empty. + */ +ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, + CURLcode *err); + +CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len, + size_t *pnread); + +/** + * Peek at the head chunk in the buffer queue. Returns a pointer to + * the chunk buf (at the current offset) and its length. Does not + * modify the buffer queue. + * Returns TRUE iff bytes are available. Sets `pbuf` to NULL and `plen` + * to 0 when no bytes are available. + * Repeated calls return the same information until the buffer queue + * is modified, see `Curl_bufq_skip()`` + */ +bool Curl_bufq_peek(struct bufq *q, + const unsigned char **pbuf, size_t *plen); + +bool Curl_bufq_peek_at(struct bufq *q, size_t offset, + const unsigned char **pbuf, size_t *plen); + +/** + * Tell the buffer queue to discard `amount` buf bytes at the head + * of the queue. Skipping more buf than is currently buffered will + * just empty the queue. + */ +void Curl_bufq_skip(struct bufq *q, size_t amount); + +typedef ssize_t Curl_bufq_writer(void *writer_ctx, + const unsigned char *buf, size_t len, + CURLcode *err); +/** + * Passes the chunks in the buffer queue to the writer and returns + * the amount of buf written. A writer may return -1 and CURLE_AGAIN + * to indicate blocking at which point the queue will stop and return + * the amount of buf passed so far. + * -1 is returned on any other errors reported by the writer. + * Note that in case of a -1 chunks may have been written and + * the buffer queue will have different length than before. + */ +ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, + void *writer_ctx, CURLcode *err); + +typedef ssize_t Curl_bufq_reader(void *reader_ctx, + unsigned char *buf, size_t len, + CURLcode *err); + +/** + * Read date and append it to the end of the buffer queue until the + * reader returns blocking or the queue is full. A reader returns + * -1 and CURLE_AGAIN to indicate blocking. + * Returns the total amount of buf read (may be 0) or -1 on other + * reader errors. + * Note that in case of a -1 chunks may have been read and + * the buffer queue will have different length than before. + */ +ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, + void *reader_ctx, CURLcode *err); + +/** + * Read *once* up to `max_len` bytes and append it to the buffer. + * if `max_len` is 0, no limit is imposed besides the chunk space. + * Returns the total amount of buf read (may be 0) or -1 on other + * reader errors. + */ +ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len, + Curl_bufq_reader *reader, void *reader_ctx, + CURLcode *err); + +/** + * Write buf to the end of the buffer queue. + * Will write bufq content or passed `buf` directly using the `writer` + * callback when it sees fit. 'buf' might get passed directly + * on or is placed into the buffer, depending on `len` and current + * amount buffered, chunk size, etc. + */ +ssize_t Curl_bufq_write_pass(struct bufq *q, + const unsigned char *buf, size_t len, + Curl_bufq_writer *writer, void *writer_ctx, + CURLcode *err); + +#endif /* HEADER_CURL_BUFQ_H */ diff --git a/third_party/curl/lib/bufref.c b/third_party/curl/lib/bufref.c new file mode 100644 index 0000000000..f048b57011 --- /dev/null +++ b/third_party/curl/lib/bufref.c @@ -0,0 +1,127 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "urldata.h" +#include "bufref.h" +#include "strdup.h" + +#include "curl_memory.h" +#include "memdebug.h" + +#define SIGNATURE 0x5c48e9b2 /* Random pattern. */ + +/* + * Init a bufref struct. + */ +void Curl_bufref_init(struct bufref *br) +{ + DEBUGASSERT(br); + br->dtor = NULL; + br->ptr = NULL; + br->len = 0; + +#ifdef DEBUGBUILD + br->signature = SIGNATURE; +#endif +} + +/* + * Free the buffer and re-init the necessary fields. It does not touch the + * 'signature' field and thus this buffer reference can be reused. + */ + +void Curl_bufref_free(struct bufref *br) +{ + DEBUGASSERT(br); + DEBUGASSERT(br->signature == SIGNATURE); + DEBUGASSERT(br->ptr || !br->len); + + if(br->ptr && br->dtor) + br->dtor((void *) br->ptr); + + br->dtor = NULL; + br->ptr = NULL; + br->len = 0; +} + +/* + * Set the buffer reference to new values. The previously referenced buffer + * is released before assignment. + */ +void Curl_bufref_set(struct bufref *br, const void *ptr, size_t len, + void (*dtor)(void *)) +{ + DEBUGASSERT(ptr || !len); + DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH); + + Curl_bufref_free(br); + br->ptr = (const unsigned char *) ptr; + br->len = len; + br->dtor = dtor; +} + +/* + * Get a pointer to the referenced buffer. + */ +const unsigned char *Curl_bufref_ptr(const struct bufref *br) +{ + DEBUGASSERT(br); + DEBUGASSERT(br->signature == SIGNATURE); + DEBUGASSERT(br->ptr || !br->len); + + return br->ptr; +} + +/* + * Get the length of the referenced buffer data. + */ +size_t Curl_bufref_len(const struct bufref *br) +{ + DEBUGASSERT(br); + DEBUGASSERT(br->signature == SIGNATURE); + DEBUGASSERT(br->ptr || !br->len); + + return br->len; +} + +CURLcode Curl_bufref_memdup(struct bufref *br, const void *ptr, size_t len) +{ + unsigned char *cpy = NULL; + + DEBUGASSERT(br); + DEBUGASSERT(br->signature == SIGNATURE); + DEBUGASSERT(br->ptr || !br->len); + DEBUGASSERT(ptr || !len); + DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH); + + if(ptr) { + cpy = Curl_memdup0(ptr, len); + if(!cpy) + return CURLE_OUT_OF_MEMORY; + } + + Curl_bufref_set(br, cpy, len, curl_free); + return CURLE_OK; +} diff --git a/third_party/curl/lib/bufref.h b/third_party/curl/lib/bufref.h new file mode 100644 index 0000000000..dd424f18f5 --- /dev/null +++ b/third_party/curl/lib/bufref.h @@ -0,0 +1,48 @@ +#ifndef HEADER_CURL_BUFREF_H +#define HEADER_CURL_BUFREF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Generic buffer reference. + */ +struct bufref { + void (*dtor)(void *); /* Associated destructor. */ + const unsigned char *ptr; /* Referenced data buffer. */ + size_t len; /* The data size in bytes. */ +#ifdef DEBUGBUILD + int signature; /* Detect API use mistakes. */ +#endif +}; + + +void Curl_bufref_init(struct bufref *br); +void Curl_bufref_set(struct bufref *br, const void *ptr, size_t len, + void (*dtor)(void *)); +const unsigned char *Curl_bufref_ptr(const struct bufref *br); +size_t Curl_bufref_len(const struct bufref *br); +CURLcode Curl_bufref_memdup(struct bufref *br, const void *ptr, size_t len); +void Curl_bufref_free(struct bufref *br); + +#endif diff --git a/third_party/curl/lib/c-hyper.c b/third_party/curl/lib/c-hyper.c new file mode 100644 index 0000000000..3008d9a14d --- /dev/null +++ b/third_party/curl/lib/c-hyper.c @@ -0,0 +1,1229 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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 + * + ***************************************************************************/ + +/* Curl's integration with Hyper. This replaces certain functions in http.c, + * based on configuration #defines. This implementation supports HTTP/1.1 but + * not HTTP/2. + */ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include +#include "urldata.h" +#include "cfilters.h" +#include "sendf.h" +#include "headers.h" +#include "transfer.h" +#include "multiif.h" +#include "progress.h" +#include "content_encoding.h" +#include "ws.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +static CURLcode cr_hyper_add(struct Curl_easy *data); + +typedef enum { + USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */ + USERDATA_RESP_BODY +} userdata_t; + +size_t Curl_hyper_recv(void *userp, hyper_context *ctx, + uint8_t *buf, size_t buflen) +{ + struct hyp_io_ctx *io_ctx = userp; + struct Curl_easy *data = io_ctx->data; + struct connectdata *conn = data->conn; + CURLcode result; + ssize_t nread; + DEBUGASSERT(conn); + (void)ctx; + + DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen)); + result = Curl_conn_recv(data, io_ctx->sockindex, + (char *)buf, buflen, &nread); + if(result == CURLE_AGAIN) { + /* would block, register interest */ + DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen)); + if(data->hyp.read_waker) + hyper_waker_free(data->hyp.read_waker); + data->hyp.read_waker = hyper_context_waker(ctx); + if(!data->hyp.read_waker) { + failf(data, "Couldn't make the read hyper_context_waker"); + return HYPER_IO_ERROR; + } + return HYPER_IO_PENDING; + } + else if(result) { + failf(data, "Curl_read failed"); + return HYPER_IO_ERROR; + } + DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> %zd", buflen, nread)); + return (size_t)nread; +} + +size_t Curl_hyper_send(void *userp, hyper_context *ctx, + const uint8_t *buf, size_t buflen) +{ + struct hyp_io_ctx *io_ctx = userp; + struct Curl_easy *data = io_ctx->data; + CURLcode result; + size_t nwrote; + + DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen)); + result = Curl_conn_send(data, io_ctx->sockindex, + (void *)buf, buflen, &nwrote); + if(result == CURLE_AGAIN) { + DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen)); + /* would block, register interest */ + if(data->hyp.write_waker) + hyper_waker_free(data->hyp.write_waker); + data->hyp.write_waker = hyper_context_waker(ctx); + if(!data->hyp.write_waker) { + failf(data, "Couldn't make the write hyper_context_waker"); + return HYPER_IO_ERROR; + } + return HYPER_IO_PENDING; + } + else if(result) { + failf(data, "Curl_write failed"); + return HYPER_IO_ERROR; + } + DEBUGF(infof(data, "Curl_hyper_send(%zu) -> %zd", buflen, nwrote)); + return (size_t)nwrote; +} + +static int hyper_each_header(void *userdata, + const uint8_t *name, + size_t name_len, + const uint8_t *value, + size_t value_len) +{ + struct Curl_easy *data = (struct Curl_easy *)userdata; + size_t len; + char *headp; + CURLcode result; + int writetype; + + if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) { + failf(data, "Too long response header"); + data->state.hresult = CURLE_TOO_LARGE; + return HYPER_ITER_BREAK; + } + + Curl_dyn_reset(&data->state.headerb); + if(name_len) { + if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n", + (int) name_len, name, (int) value_len, value)) + return HYPER_ITER_BREAK; + } + else { + if(Curl_dyn_addn(&data->state.headerb, STRCONST("\r\n"))) + return HYPER_ITER_BREAK; + } + len = Curl_dyn_len(&data->state.headerb); + headp = Curl_dyn_ptr(&data->state.headerb); + + result = Curl_http_header(data, headp, len); + if(result) { + data->state.hresult = result; + return HYPER_ITER_BREAK; + } + + Curl_debug(data, CURLINFO_HEADER_IN, headp, len); + + writetype = CLIENTWRITE_HEADER; + if(data->state.hconnect) + writetype |= CLIENTWRITE_CONNECT; + if(data->req.httpcode/100 == 1) + writetype |= CLIENTWRITE_1XX; + result = Curl_client_write(data, writetype, headp, len); + if(result) { + data->state.hresult = CURLE_ABORTED_BY_CALLBACK; + return HYPER_ITER_BREAK; + } + + result = Curl_bump_headersize(data, len, FALSE); + if(result) { + data->state.hresult = result; + return HYPER_ITER_BREAK; + } + return HYPER_ITER_CONTINUE; +} + +static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) +{ + char *buf = (char *)hyper_buf_bytes(chunk); + size_t len = hyper_buf_len(chunk); + struct Curl_easy *data = (struct Curl_easy *)userdata; + struct SingleRequest *k = &data->req; + CURLcode result = CURLE_OK; + + if(!k->bodywritten) { +#if defined(USE_NTLM) + struct connectdata *conn = data->conn; + if(conn->bits.close && + (((data->req.httpcode == 401) && + (conn->http_ntlm_state == NTLMSTATE_TYPE2)) || + ((data->req.httpcode == 407) && + (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) { + infof(data, "Connection closed while negotiating NTLM"); + data->state.authproblem = TRUE; + Curl_safefree(data->req.newurl); + } +#endif + if(Curl_http_exp100_is_selected(data)) { + if(data->req.httpcode < 400) { + Curl_http_exp100_got100(data); + if(data->hyp.send_body_waker) { + hyper_waker_wake(data->hyp.send_body_waker); + data->hyp.send_body_waker = NULL; + } + } + else { /* >= 4xx */ + Curl_req_abort_sending(data); + } + } + if(data->state.hconnect && (data->req.httpcode/100 != 2) && + data->state.authproxy.done) { + data->req.done = TRUE; + result = CURLE_OK; + } + else + result = Curl_http_firstwrite(data); + if(result || data->req.done) { + infof(data, "Return early from hyper_body_chunk"); + data->state.hresult = result; + return HYPER_ITER_BREAK; + } + } + result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len); + + if(result) { + data->state.hresult = result; + return HYPER_ITER_BREAK; + } + + return HYPER_ITER_CONTINUE; +} + +/* + * Hyper does not consider the status line, the first line in an HTTP/1 + * response, to be a header. The libcurl API does. This function sends the + * status line in the header callback. */ +static CURLcode status_line(struct Curl_easy *data, + struct connectdata *conn, + uint16_t http_status, + int http_version, + const uint8_t *reason, size_t rlen) +{ + CURLcode result; + size_t len; + const char *vstr; + int writetype; + vstr = http_version == HYPER_HTTP_VERSION_1_1 ? "1.1" : + (http_version == HYPER_HTTP_VERSION_2 ? "2" : "1.0"); + + /* We need to set 'httpcodeq' for functions that check the response code in + a single place. */ + data->req.httpcode = http_status; + data->req.httpversion = http_version == HYPER_HTTP_VERSION_1_1? 11 : + (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10); + if(data->state.hconnect) + /* CONNECT */ + data->info.httpproxycode = http_status; + else { + conn->httpversion = (unsigned char)data->req.httpversion; + if(http_version == HYPER_HTTP_VERSION_1_0) + data->state.httpwant = CURL_HTTP_VERSION_1_0; + + result = Curl_http_statusline(data, conn); + if(result) + return result; + } + + Curl_dyn_reset(&data->state.headerb); + + result = Curl_dyn_addf(&data->state.headerb, "HTTP/%s %03d %.*s\r\n", + vstr, + (int)http_status, + (int)rlen, reason); + if(result) + return result; + len = Curl_dyn_len(&data->state.headerb); + Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb), + len); + + writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS; + if(data->state.hconnect) + writetype |= CLIENTWRITE_CONNECT; + result = Curl_client_write(data, writetype, + Curl_dyn_ptr(&data->state.headerb), len); + if(result) + return result; + + result = Curl_bump_headersize(data, len, FALSE); + return result; +} + +/* + * Hyper does not pass on the last empty response header. The libcurl API + * does. This function sends an empty header in the header callback. + */ +static CURLcode empty_header(struct Curl_easy *data) +{ + CURLcode result = Curl_http_size(data); + if(!result) { + result = hyper_each_header(data, NULL, 0, NULL, 0) ? + CURLE_WRITE_ERROR : CURLE_OK; + if(result) + failf(data, "hyperstream: could not pass blank header"); + /* Hyper does chunked decoding itself. If it was added during + * response header processing, remove it again. */ + Curl_cwriter_remove_by_name(data, "chunked"); + } + return result; +} + +CURLcode Curl_hyper_stream(struct Curl_easy *data, + struct connectdata *conn, + int *didwhat, + int select_res) +{ + hyper_response *resp = NULL; + uint16_t http_status; + int http_version; + hyper_headers *headers = NULL; + hyper_body *resp_body = NULL; + struct hyptransfer *h = &data->hyp; + hyper_task *task; + hyper_task *foreach; + const uint8_t *reasonp; + size_t reason_len; + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + (void)conn; + + if(data->hyp.send_body_waker) { + hyper_waker_wake(data->hyp.send_body_waker); + data->hyp.send_body_waker = NULL; + } + + if(select_res & CURL_CSELECT_IN) { + if(h->read_waker) + hyper_waker_wake(h->read_waker); + h->read_waker = NULL; + } + if(select_res & CURL_CSELECT_OUT) { + if(h->write_waker) + hyper_waker_wake(h->write_waker); + h->write_waker = NULL; + } + + do { + hyper_task_return_type t; + task = hyper_executor_poll(h->exec); + if(!task) { + *didwhat = KEEP_RECV; + break; + } + t = hyper_task_type(task); + if(t == HYPER_TASK_ERROR) { + hyper_error *hypererr = hyper_task_value(task); + hyper_task_free(task); + if(data->state.hresult) { + /* override Hyper's view, might not even be an error */ + result = data->state.hresult; + infof(data, "hyperstream is done (by early callback)"); + } + else { + uint8_t errbuf[256]; + size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); + hyper_code code = hyper_error_code(hypererr); + failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf); + switch(code) { + case HYPERE_ABORTED_BY_CALLBACK: + result = CURLE_OK; + break; + case HYPERE_UNEXPECTED_EOF: + if(!data->req.bytecount) + result = CURLE_GOT_NOTHING; + else + result = CURLE_RECV_ERROR; + break; + case HYPERE_INVALID_PEER_MESSAGE: + /* bump headerbytecount to avoid the count remaining at zero and + appearing to not having read anything from the peer at all */ + data->req.headerbytecount++; + result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */ + break; + default: + result = CURLE_RECV_ERROR; + break; + } + } + data->req.done = TRUE; + hyper_error_free(hypererr); + break; + } + else if(t == HYPER_TASK_EMPTY) { + void *userdata = hyper_task_userdata(task); + hyper_task_free(task); + if((userdata_t)userdata == USERDATA_RESP_BODY) { + /* end of transfer */ + data->req.done = TRUE; + infof(data, "hyperstream is done"); + if(!k->bodywritten) { + /* hyper does not always call the body write callback */ + result = Curl_http_firstwrite(data); + } + break; + } + else { + /* A background task for hyper; ignore */ + continue; + } + } + + DEBUGASSERT(HYPER_TASK_RESPONSE); + + resp = hyper_task_value(task); + hyper_task_free(task); + + *didwhat = KEEP_RECV; + if(!resp) { + failf(data, "hyperstream: could not get response"); + return CURLE_RECV_ERROR; + } + + http_status = hyper_response_status(resp); + http_version = hyper_response_version(resp); + reasonp = hyper_response_reason_phrase(resp); + reason_len = hyper_response_reason_phrase_len(resp); + + if(http_status == 417 && Curl_http_exp100_is_selected(data)) { + infof(data, "Got 417 while waiting for a 100"); + data->state.disableexpect = TRUE; + data->req.newurl = strdup(data->state.url); + Curl_req_abort_sending(data); + } + + result = status_line(data, conn, + http_status, http_version, reasonp, reason_len); + if(result) + break; + + headers = hyper_response_headers(resp); + if(!headers) { + failf(data, "hyperstream: could not get response headers"); + result = CURLE_RECV_ERROR; + break; + } + + /* the headers are already received */ + hyper_headers_foreach(headers, hyper_each_header, data); + if(data->state.hresult) { + result = data->state.hresult; + break; + } + + result = empty_header(data); + if(result) + break; + + k->deductheadercount = + (100 <= http_status && 199 >= http_status)?k->headerbytecount:0; +#ifdef USE_WEBSOCKETS + if(k->upgr101 == UPGR101_WS) { + if(http_status == 101) { + /* verify the response */ + result = Curl_ws_accept(data, NULL, 0); + if(result) + return result; + } + else { + failf(data, "Expected 101, got %u", k->httpcode); + result = CURLE_HTTP_RETURNED_ERROR; + break; + } + } +#endif + + /* Curl_http_auth_act() checks what authentication methods that are + * available and decides which one (if any) to use. It will set 'newurl' + * if an auth method was picked. */ + result = Curl_http_auth_act(data); + if(result) + break; + + resp_body = hyper_response_body(resp); + if(!resp_body) { + failf(data, "hyperstream: could not get response body"); + result = CURLE_RECV_ERROR; + break; + } + foreach = hyper_body_foreach(resp_body, hyper_body_chunk, data); + if(!foreach) { + failf(data, "hyperstream: body foreach failed"); + result = CURLE_OUT_OF_MEMORY; + break; + } + hyper_task_set_userdata(foreach, (void *)USERDATA_RESP_BODY); + if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) { + failf(data, "Couldn't hyper_executor_push the body-foreach"); + result = CURLE_OUT_OF_MEMORY; + break; + } + + hyper_response_free(resp); + resp = NULL; + } while(1); + if(resp) + hyper_response_free(resp); + return result; +} + +static CURLcode debug_request(struct Curl_easy *data, + const char *method, + const char *path) +{ + char *req = aprintf("%s %s HTTP/1.1\r\n", method, path); + if(!req) + return CURLE_OUT_OF_MEMORY; + Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req)); + free(req); + return CURLE_OK; +} + +/* + * Given a full header line "name: value" (optional CRLF in the input, should + * be in the output), add to Hyper and send to the debug callback. + * + * Supports multiple headers. + */ + +CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers, + const char *line) +{ + const char *p; + const char *n; + size_t nlen; + const char *v; + size_t vlen; + bool newline = TRUE; + int numh = 0; + + if(!line) + return CURLE_OK; + n = line; + do { + size_t linelen = 0; + + p = strchr(n, ':'); + if(!p) + /* this is fine if we already added at least one header */ + return numh ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; + nlen = p - n; + p++; /* move past the colon */ + while(*p == ' ') + p++; + v = p; + p = strchr(v, '\r'); + if(!p) { + p = strchr(v, '\n'); + if(p) + linelen = 1; /* LF only */ + else { + p = strchr(v, '\0'); + newline = FALSE; /* no newline */ + } + } + else + linelen = 2; /* CRLF ending */ + linelen += (p - n); + vlen = p - v; + + if(HYPERE_OK != hyper_headers_add(headers, (uint8_t *)n, nlen, + (uint8_t *)v, vlen)) { + failf(data, "hyper refused to add header '%s'", line); + return CURLE_OUT_OF_MEMORY; + } + if(data->set.verbose) { + char *ptr = NULL; + if(!newline) { + ptr = aprintf("%.*s\r\n", (int)linelen, line); + if(!ptr) + return CURLE_OUT_OF_MEMORY; + Curl_debug(data, CURLINFO_HEADER_OUT, ptr, linelen + 2); + free(ptr); + } + else + Curl_debug(data, CURLINFO_HEADER_OUT, (char *)n, linelen); + } + numh++; + n += linelen; + } while(newline); + return CURLE_OK; +} + +static CURLcode request_target(struct Curl_easy *data, + struct connectdata *conn, + const char *method, + hyper_request *req) +{ + CURLcode result; + struct dynbuf r; + + Curl_dyn_init(&r, DYN_HTTP_REQUEST); + + result = Curl_http_target(data, conn, &r); + if(result) + return result; + + if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r), + Curl_dyn_len(&r))) { + failf(data, "error setting uri to hyper"); + result = CURLE_OUT_OF_MEMORY; + } + else + result = debug_request(data, method, Curl_dyn_ptr(&r)); + + Curl_dyn_free(&r); + + return result; +} + +static int uploadstreamed(void *userdata, hyper_context *ctx, + hyper_buf **chunk) +{ + size_t fillcount; + struct Curl_easy *data = (struct Curl_easy *)userdata; + CURLcode result; + char *xfer_ulbuf; + size_t xfer_ulblen; + bool eos; + int rc = HYPER_POLL_ERROR; + (void)ctx; + + result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen); + if(result) + goto out; + + result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &fillcount, &eos); + if(result) + goto out; + + if(fillcount) { + hyper_buf *copy = hyper_buf_copy((uint8_t *)xfer_ulbuf, fillcount); + if(copy) + *chunk = copy; + else { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + /* increasing the writebytecount here is a little premature but we + do not know exactly when the body is sent */ + data->req.writebytecount += fillcount; + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); + rc = HYPER_POLL_READY; + } + else if(eos) { + *chunk = NULL; + rc = HYPER_POLL_READY; + } + else { + /* paused, save a waker */ + if(data->hyp.send_body_waker) + hyper_waker_free(data->hyp.send_body_waker); + data->hyp.send_body_waker = hyper_context_waker(ctx); + rc = HYPER_POLL_PENDING; + } + +out: + Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf); + data->state.hresult = result; + return rc; +} + +/* + * finalize_request() sets up last headers and optional body settings + */ +static CURLcode finalize_request(struct Curl_easy *data, + hyper_headers *headers, + hyper_request *hyperreq, + Curl_HttpReq httpreq) +{ + CURLcode result = CURLE_OK; + struct dynbuf req; + if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) + Curl_pgrsSetUploadSize(data, 0); /* no request body */ + else { + hyper_body *body; + Curl_dyn_init(&req, DYN_HTTP_REQUEST); + result = Curl_http_req_complete(data, &req, httpreq); + if(result) + return result; + + /* if the "complete" above did produce more than the closing line, + parse the added headers */ + if(Curl_dyn_len(&req) != 2 || strcmp(Curl_dyn_ptr(&req), "\r\n")) { + result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req)); + if(result) + return result; + } + + Curl_dyn_free(&req); + + body = hyper_body_new(); + hyper_body_set_userdata(body, data); + hyper_body_set_data_func(body, uploadstreamed); + + if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) { + /* fail */ + result = CURLE_OUT_OF_MEMORY; + } + } + + return cr_hyper_add(data); +} + +static CURLcode cookies(struct Curl_easy *data, + struct connectdata *conn, + hyper_headers *headers) +{ + struct dynbuf req; + CURLcode result; + Curl_dyn_init(&req, DYN_HTTP_REQUEST); + + result = Curl_http_cookies(data, conn, &req); + if(!result) + result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req)); + Curl_dyn_free(&req); + return result; +} + +/* called on 1xx responses */ +static void http1xx_cb(void *arg, struct hyper_response *resp) +{ + struct Curl_easy *data = (struct Curl_easy *)arg; + hyper_headers *headers = NULL; + CURLcode result = CURLE_OK; + uint16_t http_status; + int http_version; + const uint8_t *reasonp; + size_t reason_len; + + infof(data, "Got HTTP 1xx informational"); + + http_status = hyper_response_status(resp); + http_version = hyper_response_version(resp); + reasonp = hyper_response_reason_phrase(resp); + reason_len = hyper_response_reason_phrase_len(resp); + + result = status_line(data, data->conn, + http_status, http_version, reasonp, reason_len); + if(!result) { + headers = hyper_response_headers(resp); + if(!headers) { + failf(data, "hyperstream: could not get 1xx response headers"); + result = CURLE_RECV_ERROR; + } + } + data->state.hresult = result; + + if(!result) { + /* the headers are already received */ + hyper_headers_foreach(headers, hyper_each_header, data); + /* this callback also sets data->state.hresult on error */ + + if(empty_header(data)) + result = CURLE_OUT_OF_MEMORY; + } + + if(data->state.hresult) + infof(data, "ERROR in 1xx, bail out"); +} + +/* + * Curl_http() gets called from the generic multi_do() function when an HTTP + * request is to be performed. This creates and sends a properly constructed + * HTTP request. + */ +CURLcode Curl_http(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct hyptransfer *h = &data->hyp; + hyper_io *io = NULL; + hyper_clientconn_options *options = NULL; + hyper_task *task = NULL; /* for the handshake */ + hyper_task *sendtask = NULL; /* for the send */ + hyper_clientconn *client = NULL; + hyper_request *req = NULL; + hyper_headers *headers = NULL; + hyper_task *handshake = NULL; + CURLcode result; + const char *p_accept; /* Accept: string */ + const char *method; + Curl_HttpReq httpreq; + const char *te = NULL; /* transfer-encoding */ + hyper_code rc; + + /* Always consider the DO phase done after this function call, even if there + may be parts of the request that is not yet sent, since we can deal with + the rest of the request in the PERFORM phase. */ + *done = TRUE; + result = Curl_client_start(data); + if(result) + return result; + + /* Add collecting of headers written to client. For a new connection, + * we might have done that already, but reuse + * or multiplex needs it here as well. */ + result = Curl_headers_init(data); + if(result) + return result; + + infof(data, "Time for the Hyper dance"); + memset(h, 0, sizeof(struct hyptransfer)); + + result = Curl_http_host(data, conn); + if(result) + return result; + + Curl_http_method(data, conn, &method, &httpreq); + + DEBUGASSERT(data->req.bytecount == 0); + + /* setup the authentication headers */ + { + char *pq = NULL; + if(data->state.up.query) { + pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); + if(!pq) + return CURLE_OUT_OF_MEMORY; + } + result = Curl_http_output_auth(data, conn, method, httpreq, + (pq ? pq : data->state.up.path), FALSE); + free(pq); + if(result) + return result; + } + + result = Curl_http_req_set_reader(data, httpreq, &te); + if(result) + goto error; + + result = Curl_http_range(data, httpreq); + if(result) + return result; + + result = Curl_http_useragent(data); + if(result) + return result; + + 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 */ + h->io_ctx.data = data; + h->io_ctx.sockindex = FIRSTSOCKET; + hyper_io_set_userdata(io, &h->io_ctx); + hyper_io_set_read(io, Curl_hyper_recv); + hyper_io_set_write(io, Curl_hyper_send); + + /* 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; + } + } + + options = hyper_clientconn_options_new(); + if(!options) { + failf(data, "Couldn't create hyper client options"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + if(conn->alpn == CURL_HTTP_VERSION_2) { + failf(data, "ALPN protocol h2 not supported with Hyper"); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto error; + } + hyper_clientconn_options_set_preserve_header_case(options, 1); + hyper_clientconn_options_set_preserve_header_order(options, 1); + hyper_clientconn_options_http1_allow_multiline_headers(options, 1); + + hyper_clientconn_options_exec(options, h->exec); + + /* "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; + + 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 */ + + task = hyper_executor_poll(h->exec); + if(!task) { + failf(data, "Couldn't hyper_executor_poll the handshake"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + 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(!Curl_use_http_1_1plus(data, conn)) { + if(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(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) { + failf(data, "error setting method"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + result = request_target(data, conn, method, req); + if(result) + goto error; + + headers = hyper_request_headers(req); + if(!headers) { + failf(data, "hyper_request_headers"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + rc = hyper_request_on_informational(req, http1xx_cb, data); + if(rc) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + if(data->state.aptr.host) { + result = Curl_hyper_header(data, headers, data->state.aptr.host); + if(result) + goto error; + } + +#ifndef CURL_DISABLE_PROXY + if(data->state.aptr.proxyuserpwd) { + result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd); + if(result) + goto error; + } +#endif + + if(data->state.aptr.userpwd) { + result = Curl_hyper_header(data, headers, data->state.aptr.userpwd); + if(result) + goto error; + } + + if((data->state.use_range && data->state.aptr.rangeline)) { + result = Curl_hyper_header(data, headers, data->state.aptr.rangeline); + if(result) + goto error; + } + + if(data->set.str[STRING_USERAGENT] && + *data->set.str[STRING_USERAGENT] && + data->state.aptr.uagent) { + result = Curl_hyper_header(data, headers, data->state.aptr.uagent); + if(result) + goto error; + } + + p_accept = Curl_checkheaders(data, + STRCONST("Accept"))?NULL:"Accept: */*\r\n"; + if(p_accept) { + result = Curl_hyper_header(data, headers, p_accept); + if(result) + goto error; + } + if(te) { + result = Curl_hyper_header(data, headers, te); + if(result) + goto error; + } + +#ifndef CURL_DISABLE_ALTSVC + if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) { + char *altused = aprintf("Alt-Used: %s:%d\r\n", + conn->conn_to_host.name, conn->conn_to_port); + if(!altused) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + result = Curl_hyper_header(data, headers, altused); + if(result) + goto error; + free(altused); + } +#endif + +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy && + !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && + !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { + result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"); + if(result) + goto error; + } +#endif + + Curl_safefree(data->state.aptr.ref); + if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) { + data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); + if(!data->state.aptr.ref) + result = CURLE_OUT_OF_MEMORY; + else + result = Curl_hyper_header(data, headers, data->state.aptr.ref); + if(result) + goto error; + } + +#ifdef HAVE_LIBZ + /* we only consider transfer-encoding magic if libz support is built-in */ + result = Curl_transferencode(data); + if(result) + goto error; + result = Curl_hyper_header(data, headers, data->state.aptr.te); + if(result) + goto error; +#endif + + if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && + data->set.str[STRING_ENCODING]) { + Curl_safefree(data->state.aptr.accept_encoding); + data->state.aptr.accept_encoding = + aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); + if(!data->state.aptr.accept_encoding) + result = CURLE_OUT_OF_MEMORY; + else + result = Curl_hyper_header(data, headers, + data->state.aptr.accept_encoding); + if(result) + goto error; + } + else + Curl_safefree(data->state.aptr.accept_encoding); + + result = cookies(data, conn, headers); + if(result) + goto error; + + if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) + result = Curl_ws_request(data, headers); + + result = Curl_add_timecondition(data, headers); + if(result) + goto error; + + result = Curl_add_custom_headers(data, FALSE, headers); + if(result) + goto error; + + result = finalize_request(data, headers, req, httpreq); + if(result) + goto error; + + Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2); + + if(data->req.upload_chunky && data->req.authneg) { + data->req.upload_chunky = TRUE; + } + else { + data->req.upload_chunky = FALSE; + } + sendtask = hyper_clientconn_send(client, req); + if(!sendtask) { + failf(data, "hyper_clientconn_send"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + req = NULL; + + if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { + failf(data, "Couldn't hyper_executor_push the send"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + sendtask = NULL; /* ownership passed on */ + + hyper_clientconn_free(client); + client = NULL; + + if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) { + /* HTTP GET/HEAD download */ + Curl_pgrsSetUploadSize(data, 0); /* nothing */ + } + + Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE); + conn->datastream = Curl_hyper_stream; + + /* clear userpwd and proxyuserpwd to avoid reusing old credentials + * from reused connections */ + Curl_safefree(data->state.aptr.userpwd); +#ifndef CURL_DISABLE_PROXY + Curl_safefree(data->state.aptr.proxyuserpwd); +#endif + return CURLE_OK; +error: + DEBUGASSERT(result); + 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); + + if(req) + hyper_request_free(req); + + return result; +} + +void Curl_hyper_done(struct Curl_easy *data) +{ + struct hyptransfer *h = &data->hyp; + 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; + } + if(h->send_body_waker) { + hyper_waker_free(h->send_body_waker); + h->send_body_waker = NULL; + } +} + +static CURLcode cr_hyper_unpause(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)reader; + if(data->hyp.send_body_waker) { + hyper_waker_wake(data->hyp.send_body_waker); + data->hyp.send_body_waker = NULL; + } + return CURLE_OK; +} + +/* Hyper client reader, handling unpausing */ +static const struct Curl_crtype cr_hyper_protocol = { + "cr-hyper", + Curl_creader_def_init, + Curl_creader_def_read, + Curl_creader_def_close, + Curl_creader_def_needs_rewind, + Curl_creader_def_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_rewind, + cr_hyper_unpause, + Curl_creader_def_is_paused, + Curl_creader_def_done, + sizeof(struct Curl_creader) +}; + +static CURLcode cr_hyper_add(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &cr_hyper_protocol, + CURL_CR_PROTOCOL); + if(!result) + result = Curl_creader_add(data, reader); + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + +#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */ diff --git a/third_party/curl/lib/c-hyper.h b/third_party/curl/lib/c-hyper.h new file mode 100644 index 0000000000..89dd53b8fd --- /dev/null +++ b/third_party/curl/lib/c-hyper.h @@ -0,0 +1,63 @@ +#ifndef HEADER_CURL_HYPER_H +#define HEADER_CURL_HYPER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) + +#include + +struct hyp_io_ctx { + struct Curl_easy *data; + int sockindex; +}; + +/* per-transfer data for the Hyper backend */ +struct hyptransfer { + hyper_waker *write_waker; + hyper_waker *read_waker; + const hyper_executor *exec; + hyper_waker *send_body_waker; + struct hyp_io_ctx io_ctx; +}; + +size_t Curl_hyper_recv(void *userp, hyper_context *ctx, + uint8_t *buf, size_t buflen); +size_t Curl_hyper_send(void *userp, hyper_context *ctx, + const uint8_t *buf, size_t buflen); +CURLcode Curl_hyper_stream(struct Curl_easy *data, + struct connectdata *conn, + int *didwhat, + int select_res); + +CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers, + const char *line); +void Curl_hyper_done(struct Curl_easy *); + +#else +#define Curl_hyper_done(x) + +#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */ +#endif /* HEADER_CURL_HYPER_H */ diff --git a/third_party/curl/lib/cf-h1-proxy.c b/third_party/curl/lib/cf-h1-proxy.c new file mode 100644 index 0000000000..6656b4ddcd --- /dev/null +++ b/third_party/curl/lib/cf-h1-proxy.c @@ -0,0 +1,1104 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include +#ifdef USE_HYPER +#include +#endif +#include "urldata.h" +#include "dynbuf.h" +#include "sendf.h" +#include "http.h" +#include "http1.h" +#include "http_proxy.h" +#include "url.h" +#include "select.h" +#include "progress.h" +#include "cfilters.h" +#include "cf-h1-proxy.h" +#include "connect.h" +#include "curl_trc.h" +#include "curlx.h" +#include "vtls/vtls.h" +#include "transfer.h" +#include "multiif.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +typedef enum { + H1_TUNNEL_INIT, /* init/default/no tunnel state */ + H1_TUNNEL_CONNECT, /* CONNECT request is being send */ + H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */ + H1_TUNNEL_RESPONSE, /* CONNECT response received completely */ + H1_TUNNEL_ESTABLISHED, + H1_TUNNEL_FAILED +} h1_tunnel_state; + +/* struct for HTTP CONNECT tunneling */ +struct h1_tunnel_state { + struct dynbuf rcvbuf; + struct dynbuf request_data; + size_t nsent; + size_t headerlines; + struct Curl_chunker ch; + enum keeponval { + KEEPON_DONE, + KEEPON_CONNECT, + KEEPON_IGNORE + } keepon; + curl_off_t cl; /* size of content to read and ignore */ + h1_tunnel_state tunnel_state; + BIT(chunked_encoding); + BIT(close_connection); +}; + + +static bool tunnel_is_established(struct h1_tunnel_state *ts) +{ + return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED); +} + +static bool tunnel_is_failed(struct h1_tunnel_state *ts) +{ + return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); +} + +static CURLcode tunnel_reinit(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts) +{ + (void)data; + (void)cf; + DEBUGASSERT(ts); + Curl_dyn_reset(&ts->rcvbuf); + Curl_dyn_reset(&ts->request_data); + ts->tunnel_state = H1_TUNNEL_INIT; + ts->keepon = KEEPON_CONNECT; + ts->cl = 0; + ts->close_connection = FALSE; + return CURLE_OK; +} + +static CURLcode tunnel_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state **pts) +{ + struct h1_tunnel_state *ts; + + if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) { + failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme); + return CURLE_UNSUPPORTED_PROTOCOL; + } + + ts = calloc(1, sizeof(*ts)); + if(!ts) + return CURLE_OUT_OF_MEMORY; + + infof(data, "allocate connect buffer"); + + Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); + Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); + Curl_httpchunk_init(data, &ts->ch, TRUE); + + *pts = ts; + connkeep(cf->conn, "HTTP proxy CONNECT"); + return tunnel_reinit(cf, data, ts); +} + +static void h1_tunnel_go_state(struct Curl_cfilter *cf, + struct h1_tunnel_state *ts, + h1_tunnel_state new_state, + struct Curl_easy *data) +{ + if(ts->tunnel_state == new_state) + return; + /* entering this one */ + switch(new_state) { + case H1_TUNNEL_INIT: + CURL_TRC_CF(data, cf, "new tunnel state 'init'"); + tunnel_reinit(cf, data, ts); + break; + + case H1_TUNNEL_CONNECT: + CURL_TRC_CF(data, cf, "new tunnel state 'connect'"); + ts->tunnel_state = H1_TUNNEL_CONNECT; + ts->keepon = KEEPON_CONNECT; + Curl_dyn_reset(&ts->rcvbuf); + break; + + case H1_TUNNEL_RECEIVE: + CURL_TRC_CF(data, cf, "new tunnel state 'receive'"); + ts->tunnel_state = H1_TUNNEL_RECEIVE; + break; + + case H1_TUNNEL_RESPONSE: + CURL_TRC_CF(data, cf, "new tunnel state 'response'"); + ts->tunnel_state = H1_TUNNEL_RESPONSE; + break; + + case H1_TUNNEL_ESTABLISHED: + CURL_TRC_CF(data, cf, "new tunnel state 'established'"); + infof(data, "CONNECT phase completed"); + data->state.authproxy.done = TRUE; + data->state.authproxy.multipass = FALSE; + FALLTHROUGH(); + case H1_TUNNEL_FAILED: + if(new_state == H1_TUNNEL_FAILED) + CURL_TRC_CF(data, cf, "new tunnel state 'failed'"); + ts->tunnel_state = new_state; + Curl_dyn_reset(&ts->rcvbuf); + Curl_dyn_reset(&ts->request_data); + /* restore the protocol pointer */ + data->info.httpcode = 0; /* clear it as it might've been used for the + proxy */ + /* If a proxy-authorization header was used for the proxy, then we should + make sure that it is not accidentally used for the document request + after we have connected. So let's free and clear it here. */ + Curl_safefree(data->state.aptr.proxyuserpwd); +#ifdef USE_HYPER + data->state.hconnect = FALSE; +#endif + break; + } +} + +static void tunnel_free(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + if(cf) { + struct h1_tunnel_state *ts = cf->ctx; + if(ts) { + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + Curl_dyn_free(&ts->rcvbuf); + Curl_dyn_free(&ts->request_data); + Curl_httpchunk_free(data, &ts->ch); + free(ts); + cf->ctx = NULL; + } + } +} + +static bool tunnel_want_send(struct h1_tunnel_state *ts) +{ + return (ts->tunnel_state == H1_TUNNEL_CONNECT); +} + +#ifndef USE_HYPER +static CURLcode start_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts) +{ + struct httpreq *req = NULL; + int http_minor; + CURLcode result; + + /* This only happens if we have looped here due to authentication + reasons, and we do not really use the newly cloned URL here + then. Just free() it. */ + Curl_safefree(data->req.newurl); + + result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); + if(result) + goto out; + + infof(data, "Establish HTTP proxy tunnel to %s", req->authority); + + Curl_dyn_reset(&ts->request_data); + ts->nsent = 0; + ts->headerlines = 0; + http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1; + + result = Curl_h1_req_write_head(req, http_minor, &ts->request_data); + if(!result) + result = Curl_creader_set_null(data); + +out: + if(result) + failf(data, "Failed sending CONNECT to proxy"); + if(req) + Curl_http_req_free(req); + return result; +} + +static CURLcode send_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts, + bool *done) +{ + char *buf = Curl_dyn_ptr(&ts->request_data); + size_t request_len = Curl_dyn_len(&ts->request_data); + size_t blen = request_len; + CURLcode result = CURLE_OK; + ssize_t nwritten; + + if(blen <= ts->nsent) + goto out; /* we are done */ + + blen -= ts->nsent; + buf += ts->nsent; + + nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result); + if(nwritten < 0) { + if(result == CURLE_AGAIN) { + result = CURLE_OK; + } + goto out; + } + + DEBUGASSERT(blen >= (size_t)nwritten); + ts->nsent += (size_t)nwritten; + Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten); + +out: + if(result) + failf(data, "Failed sending CONNECT to proxy"); + *done = (!result && (ts->nsent >= request_len)); + return result; +} + +static CURLcode on_resp_header(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts, + const char *header) +{ + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + (void)cf; + + if((checkprefix("WWW-Authenticate:", header) && + (401 == k->httpcode)) || + (checkprefix("Proxy-authenticate:", header) && + (407 == k->httpcode))) { + + bool proxy = (k->httpcode == 407) ? TRUE : FALSE; + char *auth = Curl_copy_header_value(header); + if(!auth) + return CURLE_OUT_OF_MEMORY; + + CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header); + result = Curl_http_input_auth(data, proxy, auth); + + free(auth); + + 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; + /* reset our chunky engine */ + Curl_httpchunk_reset(data, &ts->ch, TRUE); + } + } + else if(Curl_compareheader(header, + STRCONST("Proxy-Connection:"), + STRCONST("close"))) + ts->close_connection = TRUE; + else if(!strncmp(header, "HTTP/1.", 7) && + ((header[7] == '0') || (header[7] == '1')) && + (header[8] == ' ') && + ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && + !ISDIGIT(header[12])) { + /* store the HTTP code from the proxy */ + data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 + + (header[10] - '0') * 10 + (header[11] - '0'); + } + return result; +} + +static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts, + bool *done) +{ + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + char *linep; + size_t line_len; + int error, writetype; + +#define SELECT_OK 0 +#define SELECT_ERROR 1 + + error = SELECT_OK; + *done = FALSE; + + if(!Curl_conn_data_pending(data, cf->sockindex)) + return CURLE_OK; + + while(ts->keepon) { + ssize_t nread; + 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_conn_recv(data, cf->sockindex, &byte, 1, &nread); + if(result == CURLE_AGAIN) + /* socket buffer drained, return */ + return CURLE_OK; + + if(Curl_pgrsUpdate(data)) + return CURLE_ABORTED_BY_CALLBACK; + + if(result) { + ts->keepon = KEEPON_DONE; + break; + } + + if(nread <= 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"); + } + else { + error = SELECT_ERROR; + failf(data, "Proxy CONNECT aborted"); + } + ts->keepon = KEEPON_DONE; + break; + } + + if(ts->keepon == KEEPON_IGNORE) { + /* This means we are currently ignoring a response-body */ + + if(ts->cl) { + /* A Content-Length based body: simply count down the counter + and make sure to break out of the loop when we are done! */ + ts->cl--; + if(ts->cl <= 0) { + ts->keepon = KEEPON_DONE; + break; + } + } + else if(ts->chunked_encoding) { + /* chunked-encoded body, so we need to do the chunked dance + properly to know when the end of the body is reached */ + size_t consumed = 0; + + /* now parse the chunked piece of data so that we can + properly tell when the stream ends */ + result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed); + if(result) + return result; + if(Curl_httpchunk_is_done(data, &ts->ch)) { + /* we are done reading chunks! */ + infof(data, "chunk reading DONE"); + ts->keepon = KEEPON_DONE; + } + } + continue; + } + + if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) { + failf(data, "CONNECT response too large"); + return CURLE_RECV_ERROR; + } + + /* if this is not the end of a header line then continue */ + if(byte != 0x0a) + continue; + + ts->headerlines++; + linep = Curl_dyn_ptr(&ts->rcvbuf); + line_len = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */ + + /* output debug if that is requested */ + Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len); + + /* send the header to the callback */ + writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | + (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); + result = Curl_client_write(data, writetype, linep, line_len); + if(result) + return result; + + result = Curl_bump_headersize(data, line_len, TRUE); + if(result) + return result; + + /* Newlines are CRLF, so the CR is ignored as the line is not + 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 */ + ts->keepon = KEEPON_IGNORE; + + if(ts->cl) { + infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T + " bytes of response-body", ts->cl); + } + else if(ts->chunked_encoding) { + infof(data, "Ignore chunked response-body"); + } + else { + /* without content-length or chunked encoding, we + cannot keep the connection alive since the close is + the end signal so we bail out at once instead */ + CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked"); + ts->keepon = KEEPON_DONE; + } + } + else { + ts->keepon = KEEPON_DONE; + } + + DEBUGASSERT(ts->keepon == KEEPON_IGNORE + || ts->keepon == KEEPON_DONE); + continue; + } + + result = on_resp_header(cf, data, ts, linep); + if(result) + return result; + + Curl_dyn_reset(&ts->rcvbuf); + } /* while there is buffer left and loop is requested */ + + if(error) + result = CURLE_RECV_ERROR; + *done = (ts->keepon == KEEPON_DONE); + if(!result && *done && data->info.httpproxycode/100 != 2) { + /* 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); + } + return result; +} + +#else /* USE_HYPER */ + +static CURLcode CONNECT_host(struct Curl_cfilter *cf, + struct Curl_easy *data, + char **pauthority, + char **phost_header) +{ + const char *hostname; + int port; + bool ipv6_ip; + CURLcode result; + char *authority; /* for CONNECT, the destination host + port */ + char *host_header = NULL; /* Host: authority */ + + result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); + if(result) + return result; + + authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", + port); + if(!authority) + return CURLE_OUT_OF_MEMORY; + + /* If user is not overriding the Host header later */ + if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { + host_header = aprintf("Host: %s\r\n", authority); + if(!host_header) { + free(authority); + return CURLE_OUT_OF_MEMORY; + } + } + *pauthority = authority; + *phost_header = host_header; + return CURLE_OK; +} + +/* The Hyper version of CONNECT */ +static CURLcode start_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts) +{ + struct connectdata *conn = cf->conn; + struct hyptransfer *h = &data->hyp; + curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); + 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_clientconn *client = NULL; + hyper_task *sendtask = NULL; /* for the send */ + char *authority = NULL; /* for CONNECT */ + char *host_header = NULL; /* Host: */ + CURLcode result = CURLE_OUT_OF_MEMORY; + (void)ts; + + 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 */ + h->io_ctx.data = data; + h->io_ctx.sockindex = cf->sockindex; + hyper_io_set_userdata(io, &h->io_ctx); + 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; + } + } + + options = hyper_clientconn_options_new(); + if(!options) { + failf(data, "Couldn't create hyper client options"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + hyper_clientconn_options_set_preserve_header_case(options, 1); + hyper_clientconn_options_set_preserve_header_order(options, 1); + + hyper_clientconn_options_exec(options, h->exec); + + /* "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; + + 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 */ + + task = hyper_executor_poll(h->exec); + if(!task) { + failf(data, "Couldn't hyper_executor_poll the handshake"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + 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 have looped here due to authentication + reasons, and we do not really use the newly cloned URL here + then. Just free() it. */ + Curl_safefree(data->req.newurl); + + result = CONNECT_host(cf, data, &authority, &host_header); + if(result) + goto error; + + infof(data, "Establish HTTP proxy tunnel to %s", authority); + + if(hyper_request_set_uri(req, (uint8_t *)authority, + strlen(authority))) { + 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", authority); + 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, + authority, TRUE); + if(result) + goto error; + Curl_safefree(authority); + + /* 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; + } + + headers = hyper_request_headers(req); + if(!headers) { + failf(data, "hyper_request_headers"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + if(host_header) { + result = Curl_hyper_header(data, headers, host_header); + if(result) + goto error; + Curl_safefree(host_header); + } + + if(data->state.aptr.proxyuserpwd) { + result = Curl_hyper_header(data, headers, + data->state.aptr.proxyuserpwd); + if(result) + goto error; + } + + if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && + data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { + struct dynbuf ua; + Curl_dyn_init(&ua, DYN_HTTP_REQUEST); + result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n", + 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; + + result = Curl_creader_set_null(data); + if(result) + goto error; + + sendtask = hyper_clientconn_send(client, req); + if(!sendtask) { + failf(data, "hyper_clientconn_send"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + req = NULL; + + if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { + failf(data, "Couldn't hyper_executor_push the send"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + sendtask = NULL; /* ownership passed on */ + + hyper_clientconn_free(client); + client = NULL; + +error: + free(host_header); + free(authority); + 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); + if(req) + hyper_request_free(req); + + return result; +} + +static CURLcode send_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts, + bool *done) +{ + struct hyptransfer *h = &data->hyp; + struct connectdata *conn = cf->conn; + 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; + } + } + } 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; +} + +static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts, + bool *done) +{ + struct hyptransfer *h = &data->hyp; + CURLcode result; + int didwhat; + + (void)ts; + result = Curl_hyper_stream(data, cf->conn, &didwhat, + CURL_CSELECT_IN | CURL_CSELECT_OUT); + *done = data->req.done; + 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; +} + +#endif /* USE_HYPER */ + +static CURLcode H1_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_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; + } + + switch(ts->tunnel_state) { + case H1_TUNNEL_INIT: + /* Prepare the CONNECT request and make a first attempt to send. */ + CURL_TRC_CF(data, cf, "CONNECT start"); + result = start_CONNECT(cf, data, ts); + if(result) + goto out; + h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data); + FALLTHROUGH(); + + case H1_TUNNEL_CONNECT: + /* see that the request is completely sent */ + CURL_TRC_CF(data, cf, "CONNECT send"); + result = send_CONNECT(cf, data, ts, &done); + if(result || !done) + goto out; + h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data); + FALLTHROUGH(); + + case H1_TUNNEL_RECEIVE: + /* read what is there */ + CURL_TRC_CF(data, cf, "CONNECT receive"); + result = recv_CONNECT_resp(cf, data, 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 */ + h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data); + FALLTHROUGH(); + + case H1_TUNNEL_RESPONSE: + CURL_TRC_CF(data, cf, "CONNECT response"); + if(data->req.newurl) { + /* not the "final" response, we need to do a follow up request. + * If the other side indicated a connection close, or if someone + * else told us to close this connection, do so now. + */ + Curl_req_soft_reset(&data->req, data); + if(ts->close_connection || conn->bits.close) { + /* Close this filter and the sub-chain, re-connect the + * sub-chain and continue. Closing this filter will + * reset our tunnel state. To avoid recursion, we return + * and expect to be called again. + */ + CURL_TRC_CF(data, cf, "CONNECT need to close+open"); + infof(data, "Connect me again please"); + Curl_conn_cf_close(cf, data); + connkeep(conn, "HTTP proxy CONNECT"); + result = Curl_conn_cf_connect(cf->next, data, FALSE, &done); + goto out; + } + else { + /* staying on this connection, reset state */ + h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data); + } + } + break; + + default: + break; + } + + } while(data->req.newurl); + + DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); + if(data->info.httpproxycode/100 != 2) { + /* a non-2xx response and we have no next URL to try. */ + Curl_safefree(data->req.newurl); + /* failure, close this connection to avoid reuse */ + streamclose(conn, "proxy CONNECT failure"); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); + return CURLE_RECV_ERROR; + } + /* 2xx response, SUCCESS! */ + h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); + infof(data, "CONNECT tunnel established, response %d", + data->info.httpproxycode); + result = CURLE_OK; + +out: + if(result) + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + return result; +} + +static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + CURLcode result; + struct h1_tunnel_state *ts = cf->ctx; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + CURL_TRC_CF(data, cf, "connect"); + result = cf->next->cft->do_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + + *done = FALSE; + if(!ts) { + result = tunnel_init(cf, data, &ts); + if(result) + return result; + cf->ctx = ts; + } + + /* TODO: can we do blocking? */ + /* We want "seamless" operations through HTTP proxy tunnel */ + + result = H1_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; + /* The real request will follow the CONNECT, reset request partially */ + Curl_req_soft_reset(&data->req, data); + Curl_client_reset(data); + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + + tunnel_free(cf, data); + } + return result; +} + +static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct h1_tunnel_state *ts = cf->ctx; + + if(!cf->connected) { + /* If we are not connected, but the filter "below" is + * and not waiting on something, we are tunneling. */ + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + if(ts) { + /* when we have 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 are still sending the request, wait + for write. */ + if(tunnel_want_send(ts)) + Curl_pollset_set_out_only(data, ps, sock); + else + Curl_pollset_set_in_only(data, ps, sock); + } + else + Curl_pollset_set_out_only(data, ps, sock); + } +} + +static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURL_TRC_CF(data, cf, "destroy"); + tunnel_free(cf, data); +} + +static void cf_h1_proxy_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURL_TRC_CF(data, cf, "close"); + if(cf) { + cf->connected = FALSE; + if(cf->ctx) { + h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); + } + if(cf->next) + cf->next->cft->do_close(cf->next, data); + } +} + + +struct Curl_cftype Curl_cft_h1_proxy = { + "H1-PROXY", + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, + 0, + cf_h1_proxy_destroy, + cf_h1_proxy_connect, + cf_h1_proxy_close, + Curl_cf_def_shutdown, + Curl_cf_http_proxy_get_host, + cf_h1_proxy_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + (void)data; + result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); + return result; +} + +#endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */ diff --git a/third_party/curl/lib/cf-h1-proxy.h b/third_party/curl/lib/cf-h1-proxy.h new file mode 100644 index 0000000000..ac5bed0b2b --- /dev/null +++ b/third_party/curl/lib/cf-h1-proxy.h @@ -0,0 +1,39 @@ +#ifndef HEADER_CURL_H1_PROXY_H +#define HEADER_CURL_H1_PROXY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_h1_proxy; + + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + +#endif /* HEADER_CURL_H1_PROXY_H */ diff --git a/third_party/curl/lib/cf-h2-proxy.c b/third_party/curl/lib/cf-h2-proxy.c new file mode 100644 index 0000000000..7613b7828d --- /dev/null +++ b/third_party/curl/lib/cf-h2-proxy.c @@ -0,0 +1,1636 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) + +#include +#include "urldata.h" +#include "cfilters.h" +#include "connect.h" +#include "curl_trc.h" +#include "bufq.h" +#include "dynbuf.h" +#include "dynhds.h" +#include "http1.h" +#include "http2.h" +#include "http_proxy.h" +#include "multiif.h" +#include "sendf.h" +#include "cf-h2-proxy.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define PROXY_H2_CHUNK_SIZE (16*1024) + +#define PROXY_HTTP2_HUGE_WINDOW_SIZE (100 * 1024 * 1024) +#define H2_TUNNEL_WINDOW_SIZE (10 * 1024 * 1024) + +#define PROXY_H2_NW_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) +#define PROXY_H2_NW_SEND_CHUNKS 1 + +#define H2_TUNNEL_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) +#define H2_TUNNEL_SEND_CHUNKS ((128 * 1024) / PROXY_H2_CHUNK_SIZE) + + +typedef enum { + H2_TUNNEL_INIT, /* init/default/no tunnel state */ + H2_TUNNEL_CONNECT, /* CONNECT request is being send */ + H2_TUNNEL_RESPONSE, /* CONNECT response received completely */ + H2_TUNNEL_ESTABLISHED, + H2_TUNNEL_FAILED +} h2_tunnel_state; + +struct tunnel_stream { + struct http_resp *resp; + struct bufq recvbuf; + struct bufq sendbuf; + char *authority; + int32_t stream_id; + uint32_t error; + size_t upload_blocked_len; + h2_tunnel_state state; + BIT(has_final_response); + BIT(closed); + BIT(reset); +}; + +static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, + struct tunnel_stream *ts) +{ + const char *hostname; + int port; + bool ipv6_ip; + CURLcode result; + + ts->state = H2_TUNNEL_INIT; + ts->stream_id = -1; + Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); + Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); + + result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); + if(result) + return result; + + ts->authority = /* host:port with IPv6 support */ + aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", port); + if(!ts->authority) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static void tunnel_stream_clear(struct tunnel_stream *ts) +{ + Curl_http_resp_free(ts->resp); + Curl_bufq_free(&ts->recvbuf); + Curl_bufq_free(&ts->sendbuf); + Curl_safefree(ts->authority); + memset(ts, 0, sizeof(*ts)); + ts->state = H2_TUNNEL_INIT; +} + +static void h2_tunnel_go_state(struct Curl_cfilter *cf, + struct tunnel_stream *ts, + h2_tunnel_state new_state, + struct Curl_easy *data) +{ + (void)cf; + + if(ts->state == new_state) + return; + /* leaving this one */ + switch(ts->state) { + case H2_TUNNEL_CONNECT: + data->req.ignorebody = FALSE; + break; + default: + break; + } + /* entering this one */ + switch(new_state) { + case H2_TUNNEL_INIT: + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id); + tunnel_stream_clear(ts); + break; + + case H2_TUNNEL_CONNECT: + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'connect'", ts->stream_id); + ts->state = H2_TUNNEL_CONNECT; + break; + + case H2_TUNNEL_RESPONSE: + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'response'", ts->stream_id); + ts->state = H2_TUNNEL_RESPONSE; + break; + + case H2_TUNNEL_ESTABLISHED: + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'", + ts->stream_id); + infof(data, "CONNECT phase completed"); + data->state.authproxy.done = TRUE; + data->state.authproxy.multipass = FALSE; + FALLTHROUGH(); + case H2_TUNNEL_FAILED: + if(new_state == H2_TUNNEL_FAILED) + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'failed'", ts->stream_id); + ts->state = new_state; + /* If a proxy-authorization header was used for the proxy, then we should + make sure that it is not accidentally used for the document request + after we have connected. So let's free and clear it here. */ + Curl_safefree(data->state.aptr.proxyuserpwd); + break; + } +} + +struct cf_h2_proxy_ctx { + nghttp2_session *h2; + /* The easy handle used in the current filter call, cleared at return */ + struct cf_call_data call_data; + + struct bufq inbufq; /* network receive buffer */ + struct bufq outbufq; /* network send buffer */ + + struct tunnel_stream tunnel; /* our tunnel CONNECT stream */ + int32_t goaway_error; + int32_t last_stream_id; + BIT(conn_closed); + BIT(rcvd_goaway); + BIT(sent_goaway); + BIT(nw_out_blocked); +}; + +/* How to access `call_data` from a cf_h2 filter */ +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) \ + ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data + +static void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx) +{ + struct cf_call_data save = ctx->call_data; + + if(ctx->h2) { + nghttp2_session_del(ctx->h2); + } + Curl_bufq_free(&ctx->inbufq); + Curl_bufq_free(&ctx->outbufq); + tunnel_stream_clear(&ctx->tunnel); + memset(ctx, 0, sizeof(*ctx)); + ctx->call_data = save; +} + +static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx) +{ + if(ctx) { + cf_h2_proxy_ctx_clear(ctx); + free(ctx); + } +} + +static void drain_tunnel(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct tunnel_stream *tunnel) +{ + unsigned char bits; + + (void)cf; + bits = CURL_CSELECT_IN; + if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len) + bits |= CURL_CSELECT_OUT; + if(data->state.select_bits != bits) { + CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x", + tunnel->stream_id, bits); + data->state.select_bits = bits; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } +} + +static ssize_t proxy_nw_in_reader(void *reader_ctx, + unsigned char *buf, size_t buflen, + CURLcode *err) +{ + struct Curl_cfilter *cf = reader_ctx; + ssize_t nread; + + if(cf) { + struct Curl_easy *data = CF_DATA_CURRENT(cf); + nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); + CURL_TRC_CF(data, cf, "[0] nw_in_reader(len=%zu) -> %zd, %d", + buflen, nread, *err); + } + else { + nread = 0; + } + return nread; +} + +static ssize_t proxy_h2_nw_out_writer(void *writer_ctx, + const unsigned char *buf, size_t buflen, + CURLcode *err) +{ + struct Curl_cfilter *cf = writer_ctx; + ssize_t nwritten; + + if(cf) { + struct Curl_easy *data = CF_DATA_CURRENT(cf); + nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, + err); + CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %zd, %d", + buflen, nwritten, *err); + } + else { + nwritten = 0; + } + return nwritten; +} + +static int proxy_h2_client_new(struct Curl_cfilter *cf, + nghttp2_session_callbacks *cbs) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + nghttp2_option *o; + + int rc = nghttp2_option_new(&o); + if(rc) + return rc; + /* We handle window updates ourself to enforce buffer limits */ + nghttp2_option_set_no_auto_window_update(o, 1); +#if NGHTTP2_VERSION_NUM >= 0x013200 + /* with 1.50.0 */ + /* turn off RFC 9113 leading and trailing white spaces validation against + HTTP field value. */ + nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); +#endif + rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o); + nghttp2_option_del(o); + return rc; +} + +static ssize_t on_session_send(nghttp2_session *h2, + const uint8_t *buf, size_t blen, + int flags, void *userp); +static int proxy_h2_on_frame_recv(nghttp2_session *session, + const nghttp2_frame *frame, + void *userp); +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int proxy_h2_on_frame_send(nghttp2_session *session, + const nghttp2_frame *frame, + void *userp); +#endif +static int proxy_h2_on_stream_close(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, void *userp); +static int proxy_h2_on_header(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *userp); +static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const uint8_t *mem, size_t len, void *userp); + +/* + * Initialize the cfilter context + */ +static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + nghttp2_session_callbacks *cbs = NULL; + int rc; + + DEBUGASSERT(!ctx->h2); + memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); + + Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); + Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); + + if(tunnel_stream_init(cf, &ctx->tunnel)) + goto out; + + rc = nghttp2_session_callbacks_new(&cbs); + if(rc) { + failf(data, "Couldn't initialize nghttp2 callbacks"); + goto out; + } + + nghttp2_session_callbacks_set_send_callback(cbs, on_session_send); + nghttp2_session_callbacks_set_on_frame_recv_callback( + cbs, proxy_h2_on_frame_recv); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + nghttp2_session_callbacks_set_on_frame_send_callback(cbs, + proxy_h2_on_frame_send); +#endif + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + cbs, tunnel_recv_callback); + nghttp2_session_callbacks_set_on_stream_close_callback( + cbs, proxy_h2_on_stream_close); + nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header); + + /* The nghttp2 session is not yet setup, do it */ + rc = proxy_h2_client_new(cf, cbs); + if(rc) { + failf(data, "Couldn't initialize nghttp2"); + goto out; + } + + { + nghttp2_settings_entry iv[3]; + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = Curl_multi_max_concurrent_streams(data->multi); + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = H2_TUNNEL_WINDOW_SIZE; + iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[2].value = 0; + rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3); + if(rc) { + failf(data, "nghttp2_submit_settings() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + } + + rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, + PROXY_HTTP2_HUGE_WINDOW_SIZE); + if(rc) { + failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + + + /* all set, traffic will be send on connect */ + result = CURLE_OK; + +out: + if(cbs) + nghttp2_session_callbacks_del(cbs); + CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); + return result; +} + +static int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx) +{ + return !nghttp2_session_want_read(ctx->h2) && + !nghttp2_session_want_write(ctx->h2); +} + +static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + ssize_t nwritten; + CURLcode result; + + (void)data; + if(Curl_bufq_is_empty(&ctx->outbufq)) + return CURLE_OK; + + nwritten = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf, + &result); + if(nwritten < 0) { + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN", + Curl_bufq_len(&ctx->outbufq)); + ctx->nw_out_blocked = 1; + } + return result; + } + CURL_TRC_CF(data, cf, "[0] nw send buffer flushed"); + return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN; +} + +/* + * Processes pending input left in network input buffer. + * This function returns 0 if it succeeds, or -1 and error code will + * be assigned to *err. + */ +static int proxy_h2_process_pending_input(struct Curl_cfilter *cf, + struct Curl_easy *data, + CURLcode *err) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + const unsigned char *buf; + size_t blen; + ssize_t rv; + + while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { + + rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); + CURL_TRC_CF(data, cf, "[0] %zu bytes to nghttp2 -> %zd", blen, rv); + if(rv < 0) { + failf(data, + "process_pending_input: nghttp2_session_mem_recv() returned " + "%zd:%s", rv, nghttp2_strerror((int)rv)); + *err = CURLE_RECV_ERROR; + return -1; + } + Curl_bufq_skip(&ctx->inbufq, (size_t)rv); + if(Curl_bufq_is_empty(&ctx->inbufq)) { + CURL_TRC_CF(data, cf, "[0] all data in connection buffer processed"); + break; + } + else { + CURL_TRC_CF(data, cf, "[0] process_pending_input: %zu bytes left " + "in connection buffer", Curl_bufq_len(&ctx->inbufq)); + } + } + + return 0; +} + +static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + ssize_t nread; + + /* Process network input buffer fist */ + if(!Curl_bufq_is_empty(&ctx->inbufq)) { + CURL_TRC_CF(data, cf, "[0] process %zu bytes in connection buffer", + Curl_bufq_len(&ctx->inbufq)); + if(proxy_h2_process_pending_input(cf, data, &result) < 0) + return result; + } + + /* Receive data from the "lower" filters, e.g. network until + * it is time to stop or we have enough data for this stream */ + while(!ctx->conn_closed && /* not closed the connection */ + !ctx->tunnel.closed && /* nor the tunnel */ + Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */ + !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) { + + nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); + CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %zd, %d", + Curl_bufq_len(&ctx->inbufq), nread, result); + if(nread < 0) { + if(result != CURLE_AGAIN) { + failf(data, "Failed receiving HTTP2 data"); + return result; + } + break; + } + else if(nread == 0) { + ctx->conn_closed = TRUE; + break; + } + + if(proxy_h2_process_pending_input(cf, data, &result)) + return result; + } + + if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { + connclose(cf->conn, "GOAWAY received"); + } + + return CURLE_OK; +} + +static CURLcode proxy_h2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + int rv = 0; + + ctx->nw_out_blocked = 0; + while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2)) + rv = nghttp2_session_send(ctx->h2); + + if(nghttp2_is_fatal(rv)) { + CURL_TRC_CF(data, cf, "[0] nghttp2_session_send error (%s)%d", + nghttp2_strerror(rv), rv); + return CURLE_SEND_ERROR; + } + return proxy_h2_nw_out_flush(cf, data); +} + +static ssize_t on_session_send(nghttp2_session *h2, + const uint8_t *buf, size_t blen, int flags, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nwritten; + CURLcode result = CURLE_OK; + + (void)h2; + (void)flags; + DEBUGASSERT(data); + + nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, + proxy_h2_nw_out_writer, cf, &result); + if(nwritten < 0) { + if(result == CURLE_AGAIN) { + return NGHTTP2_ERR_WOULDBLOCK; + } + failf(data, "Failed sending HTTP2 data"); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + if(!nwritten) + return NGHTTP2_ERR_WOULDBLOCK; + + return nwritten; +} + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int proxy_h2_fr_print(const nghttp2_frame *frame, + char *buffer, size_t blen) +{ + switch(frame->hd.type) { + case NGHTTP2_DATA: { + return msnprintf(buffer, blen, + "FRAME[DATA, len=%d, eos=%d, padlen=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM), + (int)frame->data.padlen); + } + case NGHTTP2_HEADERS: { + return msnprintf(buffer, blen, + "FRAME[HEADERS, len=%d, hend=%d, eos=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); + } + case NGHTTP2_PRIORITY: { + return msnprintf(buffer, blen, + "FRAME[PRIORITY, len=%d, flags=%d]", + (int)frame->hd.length, frame->hd.flags); + } + case NGHTTP2_RST_STREAM: { + return msnprintf(buffer, blen, + "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]", + (int)frame->hd.length, frame->hd.flags, + frame->rst_stream.error_code); + } + case NGHTTP2_SETTINGS: { + if(frame->hd.flags & NGHTTP2_FLAG_ACK) { + return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); + } + return msnprintf(buffer, blen, + "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); + } + case NGHTTP2_PUSH_PROMISE: { + return msnprintf(buffer, blen, + "FRAME[PUSH_PROMISE, len=%d, hend=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); + } + case NGHTTP2_PING: { + return msnprintf(buffer, blen, + "FRAME[PING, len=%d, ack=%d]", + (int)frame->hd.length, + frame->hd.flags&NGHTTP2_FLAG_ACK); + } + case NGHTTP2_GOAWAY: { + char scratch[128]; + size_t s_len = sizeof(scratch)/sizeof(scratch[0]); + size_t len = (frame->goaway.opaque_data_len < s_len)? + frame->goaway.opaque_data_len : s_len-1; + if(len) + memcpy(scratch, frame->goaway.opaque_data, len); + scratch[len] = '\0'; + return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', " + "last_stream=%d]", frame->goaway.error_code, + scratch, frame->goaway.last_stream_id); + } + case NGHTTP2_WINDOW_UPDATE: { + return msnprintf(buffer, blen, + "FRAME[WINDOW_UPDATE, incr=%d]", + frame->window_update.window_size_increment); + } + default: + return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", + frame->hd.type, (int)frame->hd.length, + frame->hd.flags); + } +} + +static int proxy_h2_on_frame_send(nghttp2_session *session, + const nghttp2_frame *frame, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)session; + DEBUGASSERT(data); + if(data && Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = proxy_h2_fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); + } + return 0; +} +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + +static int proxy_h2_on_frame_recv(nghttp2_session *session, + const nghttp2_frame *frame, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + int32_t stream_id = frame->hd.stream_id; + + (void)session; + DEBUGASSERT(data); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = proxy_h2_fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer); + } +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + + if(!stream_id) { + /* stream ID zero is for connection-oriented stuff */ + DEBUGASSERT(data); + switch(frame->hd.type) { + case NGHTTP2_SETTINGS: + /* Since the initial stream window is 64K, a request might be on HOLD, + * due to exhaustion. The (initial) SETTINGS may announce a much larger + * window and *assume* that we treat this like a WINDOW_UPDATE. Some + * servers send an explicit WINDOW_UPDATE, but not all seem to do that. + * To be safe, we UNHOLD a stream in order not to stall. */ + if(CURL_WANT_SEND(data)) { + drain_tunnel(cf, data, &ctx->tunnel); + } + break; + case NGHTTP2_GOAWAY: + ctx->rcvd_goaway = TRUE; + break; + default: + break; + } + return 0; + } + + if(stream_id != ctx->tunnel.stream_id) { + CURL_TRC_CF(data, cf, "[%d] rcvd FRAME not for tunnel", stream_id); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + switch(frame->hd.type) { + case NGHTTP2_HEADERS: + /* nghttp2 guarantees that :status is received, and we store it to + stream->status_code. Fuzzing has proven this can still be reached + without status code having been set. */ + if(!ctx->tunnel.resp) + return NGHTTP2_ERR_CALLBACK_FAILURE; + /* Only final status code signals the end of header */ + CURL_TRC_CF(data, cf, "[%d] got http status: %d", + stream_id, ctx->tunnel.resp->status); + if(!ctx->tunnel.has_final_response) { + if(ctx->tunnel.resp->status / 100 != 1) { + ctx->tunnel.has_final_response = TRUE; + } + } + break; + case NGHTTP2_WINDOW_UPDATE: + if(CURL_WANT_SEND(data)) { + drain_tunnel(cf, data, &ctx->tunnel); + } + break; + default: + break; + } + return 0; +} + +static int proxy_h2_on_header(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + int32_t stream_id = frame->hd.stream_id; + CURLcode result; + + (void)flags; + (void)data; + (void)session; + DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ + if(stream_id != ctx->tunnel.stream_id) { + CURL_TRC_CF(data, cf, "[%d] header for non-tunnel stream: " + "%.*s: %.*s", stream_id, + (int)namelen, name, (int)valuelen, value); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + if(frame->hd.type == NGHTTP2_PUSH_PROMISE) + return NGHTTP2_ERR_CALLBACK_FAILURE; + + if(ctx->tunnel.has_final_response) { + /* we do not do anything with trailers for tunnel streams */ + return 0; + } + + if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && + memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { + int http_status; + struct http_resp *resp; + + /* status: always comes first, we might get more than one response, + * link the previous ones for keepers */ + result = Curl_http_decode_status(&http_status, + (const char *)value, valuelen); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + result = Curl_http_resp_make(&resp, http_status, NULL); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + resp->prev = ctx->tunnel.resp; + ctx->tunnel.resp = resp; + CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", + stream_id, ctx->tunnel.resp->status); + return 0; + } + + if(!ctx->tunnel.resp) + return NGHTTP2_ERR_CALLBACK_FAILURE; + + result = Curl_dynhds_add(&ctx->tunnel.resp->headers, + (const char *)name, namelen, + (const char *)value, valuelen); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + + CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s", + stream_id, (int)namelen, name, (int)valuelen, value); + + return 0; /* 0 is successful */ +} + +static ssize_t tunnel_send_callback(nghttp2_session *session, + int32_t stream_id, + uint8_t *buf, size_t length, + uint32_t *data_flags, + nghttp2_data_source *source, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct tunnel_stream *ts; + CURLcode result; + ssize_t nread; + + (void)source; + (void)data; + (void)ctx; + + if(!stream_id) + return NGHTTP2_ERR_INVALID_ARGUMENT; + + ts = nghttp2_session_get_stream_user_data(session, stream_id); + if(!ts) + return NGHTTP2_ERR_CALLBACK_FAILURE; + DEBUGASSERT(ts == &ctx->tunnel); + + nread = Curl_bufq_read(&ts->sendbuf, buf, length, &result); + if(nread < 0) { + if(result != CURLE_AGAIN) + return NGHTTP2_ERR_CALLBACK_FAILURE; + return NGHTTP2_ERR_DEFERRED; + } + if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf)) + *data_flags = NGHTTP2_DATA_FLAG_EOF; + + CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd", + ts->stream_id, nread); + return nread; +} + +static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const uint8_t *mem, size_t len, void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_proxy_ctx *ctx = cf->ctx; + ssize_t nwritten; + CURLcode result; + + (void)flags; + (void)session; + DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ + + if(stream_id != ctx->tunnel.stream_id) + return NGHTTP2_ERR_CALLBACK_FAILURE; + + nwritten = Curl_bufq_write(&ctx->tunnel.recvbuf, mem, len, &result); + if(nwritten < 0) { + if(result != CURLE_AGAIN) + return NGHTTP2_ERR_CALLBACK_FAILURE; + nwritten = 0; + } + DEBUGASSERT((size_t)nwritten == len); + return 0; +} + +static int proxy_h2_on_stream_close(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)session; + (void)data; + + if(stream_id != ctx->tunnel.stream_id) + return 0; + + CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %d)", + stream_id, nghttp2_http2_strerror(error_code), error_code); + ctx->tunnel.closed = TRUE; + ctx->tunnel.error = error_code; + + return 0; +} + +static CURLcode proxy_h2_submit(int32_t *pstream_id, + struct Curl_cfilter *cf, + struct Curl_easy *data, + nghttp2_session *h2, + struct httpreq *req, + const nghttp2_priority_spec *pri_spec, + void *stream_user_data, + nghttp2_data_source_read_callback read_callback, + void *read_ctx) +{ + struct dynhds h2_headers; + nghttp2_nv *nva = NULL; + int32_t stream_id = -1; + size_t nheader; + CURLcode result; + + (void)cf; + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + result = Curl_http_req_to_h2(&h2_headers, req, data); + if(result) + goto out; + + nva = Curl_dynhds_to_nva(&h2_headers, &nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(read_callback) { + nghttp2_data_provider data_prd; + + data_prd.read_callback = read_callback; + data_prd.source.ptr = read_ctx; + stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader, + &data_prd, stream_user_data); + } + else { + stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader, + NULL, stream_user_data); + } + + if(stream_id < 0) { + failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", + nghttp2_strerror(stream_id), stream_id); + result = CURLE_SEND_ERROR; + goto out; + } + result = CURLE_OK; + +out: + free(nva); + Curl_dynhds_free(&h2_headers); + *pstream_id = stream_id; + return result; +} + +static CURLcode submit_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct tunnel_stream *ts) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + CURLcode result; + struct httpreq *req = NULL; + + result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2); + if(result) + goto out; + result = Curl_creader_set_null(data); + if(result) + goto out; + + infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority); + + result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, + NULL, ts, tunnel_send_callback, cf); + if(result) { + CURL_TRC_CF(data, cf, "[%d] send, nghttp2_submit_request error: %s", + ts->stream_id, nghttp2_strerror(ts->stream_id)); + } + +out: + if(req) + Curl_http_req_free(req); + if(result) + failf(data, "Failed sending CONNECT to proxy"); + return result; +} + +static CURLcode inspect_response(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct tunnel_stream *ts) +{ + CURLcode result = CURLE_OK; + struct dynhds_entry *auth_reply = NULL; + (void)cf; + + DEBUGASSERT(ts->resp); + if(ts->resp->status/100 == 2) { + infof(data, "CONNECT tunnel established, response %d", ts->resp->status); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data); + return CURLE_OK; + } + + if(ts->resp->status == 401) { + auth_reply = Curl_dynhds_cget(&ts->resp->headers, "WWW-Authenticate"); + } + else if(ts->resp->status == 407) { + auth_reply = Curl_dynhds_cget(&ts->resp->headers, "Proxy-Authenticate"); + } + + if(auth_reply) { + CURL_TRC_CF(data, cf, "[0] CONNECT: fwd auth header '%s'", + auth_reply->value); + result = Curl_http_input_auth(data, ts->resp->status == 407, + auth_reply->value); + if(result) + return result; + if(data->req.newurl) { + /* Indicator that we should try again */ + Curl_safefree(data->req.newurl); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); + return CURLE_OK; + } + } + + /* Seems to have failed */ + return CURLE_RECV_ERROR; +} + +static CURLcode H2_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct tunnel_stream *ts) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + DEBUGASSERT(ts); + DEBUGASSERT(ts->authority); + do { + switch(ts->state) { + case H2_TUNNEL_INIT: + /* Prepare the CONNECT request and make a first attempt to send. */ + CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority); + result = submit_CONNECT(cf, data, ts); + if(result) + goto out; + h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data); + FALLTHROUGH(); + + case H2_TUNNEL_CONNECT: + /* see that the request is completely sent */ + result = proxy_h2_progress_ingress(cf, data); + if(!result) + result = proxy_h2_progress_egress(cf, data); + if(result && result != CURLE_AGAIN) { + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); + break; + } + + if(ts->has_final_response) { + h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data); + } + else { + result = CURLE_OK; + goto out; + } + FALLTHROUGH(); + + case H2_TUNNEL_RESPONSE: + DEBUGASSERT(ts->has_final_response); + result = inspect_response(cf, data, ts); + if(result) + goto out; + break; + + case H2_TUNNEL_ESTABLISHED: + return CURLE_OK; + + case H2_TUNNEL_FAILED: + return CURLE_RECV_ERROR; + + default: + break; + } + + } while(ts->state == H2_TUNNEL_INIT); + +out: + if(result || ctx->tunnel.closed) + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); + return result; +} + +static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct cf_call_data save; + timediff_t check; + struct tunnel_stream *ts = &ctx->tunnel; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* Connect the lower filters first */ + if(!cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + } + + *done = FALSE; + + CF_DATA_SAVE(save, cf, data); + if(!ctx->h2) { + result = cf_h2_proxy_ctx_init(cf, data); + if(result) + goto out; + } + DEBUGASSERT(ts->authority); + + check = Curl_timeleft(data, NULL, TRUE); + if(check <= 0) { + failf(data, "Proxy CONNECT aborted due to timeout"); + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + + /* for the secondary socket (FTP), use the "connect to host" + * but ignore the "connect to port" (use the secondary port) + */ + result = H2_CONNECT(cf, data, ts); + +out: + *done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED); + if(*done) { + cf->connected = TRUE; + /* The real request will follow the CONNECT, reset request partially */ + Curl_req_soft_reset(&data->req, data); + Curl_client_reset(data); + } + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_h2_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + + if(ctx) { + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + cf_h2_proxy_ctx_clear(ctx); + CF_DATA_RESTORE(cf, save); + } + if(cf->next) + cf->next->cft->do_close(cf->next, data); +} + +static void cf_h2_proxy_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + + (void)data; + if(ctx) { + cf_h2_proxy_ctx_free(ctx); + cf->ctx = NULL; + } +} + +static CURLcode cf_h2_proxy_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct cf_call_data save; + CURLcode result; + int rv; + + if(!cf->connected || !ctx->h2 || cf->shutdown || ctx->conn_closed) { + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + + if(!ctx->sent_goaway) { + rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE, + 0, 0, + (const uint8_t *)"shutown", sizeof("shutown")); + if(rv) { + failf(data, "nghttp2_submit_goaway() failed: %s(%d)", + nghttp2_strerror(rv), rv); + result = CURLE_SEND_ERROR; + goto out; + } + ctx->sent_goaway = TRUE; + } + /* GOAWAY submitted, process egress and ingress until nghttp2 is done. */ + result = CURLE_OK; + if(nghttp2_session_want_write(ctx->h2)) + result = proxy_h2_progress_egress(cf, data); + if(!result && nghttp2_session_want_read(ctx->h2)) + result = proxy_h2_progress_ingress(cf, data); + + *done = (ctx->conn_closed || + (!result && !nghttp2_session_want_write(ctx->h2) && + !nghttp2_session_want_read(ctx->h2))); +out: + CF_DATA_RESTORE(cf, save); + cf->shutdown = (result || *done); + return result; +} + +static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + if((ctx && !Curl_bufq_is_empty(&ctx->inbufq)) || + (ctx && ctx->tunnel.state == H2_TUNNEL_ESTABLISHED && + !Curl_bufq_is_empty(&ctx->tunnel.recvbuf))) + return TRUE; + return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct cf_call_data save; + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + bool want_recv, want_send; + + if(!cf->connected && ctx->h2) { + want_send = nghttp2_session_want_write(ctx->h2); + want_recv = nghttp2_session_want_read(ctx->h2); + } + else + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + + if(ctx->h2 && (want_recv || want_send)) { + bool c_exhaust, s_exhaust; + + CF_DATA_SAVE(save, cf, data); + c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2); + s_exhaust = ctx->tunnel.stream_id >= 0 && + !nghttp2_session_get_stream_remote_window_size( + ctx->h2, ctx->tunnel.stream_id); + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + (!c_exhaust && nghttp2_session_want_write(ctx->h2)); + + Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } + else if(ctx->sent_goaway && !cf->shutdown) { + /* shutdown in progress */ + CF_DATA_SAVE(save, cf, data); + want_send = nghttp2_session_want_write(ctx->h2); + want_recv = nghttp2_session_want_read(ctx->h2); + Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } +} + +static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + CURLcode *err) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + ssize_t rv = 0; + + if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { + CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " + "connection", ctx->tunnel.stream_id); + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ + return -1; + } + else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) { + failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", + ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error), + ctx->tunnel.error); + *err = CURLE_HTTP2_STREAM; + return -1; + } + else if(ctx->tunnel.reset) { + failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id); + *err = CURLE_RECV_ERROR; + return -1; + } + + *err = CURLE_OK; + rv = 0; + CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> %zd, %d", + ctx->tunnel.stream_id, rv, *err); + return rv; +} + +static ssize_t tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + ssize_t nread = -1; + + *err = CURLE_AGAIN; + if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { + nread = Curl_bufq_read(&ctx->tunnel.recvbuf, + (unsigned char *)buf, len, err); + if(nread < 0) + goto out; + DEBUGASSERT(nread > 0); + } + + if(nread < 0) { + if(ctx->tunnel.closed) { + nread = h2_handle_tunnel_close(cf, data, err); + } + else if(ctx->tunnel.reset || + (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || + (ctx->rcvd_goaway && + ctx->last_stream_id < ctx->tunnel.stream_id)) { + *err = CURLE_RECV_ERROR; + nread = -1; + } + } + else if(nread == 0) { + *err = CURLE_AGAIN; + nread = -1; + } + +out: + CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %zd, %d", + ctx->tunnel.stream_id, len, nread, *err); + return nread; +} + +static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + ssize_t nread = -1; + struct cf_call_data save; + CURLcode result; + + if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { + *err = CURLE_RECV_ERROR; + return -1; + } + CF_DATA_SAVE(save, cf, data); + + if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { + *err = proxy_h2_progress_ingress(cf, data); + if(*err) + goto out; + } + + nread = tunnel_recv(cf, data, buf, len, err); + + if(nread > 0) { + CURL_TRC_CF(data, cf, "[%d] increase window by %zd", + ctx->tunnel.stream_id, nread); + nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread); + } + + result = proxy_h2_progress_egress(cf, data); + if(result == CURLE_AGAIN) { + /* pending data to send, need to be called again. Ideally, we would + * monitor the socket for POLLOUT, but we might not be in SENDING + * transfer state any longer and are unable to make this happen. + */ + CURL_TRC_CF(data, cf, "[%d] egress blocked, DRAIN", + ctx->tunnel.stream_id); + drain_tunnel(cf, data, &ctx->tunnel); + } + else if(result) { + *err = result; + nread = -1; + } + +out: + if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + (nread >= 0 || *err == CURLE_AGAIN)) { + /* data pending and no fatal error to report. Need to trigger + * draining to avoid stalling when no socket events happen. */ + drain_tunnel(cf, data, &ctx->tunnel); + } + CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d", + ctx->tunnel.stream_id, len, nread, *err); + CF_DATA_RESTORE(cf, save); + return nread; +} + +static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct cf_call_data save; + int rv; + ssize_t nwritten; + CURLcode result; + int blocked = 0; + + if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { + *err = CURLE_SEND_ERROR; + return -1; + } + CF_DATA_SAVE(save, cf, data); + + if(ctx->tunnel.closed) { + nwritten = -1; + *err = CURLE_SEND_ERROR; + goto out; + } + else if(ctx->tunnel.upload_blocked_len) { + /* the data in `buf` has already been submitted or added to the + * buffers, but have been EAGAINed on the last invocation. */ + DEBUGASSERT(len >= ctx->tunnel.upload_blocked_len); + if(len < ctx->tunnel.upload_blocked_len) { + /* Did we get called again with a smaller `len`? This should not + * happen. We are not prepared to handle that. */ + failf(data, "HTTP/2 proxy, send again with decreased length"); + *err = CURLE_HTTP2; + nwritten = -1; + goto out; + } + nwritten = (ssize_t)ctx->tunnel.upload_blocked_len; + ctx->tunnel.upload_blocked_len = 0; + *err = CURLE_OK; + } + else { + nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err); + if(nwritten < 0) { + if(*err != CURLE_AGAIN) + goto out; + nwritten = 0; + } + } + + if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { + /* req body data is buffered, resume the potentially suspended stream */ + rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id); + if(nghttp2_is_fatal(rv)) { + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + } + + result = proxy_h2_progress_ingress(cf, data); + if(result) { + *err = result; + nwritten = -1; + goto out; + } + + /* Call the nghttp2 send loop and flush to write ALL buffered data, + * headers and/or request body completely out to the network */ + result = proxy_h2_progress_egress(cf, data); + if(result == CURLE_AGAIN) { + blocked = 1; + } + else if(result) { + *err = result; + nwritten = -1; + goto out; + } + else if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { + /* although we wrote everything that nghttp2 wants to send now, + * there is data left in our stream send buffer unwritten. This may + * be due to the stream's HTTP/2 flow window being exhausted. */ + blocked = 1; + } + + if(blocked) { + /* Unable to send all data, due to connection blocked or H2 window + * exhaustion. Data is left in our stream buffer, or nghttp2's internal + * frame buffer or our network out buffer. */ + size_t rwin = (size_t)nghttp2_session_get_stream_remote_window_size( + ctx->h2, ctx->tunnel.stream_id); + if(rwin == 0) { + /* H2 flow window exhaustion. + * FIXME: there is no way to HOLD all transfers that use this + * proxy connection AND to UNHOLD all of them again when the + * window increases. + * We *could* iterate over all data on this conn maybe? */ + CURL_TRC_CF(data, cf, "[%d] remote flow " + "window is exhausted", ctx->tunnel.stream_id); + } + + /* Whatever the cause, we need to return CURL_EAGAIN for this call. + * We have unwritten state that needs us being invoked again and EAGAIN + * is the only way to ensure that. */ + ctx->tunnel.upload_blocked_len = nwritten; + CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu " + "blocked_len=%zu", + ctx->tunnel.stream_id, len, + nghttp2_session_get_remote_window_size(ctx->h2), rwin, + nwritten); + drain_tunnel(cf, data, &ctx->tunnel); + *err = CURLE_AGAIN; + nwritten = -1; + goto out; + } + else if(proxy_h2_should_close_session(ctx)) { + /* nghttp2 thinks this session is done. If the stream has not been + * closed, this is an error state for out transfer */ + if(ctx->tunnel.closed) { + *err = CURLE_SEND_ERROR; + nwritten = -1; + } + else { + CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session"); + *err = CURLE_HTTP2; + nwritten = -1; + } + } + +out: + if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + (nwritten >= 0 || *err == CURLE_AGAIN)) { + /* data pending and no fatal error to report. Need to trigger + * draining to avoid stalling when no socket events happen. */ + drain_tunnel(cf, data, &ctx->tunnel); + } + CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, " + "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", + ctx->tunnel.stream_id, len, nwritten, *err, + nghttp2_session_get_stream_remote_window_size( + ctx->h2, ctx->tunnel.stream_id), + nghttp2_session_get_remote_window_size(ctx->h2), + Curl_bufq_len(&ctx->tunnel.sendbuf), + Curl_bufq_len(&ctx->outbufq)); + CF_DATA_RESTORE(cf, save); + return nwritten; +} + +static bool proxy_h2_connisalive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + bool alive = TRUE; + + *input_pending = FALSE; + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + return FALSE; + + if(*input_pending) { + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, + only "protocol frames" */ + CURLcode result; + ssize_t nread = -1; + + *input_pending = FALSE; + nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); + if(nread != -1) { + if(proxy_h2_process_pending_input(cf, data, &result) < 0) + /* immediate error, considered dead */ + alive = FALSE; + else { + alive = !proxy_h2_should_close_session(ctx); + } + } + else if(result != CURLE_AGAIN) { + /* the read failed so let's say this is dead anyway */ + alive = FALSE; + } + } + + return alive; +} + +static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + CURLcode result; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + result = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending)); + CURL_TRC_CF(data, cf, "[0] conn alive -> %d, input_pending=%d", + result, *input_pending); + CF_DATA_RESTORE(cf, save); + return result; +} + +struct Curl_cftype Curl_cft_h2_proxy = { + "H2-PROXY", + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, + CURL_LOG_LVL_NONE, + cf_h2_proxy_destroy, + cf_h2_proxy_connect, + cf_h2_proxy_close, + cf_h2_proxy_shutdown, + Curl_cf_http_proxy_get_host, + cf_h2_proxy_adjust_pollset, + cf_h2_proxy_data_pending, + cf_h2_proxy_send, + cf_h2_proxy_recv, + Curl_cf_def_cntrl, + cf_h2_proxy_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf_h2_proxy = NULL; + struct cf_h2_proxy_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + + (void)data; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) + goto out; + + result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx); + if(result) + goto out; + + Curl_conn_cf_insert_after(cf, cf_h2_proxy); + result = CURLE_OK; + +out: + if(result) + cf_h2_proxy_ctx_free(ctx); + return result; +} + +#endif /* defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) */ diff --git a/third_party/curl/lib/cf-h2-proxy.h b/third_party/curl/lib/cf-h2-proxy.h new file mode 100644 index 0000000000..c01bf62133 --- /dev/null +++ b/third_party/curl/lib/cf-h2-proxy.h @@ -0,0 +1,39 @@ +#ifndef HEADER_CURL_H2_PROXY_H +#define HEADER_CURL_H2_PROXY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) + +CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_h2_proxy; + + +#endif /* defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) */ + +#endif /* HEADER_CURL_H2_PROXY_H */ diff --git a/third_party/curl/lib/cf-haproxy.c b/third_party/curl/lib/cf-haproxy.c new file mode 100644 index 0000000000..200c13958f --- /dev/null +++ b/third_party/curl/lib/cf-haproxy.c @@ -0,0 +1,251 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) + +#include +#include "urldata.h" +#include "cfilters.h" +#include "cf-haproxy.h" +#include "curl_trc.h" +#include "multiif.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +typedef enum { + HAPROXY_INIT, /* init/default/no tunnel state */ + HAPROXY_SEND, /* data_out being sent */ + HAPROXY_DONE /* all work done */ +} haproxy_state; + +struct cf_haproxy_ctx { + int state; + struct dynbuf data_out; +}; + +static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx) +{ + DEBUGASSERT(ctx); + ctx->state = HAPROXY_INIT; + Curl_dyn_reset(&ctx->data_out); +} + +static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx) +{ + if(ctx) { + Curl_dyn_free(&ctx->data_out); + free(ctx); + } +} + +static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf, + struct Curl_easy *data) +{ + struct cf_haproxy_ctx *ctx = cf->ctx; + CURLcode result; + const char *tcp_version; + const char *client_ip; + + DEBUGASSERT(ctx); + DEBUGASSERT(ctx->state == HAPROXY_INIT); +#ifdef USE_UNIX_SOCKETS + if(cf->conn->unix_domain_socket) + /* the buffer is large enough to hold this! */ + result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n")); + else { +#endif /* USE_UNIX_SOCKETS */ + /* Emit the correct prefix for IPv6 */ + tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4"; + if(data->set.str[STRING_HAPROXY_CLIENT_IP]) + client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; + else + client_ip = data->info.primary.local_ip; + + result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", + tcp_version, + client_ip, + data->info.primary.remote_ip, + data->info.primary.local_port, + data->info.primary.remote_port); + +#ifdef USE_UNIX_SOCKETS + } +#endif /* USE_UNIX_SOCKETS */ + return result; +} + +static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_haproxy_ctx *ctx = cf->ctx; + CURLcode result; + size_t len; + + DEBUGASSERT(ctx); + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + result = cf->next->cft->do_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + + switch(ctx->state) { + case HAPROXY_INIT: + result = cf_haproxy_date_out_set(cf, data); + if(result) + goto out; + ctx->state = HAPROXY_SEND; + FALLTHROUGH(); + case HAPROXY_SEND: + len = Curl_dyn_len(&ctx->data_out); + if(len > 0) { + size_t written; + result = Curl_conn_send(data, cf->sockindex, + Curl_dyn_ptr(&ctx->data_out), + len, &written); + if(result == CURLE_AGAIN) { + result = CURLE_OK; + written = 0; + } + else if(result) + goto out; + Curl_dyn_tail(&ctx->data_out, len - written); + if(Curl_dyn_len(&ctx->data_out) > 0) { + result = CURLE_OK; + goto out; + } + } + ctx->state = HAPROXY_DONE; + FALLTHROUGH(); + default: + Curl_dyn_free(&ctx->data_out); + break; + } + +out: + *done = (!result) && (ctx->state == HAPROXY_DONE); + cf->connected = *done; + return result; +} + +static void cf_haproxy_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)data; + CURL_TRC_CF(data, cf, "destroy"); + cf_haproxy_ctx_free(cf->ctx); +} + +static void cf_haproxy_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURL_TRC_CF(data, cf, "close"); + cf->connected = FALSE; + cf_haproxy_ctx_reset(cf->ctx); + if(cf->next) + cf->next->cft->do_close(cf->next, data); +} + +static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + if(cf->next->connected && !cf->connected) { + /* If we are not connected, but the filter "below" is + * and not waiting on something, we are sending. */ + Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data)); + } +} + +struct Curl_cftype Curl_cft_haproxy = { + "HAPROXY", + CF_TYPE_PROXY, + 0, + cf_haproxy_destroy, + cf_haproxy_connect, + cf_haproxy_close, + Curl_cf_def_shutdown, + Curl_cf_def_get_host, + cf_haproxy_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf = NULL; + struct cf_haproxy_ctx *ctx; + CURLcode result; + + (void)data; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->state = HAPROXY_INIT; + Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY); + + result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx); + if(result) + goto out; + ctx = NULL; + +out: + cf_haproxy_ctx_free(ctx); + *pcf = result? NULL : cf; + return result; +} + +CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_haproxy_create(&cf, data); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); + +out: + return result; +} + +#endif /* !CURL_DISABLE_PROXY */ diff --git a/third_party/curl/lib/cf-haproxy.h b/third_party/curl/lib/cf-haproxy.h new file mode 100644 index 0000000000..d02c323e7b --- /dev/null +++ b/third_party/curl/lib/cf-haproxy.h @@ -0,0 +1,39 @@ +#ifndef HEADER_CURL_CF_HAPROXY_H +#define HEADER_CURL_CF_HAPROXY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "urldata.h" + +#if !defined(CURL_DISABLE_PROXY) + +CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_haproxy; + +#endif /* !CURL_DISABLE_PROXY */ + +#endif /* HEADER_CURL_CF_HAPROXY_H */ diff --git a/third_party/curl/lib/cf-https-connect.c b/third_party/curl/lib/cf-https-connect.c new file mode 100644 index 0000000000..7a6bd97e1e --- /dev/null +++ b/third_party/curl/lib/cf-https-connect.c @@ -0,0 +1,576 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) + +#include "urldata.h" +#include +#include "curl_trc.h" +#include "cfilters.h" +#include "connect.h" +#include "multiif.h" +#include "cf-https-connect.h" +#include "http2.h" +#include "vquic/vquic.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +typedef enum { + CF_HC_INIT, + CF_HC_CONNECT, + CF_HC_SUCCESS, + CF_HC_FAILURE +} cf_hc_state; + +struct cf_hc_baller { + const char *name; + struct Curl_cfilter *cf; + CURLcode result; + struct curltime started; + int reply_ms; + BIT(enabled); + BIT(shutdown); +}; + +static void cf_hc_baller_reset(struct cf_hc_baller *b, + struct Curl_easy *data) +{ + if(b->cf) { + Curl_conn_cf_close(b->cf, data); + Curl_conn_cf_discard_chain(&b->cf, data); + b->cf = NULL; + } + b->result = CURLE_OK; + b->reply_ms = -1; +} + +static bool cf_hc_baller_is_active(struct cf_hc_baller *b) +{ + return b->enabled && b->cf && !b->result; +} + +static bool cf_hc_baller_has_started(struct cf_hc_baller *b) +{ + return !!b->cf; +} + +static int cf_hc_baller_reply_ms(struct cf_hc_baller *b, + struct Curl_easy *data) +{ + if(b->reply_ms < 0) + b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS, + &b->reply_ms, NULL); + return b->reply_ms; +} + +static bool cf_hc_baller_data_pending(struct cf_hc_baller *b, + const struct Curl_easy *data) +{ + return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data); +} + +struct cf_hc_ctx { + cf_hc_state state; + const struct Curl_dns_entry *remotehost; + struct curltime started; /* when connect started */ + CURLcode result; /* overall result */ + struct cf_hc_baller h3_baller; + struct cf_hc_baller h21_baller; + unsigned int soft_eyeballs_timeout_ms; + unsigned int hard_eyeballs_timeout_ms; +}; + +static void cf_hc_baller_init(struct cf_hc_baller *b, + struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *name, + int transport) +{ + struct cf_hc_ctx *ctx = cf->ctx; + struct Curl_cfilter *save = cf->next; + + b->name = name; + cf->next = NULL; + b->started = Curl_now(); + b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost, + transport, CURL_CF_SSL_ENABLE); + b->cf = cf->next; + cf->next = save; +} + +static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b, + struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct Curl_cfilter *save = cf->next; + + cf->next = b->cf; + b->result = Curl_conn_cf_connect(cf->next, data, FALSE, done); + b->cf = cf->next; /* it might mutate */ + cf->next = save; + return b->result; +} + +static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + + if(ctx) { + cf_hc_baller_reset(&ctx->h3_baller, data); + cf_hc_baller_reset(&ctx->h21_baller, data); + ctx->state = CF_HC_INIT; + ctx->result = CURLE_OK; + ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; + ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2; + } +} + +static CURLcode baller_connected(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_hc_baller *winner) +{ + struct cf_hc_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + DEBUGASSERT(winner->cf); + if(winner != &ctx->h3_baller) + cf_hc_baller_reset(&ctx->h3_baller, data); + if(winner != &ctx->h21_baller) + cf_hc_baller_reset(&ctx->h21_baller, data); + + CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", + winner->name, (int)Curl_timediff(Curl_now(), winner->started), + cf_hc_baller_reply_ms(winner, data)); + cf->next = winner->cf; + winner->cf = NULL; + + switch(cf->conn->alpn) { + case CURL_HTTP_VERSION_3: + infof(data, "using HTTP/3"); + break; + case CURL_HTTP_VERSION_2: +#ifdef USE_NGHTTP2 + /* Using nghttp2, we add the filter "below" us, so when the conn + * closes, we tear it down for a fresh reconnect */ + result = Curl_http2_switch_at(cf, data); + if(result) { + ctx->state = CF_HC_FAILURE; + ctx->result = result; + return result; + } +#endif + infof(data, "using HTTP/2"); + break; + default: + infof(data, "using HTTP/1.x"); + break; + } + ctx->state = CF_HC_SUCCESS; + cf->connected = TRUE; + Curl_conn_cf_cntrl(cf->next, data, TRUE, + CF_CTRL_CONN_INFO_UPDATE, 0, NULL); + return result; +} + + +static bool time_to_start_h21(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct curltime now) +{ + struct cf_hc_ctx *ctx = cf->ctx; + timediff_t elapsed_ms; + + if(!ctx->h21_baller.enabled || cf_hc_baller_has_started(&ctx->h21_baller)) + return FALSE; + + if(!ctx->h3_baller.enabled || !cf_hc_baller_is_active(&ctx->h3_baller)) + return TRUE; + + elapsed_ms = Curl_timediff(now, ctx->started); + if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { + CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting h21", + ctx->hard_eyeballs_timeout_ms); + return TRUE; + } + + if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) { + if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) { + CURL_TRC_CF(data, cf, "soft timeout of %dms reached, h3 has not " + "seen any data, starting h21", + ctx->soft_eyeballs_timeout_ms); + return TRUE; + } + /* set the effective hard timeout again */ + Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms, + EXPIRE_ALPN_EYEBALLS); + } + return FALSE; +} + +static CURLcode cf_hc_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_hc_ctx *ctx = cf->ctx; + struct curltime now; + CURLcode result = CURLE_OK; + + (void)blocking; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + *done = FALSE; + now = Curl_now(); + switch(ctx->state) { + case CF_HC_INIT: + DEBUGASSERT(!ctx->h3_baller.cf); + DEBUGASSERT(!ctx->h21_baller.cf); + DEBUGASSERT(!cf->next); + CURL_TRC_CF(data, cf, "connect, init"); + ctx->started = now; + if(ctx->h3_baller.enabled) { + cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC); + if(ctx->h21_baller.enabled) + Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); + } + else if(ctx->h21_baller.enabled) + cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", + cf->conn->transport); + ctx->state = CF_HC_CONNECT; + FALLTHROUGH(); + + case CF_HC_CONNECT: + if(cf_hc_baller_is_active(&ctx->h3_baller)) { + result = cf_hc_baller_connect(&ctx->h3_baller, cf, data, done); + if(!result && *done) { + result = baller_connected(cf, data, &ctx->h3_baller); + goto out; + } + } + + if(time_to_start_h21(cf, data, now)) { + cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", + cf->conn->transport); + } + + if(cf_hc_baller_is_active(&ctx->h21_baller)) { + CURL_TRC_CF(data, cf, "connect, check h21"); + result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done); + if(!result && *done) { + result = baller_connected(cf, data, &ctx->h21_baller); + goto out; + } + } + + if((!ctx->h3_baller.enabled || ctx->h3_baller.result) && + (!ctx->h21_baller.enabled || ctx->h21_baller.result)) { + /* both failed or disabled. we give up */ + CURL_TRC_CF(data, cf, "connect, all failed"); + result = ctx->result = ctx->h3_baller.enabled? + ctx->h3_baller.result : ctx->h21_baller.result; + ctx->state = CF_HC_FAILURE; + goto out; + } + result = CURLE_OK; + *done = FALSE; + break; + + case CF_HC_FAILURE: + result = ctx->result; + cf->connected = FALSE; + *done = FALSE; + break; + + case CF_HC_SUCCESS: + result = CURLE_OK; + cf->connected = TRUE; + *done = TRUE; + break; + } + +out: + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); + return result; +} + +static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_hc_ctx *ctx = cf->ctx; + struct cf_hc_baller *ballers[2]; + size_t i; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* shutdown all ballers that have not done so already. If one fails, + * continue shutting down others until all are shutdown. */ + ballers[0] = &ctx->h3_baller; + ballers[1] = &ctx->h21_baller; + for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { + struct cf_hc_baller *b = ballers[i]; + bool bdone = FALSE; + if(!cf_hc_baller_is_active(b) || b->shutdown) + continue; + b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone); + if(b->result || bdone) + b->shutdown = TRUE; /* treat a failed shutdown as done */ + } + + *done = TRUE; + for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { + if(ballers[i] && !ballers[i]->shutdown) + *done = FALSE; + } + if(*done) { + for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { + if(ballers[i] && ballers[i]->result) + result = ballers[i]->result; + } + } + CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); + return result; +} + +static void cf_hc_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + if(!cf->connected) { + struct cf_hc_ctx *ctx = cf->ctx; + struct cf_hc_baller *ballers[2]; + size_t i; + + ballers[0] = &ctx->h3_baller; + ballers[1] = &ctx->h21_baller; + for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { + struct cf_hc_baller *b = ballers[i]; + if(!cf_hc_baller_is_active(b)) + continue; + Curl_conn_cf_adjust_pollset(b->cf, data, ps); + } + CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); + } +} + +static bool cf_hc_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + + if(cf->connected) + return cf->next->cft->has_data_pending(cf->next, data); + + CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending"); + return cf_hc_baller_data_pending(&ctx->h3_baller, data) + || cf_hc_baller_data_pending(&ctx->h21_baller, data); +} + +static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query) +{ + struct cf_hc_ctx *ctx = cf->ctx; + struct Curl_cfilter *cfb; + struct curltime t, tmax; + + memset(&tmax, 0, sizeof(tmax)); + memset(&t, 0, sizeof(t)); + cfb = ctx->h21_baller.enabled? ctx->h21_baller.cf : NULL; + if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) { + if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) + tmax = t; + } + memset(&t, 0, sizeof(t)); + cfb = ctx->h3_baller.enabled? ctx->h3_baller.cf : NULL; + if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) { + if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) + tmax = t; + } + return tmax; +} + +static CURLcode cf_hc_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + if(!cf->connected) { + switch(query) { + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); + return CURLE_OK; + } + default: + break; + } + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + CURL_TRC_CF(data, cf, "close"); + cf_hc_reset(cf, data); + cf->connected = FALSE; + + if(cf->next) { + cf->next->cft->do_close(cf->next, data); + Curl_conn_cf_discard_chain(&cf->next, data); + } +} + +static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + + (void)data; + CURL_TRC_CF(data, cf, "destroy"); + cf_hc_reset(cf, data); + Curl_safefree(ctx); +} + +struct Curl_cftype Curl_cft_http_connect = { + "HTTPS-CONNECT", + 0, + CURL_LOG_LVL_NONE, + cf_hc_destroy, + cf_hc_connect, + cf_hc_close, + cf_hc_shutdown, + Curl_cf_def_get_host, + cf_hc_adjust_pollset, + cf_hc_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_hc_query, +}; + +static CURLcode cf_hc_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + bool try_h3, bool try_h21) +{ + struct Curl_cfilter *cf = NULL; + struct cf_hc_ctx *ctx; + CURLcode result = CURLE_OK; + + (void)data; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->remotehost = remotehost; + ctx->h3_baller.enabled = try_h3; + ctx->h21_baller.enabled = try_h21; + + result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx); + if(result) + goto out; + ctx = NULL; + cf_hc_reset(cf, data); + +out: + *pcf = result? NULL : cf; + free(ctx); + return result; +} + +static CURLcode cf_http_connect_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + bool try_h3, bool try_h21) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21); + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +CURLcode Curl_cf_https_setup(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost) +{ + bool try_h3 = FALSE, try_h21 = TRUE; /* defaults, for now */ + CURLcode result = CURLE_OK; + + (void)sockindex; + (void)remotehost; + + if(!conn->bits.tls_enable_alpn) + goto out; + + if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) { + result = Curl_conn_may_http3(data, conn); + if(result) /* cannot do it */ + goto out; + try_h3 = TRUE; + try_h21 = FALSE; + } + else if(data->state.httpwant >= CURL_HTTP_VERSION_3) { + /* We assume that silently not even trying H3 is ok here */ + /* TODO: should we fail instead? */ + try_h3 = (Curl_conn_may_http3(data, conn) == CURLE_OK); + try_h21 = TRUE; + } + + result = cf_http_connect_add(data, conn, sockindex, remotehost, + try_h3, try_h21); +out: + return result; +} + +#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ diff --git a/third_party/curl/lib/cf-https-connect.h b/third_party/curl/lib/cf-https-connect.h new file mode 100644 index 0000000000..6a39527317 --- /dev/null +++ b/third_party/curl/lib/cf-https-connect.h @@ -0,0 +1,58 @@ +#ifndef HEADER_CURL_CF_HTTP_H +#define HEADER_CURL_CF_HTTP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) + +struct Curl_cfilter; +struct Curl_easy; +struct connectdata; +struct Curl_cftype; +struct Curl_dns_entry; + +extern struct Curl_cftype Curl_cft_http_connect; + +CURLcode Curl_cf_http_connect_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + bool try_h3, bool try_h21); + +CURLcode +Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + bool try_h3, bool try_h21); + + +CURLcode Curl_cf_https_setup(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost); + + +#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ +#endif /* HEADER_CURL_CF_HTTP_H */ diff --git a/third_party/curl/lib/cf-socket.c b/third_party/curl/lib/cf-socket.c new file mode 100644 index 0000000000..2e92db29d3 --- /dev/null +++ b/third_party/curl/lib/cf-socket.c @@ -0,0 +1,2143 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include /* may need it */ +#endif +#ifdef HAVE_SYS_UN_H +#include /* for sockaddr_un */ +#endif +#ifdef HAVE_LINUX_TCP_H +#include +#elif defined(HAVE_NETINET_TCP_H) +#include +#endif +#ifdef HAVE_NETINET_UDP_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#ifdef __VMS +#include +#include +#endif + +#ifdef __DragonFly__ +/* Required for __DragonFly_version */ +#include +#endif + +#include "urldata.h" +#include "bufq.h" +#include "sendf.h" +#include "if2ip.h" +#include "strerror.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "connect.h" +#include "select.h" +#include "url.h" /* for Curl_safefree() */ +#include "multiif.h" +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "inet_ntop.h" +#include "inet_pton.h" +#include "progress.h" +#include "warnless.h" +#include "conncache.h" +#include "multihandle.h" +#include "rand.h" +#include "share.h" +#include "strdup.h" +#include "version_win32.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +#if defined(USE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32) +/* It makes support for IPv4-mapped IPv6 addresses. + * Linux kernel, NetBSD, FreeBSD and Darwin: default is off; + * Windows Vista and later: default is on; + * DragonFly BSD: acts like off, and dummy setting; + * OpenBSD and earlier Windows: unsupported. + * Linux: controlled by /proc/sys/net/ipv6/bindv6only. + */ +static void set_ipv6_v6only(curl_socket_t sockfd, int on) +{ + (void)setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on)); +} +#else +#define set_ipv6_v6only(x,y) +#endif + +static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd) +{ +#if defined(TCP_NODELAY) + curl_socklen_t onoff = (curl_socklen_t) 1; + int level = IPPROTO_TCP; + char buffer[STRERROR_LEN]; + + if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, + sizeof(onoff)) < 0) + infof(data, "Could not set TCP_NODELAY: %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); +#else + (void)data; + (void)sockfd; +#endif +} + +#ifdef SO_NOSIGPIPE +/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when + sending data to a dead peer (instead of relying on the 4th argument to send + being MSG_NOSIGNAL). Possibly also existing and in use on other BSD + systems? */ +static void nosigpipe(struct Curl_easy *data, + curl_socket_t sockfd) +{ + int onoff = 1; + (void)data; + if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, + sizeof(onoff)) < 0) { +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + char buffer[STRERROR_LEN]; + infof(data, "Could not set SO_NOSIGPIPE: %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); +#endif + } +} +#else +#define nosigpipe(x,y) Curl_nop_stmt +#endif + +#if defined(USE_WINSOCK) || \ + (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ + (defined(__DragonFly__) && __DragonFly_version < 500702) || \ + (defined(_WIN32) && !defined(TCP_KEEPIDLE)) +/* Solaris < 11.4, DragonFlyBSD < 500702 and Windows < 10.0.16299 + * use millisecond units. */ +#define KEEPALIVE_FACTOR(x) (x *= 1000) +#else +#define KEEPALIVE_FACTOR(x) +#endif + +#if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS) +#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) + +struct tcp_keepalive { + u_long onoff; + u_long keepalivetime; + u_long keepaliveinterval; +}; +#endif + +static void +tcpkeepalive(struct Curl_easy *data, + curl_socket_t sockfd) +{ + int optval = data->set.tcp_keepalive?1:0; + + /* only set IDLE and INTVL if setting KEEPALIVE is successful */ + if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set SO_KEEPALIVE on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } + else { +#if defined(SIO_KEEPALIVE_VALS) /* Windows */ +/* Windows 10, version 1709 (10.0.16299) and later versions */ +#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT) + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, + (const char *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPIDLE on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, + (const char *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPINTVL on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } + optval = curlx_sltosi(data->set.tcp_keepcnt); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, + (const char *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPCNT on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } +#else /* Windows < 10.0.16299 */ + struct tcp_keepalive vals; + DWORD dummy; + vals.onoff = 1; + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + vals.keepalivetime = (u_long)optval; + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + vals.keepaliveinterval = (u_long)optval; + if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), + NULL, 0, &dummy, NULL, NULL) != 0) { + infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } +#endif +#else /* !Windows */ +#ifdef TCP_KEEPIDLE + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPIDLE on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } +#elif defined(TCP_KEEPALIVE) + /* Mac OS X style */ + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPALIVE on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } +#elif defined(TCP_KEEPALIVE_THRESHOLD) + /* Solaris <11.4 style */ + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } +#endif +#ifdef TCP_KEEPINTVL + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPINTVL on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } +#elif defined(TCP_KEEPALIVE_ABORT_THRESHOLD) + /* Solaris <11.4 style */ + /* TCP_KEEPALIVE_ABORT_THRESHOLD should equal to + * TCP_KEEPCNT * TCP_KEEPINTVL on other platforms. + * The default value of TCP_KEEPCNT is 9 on Linux, + * 8 on *BSD/macOS, 5 or 10 on Windows. We use the + * default config for Solaris <11.4 because there is + * no default value for TCP_KEEPCNT on Solaris 11.4. + * + * Note that the consequent probes will not be sent + * at equal intervals on Solaris, but will be sent + * using the exponential backoff algorithm. */ + optval = curlx_sltosi(data->set.tcp_keepcnt) * + curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } +#endif +#ifdef TCP_KEEPCNT + optval = curlx_sltosi(data->set.tcp_keepcnt); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPCNT on fd " + "%" CURL_FORMAT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } +#endif +#endif + } +} + +/** + * Assign the address `ai` to the Curl_sockaddr_ex `dest` and + * set the transport used. + */ +void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, + const struct Curl_addrinfo *ai, + int transport) +{ + /* + * The Curl_sockaddr_ex structure is basically libcurl's external API + * curl_sockaddr structure with enough space available to directly hold + * any protocol-specific address structures. The variable declared here + * will be used to pass / receive data to/from the fopensocket callback + * if this has been set, before that, it is initialized from parameters. + */ + dest->family = ai->ai_family; + switch(transport) { + case TRNSPRT_TCP: + dest->socktype = SOCK_STREAM; + dest->protocol = IPPROTO_TCP; + break; + case TRNSPRT_UNIX: + dest->socktype = SOCK_STREAM; + dest->protocol = IPPROTO_IP; + break; + default: /* UDP and QUIC */ + dest->socktype = SOCK_DGRAM; + dest->protocol = IPPROTO_UDP; + break; + } + dest->addrlen = (unsigned int)ai->ai_addrlen; + + if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) + dest->addrlen = sizeof(struct Curl_sockaddr_storage); + memcpy(&dest->sa_addr, ai->ai_addr, dest->addrlen); +} + +static CURLcode socket_open(struct Curl_easy *data, + struct Curl_sockaddr_ex *addr, + curl_socket_t *sockfd) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + if(data->set.fopensocket) { + /* + * If the opensocket callback is set, all the destination address + * information is passed to the callback. Depending on this information the + * callback may opt to abort the connection, this is indicated returning + * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When + * the callback returns a valid socket the destination address information + * might have been changed and this 'new' address will actually be used + * here to connect. + */ + Curl_set_in_callback(data, true); + *sockfd = data->set.fopensocket(data->set.opensocket_client, + CURLSOCKTYPE_IPCXN, + (struct curl_sockaddr *)addr); + Curl_set_in_callback(data, false); + } + else { + /* opensocket callback not set, so simply create the socket now */ + *sockfd = socket(addr->family, addr->socktype, addr->protocol); + } + + if(*sockfd == CURL_SOCKET_BAD) + /* no socket, no connection */ + return CURLE_COULDNT_CONNECT; + +#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) + if(data->conn->scope_id && (addr->family == AF_INET6)) { + struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr; + sa6->sin6_scope_id = data->conn->scope_id; + } +#endif + return CURLE_OK; +} + +/* + * Create a socket based on info from 'conn' and 'ai'. + * + * 'addr' should be a pointer to the correct struct to get data back, or NULL. + * 'sockfd' must be a pointer to a socket descriptor. + * + * If the open socket callback is set, used that! + * + */ +CURLcode Curl_socket_open(struct Curl_easy *data, + const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, + int transport, + curl_socket_t *sockfd) +{ + struct Curl_sockaddr_ex dummy; + + if(!addr) + /* if the caller does not want info back, use a local temp copy */ + addr = &dummy; + + Curl_sock_assign_addr(addr, ai, transport); + return socket_open(data, addr, sockfd); +} + +static int socket_close(struct Curl_easy *data, struct connectdata *conn, + int use_callback, curl_socket_t sock) +{ + if(use_callback && conn && conn->fclosesocket) { + int rc; + Curl_multi_closed(data, sock); + Curl_set_in_callback(data, true); + rc = conn->fclosesocket(conn->closesocket_client, sock); + Curl_set_in_callback(data, false); + return rc; + } + + if(conn) + /* tell the multi-socket code about this */ + Curl_multi_closed(data, sock); + + sclose(sock); + + return 0; +} + +/* + * Close a socket. + * + * 'conn' can be NULL, beware! + */ +int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sock) +{ + return socket_close(data, conn, FALSE, sock); +} + +#ifdef USE_WINSOCK +/* When you run a program that uses the Windows Sockets API, you may + experience slow performance when you copy data to a TCP server. + + https://support.microsoft.com/kb/823764 + + Work-around: Make the Socket Send Buffer Size Larger Than the Program Send + Buffer Size + + The problem described in this knowledge-base is applied only to pre-Vista + Windows. Following function trying to detect OS version and skips + SO_SNDBUF adjustment for Windows Vista and above. +*/ +#define DETECT_OS_NONE 0 +#define DETECT_OS_PREVISTA 1 +#define DETECT_OS_VISTA_OR_LATER 2 + +void Curl_sndbuf_init(curl_socket_t sockfd) +{ + int val = CURL_MAX_WRITE_SIZE + 32; + int curval = 0; + int curlen = sizeof(curval); + + static int detectOsState = DETECT_OS_NONE; + + if(detectOsState == DETECT_OS_NONE) { + if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) + detectOsState = DETECT_OS_VISTA_OR_LATER; + else + detectOsState = DETECT_OS_PREVISTA; + } + + if(detectOsState == DETECT_OS_VISTA_OR_LATER) + return; + + if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0) + if(curval > val) + return; + + setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val)); +} +#endif /* USE_WINSOCK */ + +/* + * Curl_parse_interface() + * + * This is used to parse interface argument in the following formats. + * In all the examples, `host` can be an IP address or a hostname. + * + * - can be either an interface name or a host. + * if! - interface name. + * host! - hostname. + * ifhost!! - interface name and hostname. + * + * Parameters: + * + * input [in] - input string. + * len [in] - length of the input string. + * dev [in/out] - address where a pointer to newly allocated memory + * holding the interface-or-host will be stored upon + * completion. + * iface [in/out] - address where a pointer to newly allocated memory + * holding the interface will be stored upon completion. + * host [in/out] - address where a pointer to newly allocated memory + * holding the host will be stored upon completion. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_parse_interface(const char *input, size_t len, + char **dev, char **iface, char **host) +{ + static const char if_prefix[] = "if!"; + static const char host_prefix[] = "host!"; + static const char if_host_prefix[] = "ifhost!"; + + DEBUGASSERT(dev); + DEBUGASSERT(iface); + DEBUGASSERT(host); + + if(strncmp(if_prefix, input, strlen(if_prefix)) == 0) { + input += strlen(if_prefix); + if(!*input) + return CURLE_BAD_FUNCTION_ARGUMENT; + *iface = Curl_memdup0(input, len - strlen(if_prefix)); + return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY; + } + if(strncmp(host_prefix, input, strlen(host_prefix)) == 0) { + input += strlen(host_prefix); + if(!*input) + return CURLE_BAD_FUNCTION_ARGUMENT; + *host = Curl_memdup0(input, len - strlen(host_prefix)); + return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY; + } + if(strncmp(if_host_prefix, input, strlen(if_host_prefix)) == 0) { + const char *host_part; + input += strlen(if_host_prefix); + len -= strlen(if_host_prefix); + host_part = memchr(input, '!', len); + if(!host_part || !*(host_part + 1)) + return CURLE_BAD_FUNCTION_ARGUMENT; + *iface = Curl_memdup0(input, host_part - input); + if(!*iface) + return CURLE_OUT_OF_MEMORY; + ++host_part; + *host = Curl_memdup0(host_part, len - (host_part - input)); + if(!*host) { + free(*iface); + *iface = NULL; + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; + } + + if(!*input) + return CURLE_BAD_FUNCTION_ARGUMENT; + *dev = Curl_memdup0(input, len); + return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY; +} + +#ifndef CURL_DISABLE_BINDLOCAL +static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sockfd, int af, unsigned int scope) +{ + struct Curl_sockaddr_storage sa; + struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ + curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */ + struct sockaddr_in *si4 = (struct sockaddr_in *)&sa; +#ifdef USE_IPV6 + struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; +#endif + + struct Curl_dns_entry *h = NULL; + unsigned short port = data->set.localport; /* use this port number, 0 for + "random" */ + /* how many port numbers to try to bind to, increasing one at a time */ + int portnum = data->set.localportrange; + const char *dev = data->set.str[STRING_DEVICE]; + const char *iface_input = data->set.str[STRING_INTERFACE]; + const char *host_input = data->set.str[STRING_BINDHOST]; + const char *iface = iface_input ? iface_input : dev; + const char *host = host_input ? host_input : dev; + int error; +#ifdef IP_BIND_ADDRESS_NO_PORT + int on = 1; +#endif +#ifndef USE_IPV6 + (void)scope; +#endif + + /************************************************************* + * Select device to bind socket to + *************************************************************/ + if(!iface && !host && !port) + /* no local kind of binding was requested */ + return CURLE_OK; + + memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); + + if(iface && (strlen(iface)<255) ) { + char myhost[256] = ""; + int done = 0; /* -1 for error, 1 for address found */ + if2ip_result_t if2ip_result = IF2IP_NOT_FOUND; + + /* interface */ +#ifdef SO_BINDTODEVICE + /* + * This binds the local socket to a particular interface. This will + * force even requests to other local interfaces to go out the external + * interface. Only bind to the interface when specified as interface, + * not just as a hostname or ip address. + * + * The interface might be a VRF, eg: vrf-blue, which means it cannot be + * converted to an IP address and would fail Curl_if2ip. Simply try to + * use it straight away. + */ + if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, + iface, (curl_socklen_t)strlen(iface) + 1) == 0) { + /* This is often "errno 1, error: Operation not permitted" if you are + * not running as root or another suitable privileged user. If it + * succeeds it means the parameter was a valid interface and not an IP + * address. Return immediately. + */ + if(!host_input) { + infof(data, "socket successfully bound to interface '%s'", iface); + return CURLE_OK; + } + } +#endif + if(!host_input) { + /* Discover IP from input device, then bind to it */ + if2ip_result = Curl_if2ip(af, +#ifdef USE_IPV6 + scope, conn->scope_id, +#endif + iface, myhost, sizeof(myhost)); + } + switch(if2ip_result) { + case IF2IP_NOT_FOUND: + if(iface_input && !host_input) { + /* Do not fall back to treating it as a hostname */ + char buffer[STRERROR_LEN]; + data->state.os_errno = error = SOCKERRNO; + failf(data, "Couldn't bind to interface '%s' with errno %d: %s", + iface, error, Curl_strerror(error, buffer, sizeof(buffer))); + return CURLE_INTERFACE_FAILED; + } + break; + case IF2IP_AF_NOT_SUPPORTED: + /* Signal the caller to try another address family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + case IF2IP_FOUND: + /* + * We now have the numerical IP address in the 'myhost' buffer + */ + host = myhost; + infof(data, "Local Interface %s is ip %s using address family %i", + iface, host, af); + done = 1; + break; + } + if(!iface_input || host_input) { + /* + * This was not an interface, resolve the name as a hostname + * or IP number + * + * Temporarily force name resolution to use only the address type + * of the connection. The resolve functions should really be changed + * to take a type parameter instead. + */ + unsigned char ipver = conn->ip_version; + int rc; + + if(af == AF_INET) + conn->ip_version = CURL_IPRESOLVE_V4; +#ifdef USE_IPV6 + else if(af == AF_INET6) + conn->ip_version = CURL_IPRESOLVE_V6; +#endif + + rc = Curl_resolv(data, host, 80, FALSE, &h); + if(rc == CURLRESOLV_PENDING) + (void)Curl_resolver_wait_resolv(data, &h); + conn->ip_version = ipver; + + if(h) { + /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ + Curl_printable_address(h->addr, myhost, sizeof(myhost)); + infof(data, "Name '%s' family %i resolved to '%s' family %i", + host, af, myhost, h->addr->ai_family); + Curl_resolv_unlock(data, h); + if(af != h->addr->ai_family) { + /* bad IP version combo, signal the caller to try another address + family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + } + done = 1; + } + else { + /* + * provided dev was no interface (or interfaces are not supported + * e.g. solaris) no ip address and no domain we fail here + */ + done = -1; + } + } + + if(done > 0) { +#ifdef USE_IPV6 + /* IPv6 address */ + if(af == AF_INET6) { +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + char *scope_ptr = strchr(myhost, '%'); + if(scope_ptr) + *(scope_ptr++) = '\0'; +#endif + if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) { + si6->sin6_family = AF_INET6; + si6->sin6_port = htons(port); +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + if(scope_ptr) { + /* The "myhost" string either comes from Curl_if2ip or from + Curl_printable_address. The latter returns only numeric scope + IDs and the former returns none at all. So the scope ID, if + present, is known to be numeric */ + unsigned long scope_id = strtoul(scope_ptr, NULL, 10); + if(scope_id > UINT_MAX) + return CURLE_UNSUPPORTED_PROTOCOL; + + si6->sin6_scope_id = (unsigned int)scope_id; + } +#endif + } + sizeof_sa = sizeof(struct sockaddr_in6); + } + else +#endif + /* IPv4 address */ + if((af == AF_INET) && + (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) { + si4->sin_family = AF_INET; + si4->sin_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in); + } + } + + if(done < 1) { + /* errorbuf is set false so failf will overwrite any message already in + the error buffer, so the user receives this error message instead of a + generic resolve error. */ + char buffer[STRERROR_LEN]; + data->state.errorbuf = FALSE; + data->state.os_errno = error = SOCKERRNO; + failf(data, "Couldn't bind to '%s' with errno %d: %s", + host, error, Curl_strerror(error, buffer, sizeof(buffer))); + return CURLE_INTERFACE_FAILED; + } + } + else { + /* no device was given, prepare sa to match af's needs */ +#ifdef USE_IPV6 + if(af == AF_INET6) { + si6->sin6_family = AF_INET6; + si6->sin6_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in6); + } + else +#endif + if(af == AF_INET) { + si4->sin_family = AF_INET; + si4->sin_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in); + } + } +#ifdef IP_BIND_ADDRESS_NO_PORT + (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on)); +#endif + for(;;) { + if(bind(sockfd, sock, sizeof_sa) >= 0) { + /* we succeeded to bind */ + infof(data, "Local port: %hu", port); + conn->bits.bound = TRUE; + return CURLE_OK; + } + + if(--portnum > 0) { + port++; /* try next port */ + if(port == 0) + break; + infof(data, "Bind to local port %d failed, trying next", port - 1); + /* We reuse/clobber the port variable here below */ + if(sock->sa_family == AF_INET) + si4->sin_port = ntohs(port); +#ifdef USE_IPV6 + else + si6->sin6_port = ntohs(port); +#endif + } + else + break; + } + { + char buffer[STRERROR_LEN]; + data->state.os_errno = error = SOCKERRNO; + failf(data, "bind failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + } + + return CURLE_INTERFACE_FAILED; +} +#endif + +/* + * verifyconnect() returns TRUE if the connect really has happened. + */ +static bool verifyconnect(curl_socket_t sockfd, int *error) +{ + bool rc = TRUE; +#ifdef SO_ERROR + int err = 0; + curl_socklen_t errSize = sizeof(err); + +#ifdef _WIN32 + /* + * In October 2003 we effectively nullified this function on Windows due to + * problems with it using all CPU in multi-threaded cases. + * + * In May 2004, we bring it back to offer more info back on connect failures. + * Gisle Vanem could reproduce the former problems with this function, but + * could avoid them by adding this SleepEx() call below: + * + * "I do not have Rational Quantify, but the hint from his post was + * ntdll::NtRemoveIoCompletion(). I would assume the SleepEx (or maybe + * just Sleep(0) would be enough?) would release whatever + * mutex/critical-section the ntdll call is waiting on. + * + * Someone got to verify this on Win-NT 4.0, 2000." + */ + +#ifdef _WIN32_WCE + Sleep(0); +#else + SleepEx(0, FALSE); +#endif + +#endif + + if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) + err = SOCKERRNO; +#ifdef _WIN32_WCE + /* Old WinCE versions do not support SO_ERROR */ + if(WSAENOPROTOOPT == err) { + SET_SOCKERRNO(0); + err = 0; + } +#endif +#if defined(EBADIOCTL) && defined(__minix) + /* Minix 3.1.x does not support getsockopt on UDP sockets */ + if(EBADIOCTL == err) { + SET_SOCKERRNO(0); + err = 0; + } +#endif + if((0 == err) || (EISCONN == err)) + /* we are connected, awesome! */ + rc = TRUE; + else + /* This was not a successful connect */ + rc = FALSE; + if(error) + *error = err; +#else + (void)sockfd; + if(error) + *error = SOCKERRNO; +#endif + return rc; +} + +/** + * Determine the curl code for a socket connect() == -1 with errno. + */ +static CURLcode socket_connect_result(struct Curl_easy *data, + const char *ipaddress, int error) +{ + switch(error) { + case EINPROGRESS: + case EWOULDBLOCK: +#if defined(EAGAIN) +#if (EAGAIN) != (EWOULDBLOCK) + /* On some platforms EAGAIN and EWOULDBLOCK are the + * same value, and on others they are different, hence + * the odd #if + */ + case EAGAIN: +#endif +#endif + return CURLE_OK; + + default: + /* unknown error, fallthrough and try another address! */ +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)ipaddress; +#else + { + char buffer[STRERROR_LEN]; + infof(data, "Immediate connect fail for %s: %s", + ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); + } +#endif + data->state.os_errno = error; + /* connect failed */ + return CURLE_COULDNT_CONNECT; + } +} + +/* We have a recv buffer to enhance reads with len < NW_SMALL_READS. + * This happens often on TLS connections where the TLS implementation + * tries to read the head of a TLS record, determine the length of the + * full record and then make a subsequent read for that. + * On large reads, we will not fill the buffer to avoid the double copy. */ +#define NW_RECV_CHUNK_SIZE (64 * 1024) +#define NW_RECV_CHUNKS 1 +#define NW_SMALL_READS (1024) + +struct cf_socket_ctx { + int transport; + struct Curl_sockaddr_ex addr; /* address to connect to */ + curl_socket_t sock; /* current attempt socket */ + struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */ + struct curltime started_at; /* when socket was created */ + struct curltime connected_at; /* when socket connected/got first byte */ + struct curltime first_byte_at; /* when first byte was recvd */ +#ifdef USE_WINSOCK + struct curltime last_sndbuf_query_at; /* when SO_SNDBUF last queried */ + ULONG sndbuf_size; /* the last set SO_SNDBUF size */ +#endif + int error; /* errno of last failure or 0 */ +#ifdef DEBUGBUILD + int wblock_percent; /* percent of writes doing EAGAIN */ + int wpartial_percent; /* percent of bytes written in send */ + int rblock_percent; /* percent of reads doing EAGAIN */ + size_t recv_max; /* max enforced read size */ +#endif + BIT(got_first_byte); /* if first byte was received */ + BIT(accepted); /* socket was accepted, not connected */ + BIT(sock_connected); /* socket is "connected", e.g. in UDP */ + BIT(active); +}; + +static void cf_socket_ctx_init(struct cf_socket_ctx *ctx, + const struct Curl_addrinfo *ai, + int transport) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->sock = CURL_SOCKET_BAD; + ctx->transport = transport; + Curl_sock_assign_addr(&ctx->addr, ai, transport); +#ifdef DEBUGBUILD + { + char *p = getenv("CURL_DBG_SOCK_WBLOCK"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0 && l <= 100) + ctx->wblock_percent = (int)l; + } + p = getenv("CURL_DBG_SOCK_WPARTIAL"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0 && l <= 100) + ctx->wpartial_percent = (int)l; + } + p = getenv("CURL_DBG_SOCK_RBLOCK"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0 && l <= 100) + ctx->rblock_percent = (int)l; + } + p = getenv("CURL_DBG_SOCK_RMAX"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0) + ctx->recv_max = (size_t)l; + } + } +#endif +} + +static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + if(ctx && CURL_SOCKET_BAD != ctx->sock) { + CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T + ")", ctx->sock); + if(ctx->sock == cf->conn->sock[cf->sockindex]) + cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; + socket_close(data, cf->conn, !ctx->accepted, ctx->sock); + ctx->sock = CURL_SOCKET_BAD; + if(ctx->active && cf->sockindex == FIRSTSOCKET) + cf->conn->remote_addr = NULL; + ctx->active = FALSE; + memset(&ctx->started_at, 0, sizeof(ctx->started_at)); + memset(&ctx->connected_at, 0, sizeof(ctx->connected_at)); + } + + cf->connected = FALSE; +} + +static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + if(cf->connected) { + struct cf_socket_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "cf_socket_shutdown(%" CURL_FORMAT_SOCKET_T + ")", ctx->sock); + /* On TCP, and when the socket looks well and non-blocking mode + * can be enabled, receive dangling bytes before close to avoid + * entering RST states unnecessarily. */ + if(ctx->sock != CURL_SOCKET_BAD && + ctx->transport == TRNSPRT_TCP && + (curlx_nonblock(ctx->sock, TRUE) >= 0)) { + unsigned char buf[1024]; + (void)sread(ctx->sock, buf, sizeof(buf)); + } + } + *done = TRUE; + return CURLE_OK; +} + +static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + cf_socket_close(cf, data); + CURL_TRC_CF(data, cf, "destroy"); + free(ctx); + cf->ctx = NULL; +} + +static CURLcode set_local_ip(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + +#ifdef HAVE_GETSOCKNAME + if((ctx->sock != CURL_SOCKET_BAD) && + !(data->conn->handler->protocol & CURLPROTO_TFTP)) { + /* TFTP does not connect, so it cannot get the IP like this */ + + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssloc; + curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); + + memset(&ssloc, 0, sizeof(ssloc)); + if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) { + int error = SOCKERRNO; + failf(data, "getsockname() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return CURLE_FAILED_INIT; + } + if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, + ctx->ip.local_ip, &ctx->ip.local_port)) { + failf(data, "ssloc inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return CURLE_FAILED_INIT; + } + } +#else + (void)data; + ctx->ip.local_ip[0] = 0; + ctx->ip.local_port = -1; +#endif + return CURLE_OK; +} + +static CURLcode set_remote_ip(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + /* store remote address and port used in this connection attempt */ + if(!Curl_addr2string(&ctx->addr.sa_addr, (curl_socklen_t)ctx->addr.addrlen, + ctx->ip.remote_ip, &ctx->ip.remote_port)) { + char buffer[STRERROR_LEN]; + + ctx->error = errno; + /* malformed address or bug in inet_ntop, try next address */ + failf(data, "sa_addr inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return CURLE_FAILED_INIT; + } + return CURLE_OK; +} + +static CURLcode cf_socket_open(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + int error = 0; + bool isconnected = FALSE; + CURLcode result = CURLE_COULDNT_CONNECT; + bool is_tcp; + + (void)data; + DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD); + ctx->started_at = Curl_now(); +#ifdef SOCK_NONBLOCK + /* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set + * because we would not know how socketype is about to be used in the + * callback, SOCK_NONBLOCK might get factored out before calling socket(). + */ + if(!data->set.fopensocket) + ctx->addr.socktype |= SOCK_NONBLOCK; +#endif + result = socket_open(data, &ctx->addr, &ctx->sock); +#ifdef SOCK_NONBLOCK + /* Restore the socktype after the socket is created. */ + if(!data->set.fopensocket) + ctx->addr.socktype &= ~SOCK_NONBLOCK; +#endif + if(result) + goto out; + + result = set_remote_ip(cf, data); + if(result) + goto out; + +#ifdef USE_IPV6 + if(ctx->addr.family == AF_INET6) { + set_ipv6_v6only(ctx->sock, 0); + infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port); + } + else +#endif + infof(data, " Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port); + +#ifdef USE_IPV6 + is_tcp = (ctx->addr.family == AF_INET + || ctx->addr.family == AF_INET6) && + ctx->addr.socktype == SOCK_STREAM; +#else + is_tcp = (ctx->addr.family == AF_INET) && + ctx->addr.socktype == SOCK_STREAM; +#endif + if(is_tcp && data->set.tcp_nodelay) + tcpnodelay(data, ctx->sock); + + nosigpipe(data, ctx->sock); + + Curl_sndbuf_init(ctx->sock); + + if(is_tcp && data->set.tcp_keepalive) + tcpkeepalive(data, ctx->sock); + + if(data->set.fsockopt) { + /* activate callback for setting socket options */ + Curl_set_in_callback(data, true); + error = data->set.fsockopt(data->set.sockopt_client, + ctx->sock, + CURLSOCKTYPE_IPCXN); + Curl_set_in_callback(data, false); + + if(error == CURL_SOCKOPT_ALREADY_CONNECTED) + isconnected = TRUE; + else if(error) { + result = CURLE_ABORTED_BY_CALLBACK; + goto out; + } + } + +#ifndef CURL_DISABLE_BINDLOCAL + /* possibly bind the local end to an IP, interface or port */ + if(ctx->addr.family == AF_INET +#ifdef USE_IPV6 + || ctx->addr.family == AF_INET6 +#endif + ) { + result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family, + Curl_ipv6_scope(&ctx->addr.sa_addr)); + if(result) { + if(result == CURLE_UNSUPPORTED_PROTOCOL) { + /* The address family is not supported on this interface. + We can continue trying addresses */ + result = CURLE_COULDNT_CONNECT; + } + goto out; + } + } +#endif + +#ifndef SOCK_NONBLOCK + /* Set socket non-blocking, must be a non-blocking socket for + * a non-blocking connect. */ + error = curlx_nonblock(ctx->sock, TRUE); + if(error < 0) { + result = CURLE_UNSUPPORTED_PROTOCOL; + ctx->error = SOCKERRNO; + goto out; + } +#else + if(data->set.fopensocket) { + /* Set socket non-blocking, must be a non-blocking socket for + * a non-blocking connect. */ + error = curlx_nonblock(ctx->sock, TRUE); + if(error < 0) { + result = CURLE_UNSUPPORTED_PROTOCOL; + ctx->error = SOCKERRNO; + goto out; + } + } +#endif + ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM); +out: + if(result) { + if(ctx->sock != CURL_SOCKET_BAD) { + socket_close(data, cf->conn, TRUE, ctx->sock); + ctx->sock = CURL_SOCKET_BAD; + } + } + else if(isconnected) { + set_local_ip(cf, data); + ctx->connected_at = Curl_now(); + cf->connected = TRUE; + } + CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T, + result, ctx->sock); + return result; +} + +static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data, + bool is_tcp_fastopen) +{ + struct cf_socket_ctx *ctx = cf->ctx; +#ifdef TCP_FASTOPEN_CONNECT + int optval = 1; +#endif + int rc = -1; + + (void)data; + if(is_tcp_fastopen) { +#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */ +# if defined(HAVE_BUILTIN_AVAILABLE) + /* while connectx function is available since macOS 10.11 / iOS 9, + it did not have the interface declared correctly until + Xcode 9 / macOS SDK 10.13 */ + if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) { + sa_endpoints_t endpoints; + endpoints.sae_srcif = 0; + endpoints.sae_srcaddr = NULL; + endpoints.sae_srcaddrlen = 0; + endpoints.sae_dstaddr = &ctx->addr.sa_addr; + endpoints.sae_dstaddrlen = ctx->addr.addrlen; + + rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY, + CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, + NULL, 0, NULL, NULL); + } + else { + rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); + } +# else + rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); +# endif /* HAVE_BUILTIN_AVAILABLE */ +#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */ + if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + (void *)&optval, sizeof(optval)) < 0) + infof(data, "Failed to enable TCP Fast Open on fd %" + CURL_FORMAT_SOCKET_T, ctx->sock); + + rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); +#elif defined(MSG_FASTOPEN) /* old Linux */ + if(cf->conn->given->flags & PROTOPT_SSL) + rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); + else + rc = 0; /* Do nothing */ +#endif + } + else { + rc = connect(ctx->sock, &ctx->addr.sa_addr, + (curl_socklen_t)ctx->addr.addrlen); + } + return rc; +} + +static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_socket_ctx *ctx = cf->ctx; + CURLcode result = CURLE_COULDNT_CONNECT; + int rc = 0; + + (void)data; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* TODO: need to support blocking connect? */ + if(blocking) + return CURLE_UNSUPPORTED_PROTOCOL; + + *done = FALSE; /* a very negative world view is best */ + if(ctx->sock == CURL_SOCKET_BAD) { + int error; + + result = cf_socket_open(cf, data); + if(result) + goto out; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* Connect TCP socket */ + rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen); + error = SOCKERRNO; + set_local_ip(cf, data); + CURL_TRC_CF(data, cf, "local address %s port %d...", + ctx->ip.local_ip, ctx->ip.local_port); + if(-1 == rc) { + result = socket_connect_result(data, ctx->ip.remote_ip, error); + goto out; + } + } + +#ifdef mpeix + /* Call this function once now, and ignore the results. We do this to + "clear" the error state on the socket so that we can later read it + reliably. This is reported necessary on the MPE/iX operating + system. */ + (void)verifyconnect(ctx->sock, NULL); +#endif + /* check socket for connect */ + rc = SOCKET_WRITABLE(ctx->sock, 0); + + if(rc == 0) { /* no connection yet */ + CURL_TRC_CF(data, cf, "not connected yet"); + return CURLE_OK; + } + else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) { + if(verifyconnect(ctx->sock, &ctx->error)) { + /* we are connected with TCP, awesome! */ + ctx->connected_at = Curl_now(); + set_local_ip(cf, data); + *done = TRUE; + cf->connected = TRUE; + CURL_TRC_CF(data, cf, "connected"); + return CURLE_OK; + } + } + else if(rc & CURL_CSELECT_ERR) { + (void)verifyconnect(ctx->sock, &ctx->error); + result = CURLE_COULDNT_CONNECT; + } + +out: + if(result) { + if(ctx->error) { + set_local_ip(cf, data); + data->state.os_errno = ctx->error; + SET_SOCKERRNO(ctx->error); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + { + char buffer[STRERROR_LEN]; + infof(data, "connect to %s port %u from %s port %d failed: %s", + ctx->ip.remote_ip, ctx->ip.remote_port, + ctx->ip.local_ip, ctx->ip.local_port, + Curl_strerror(ctx->error, buffer, sizeof(buffer))); + } +#endif + } + if(ctx->sock != CURL_SOCKET_BAD) { + socket_close(data, cf->conn, TRUE, ctx->sock); + ctx->sock = CURL_SOCKET_BAD; + } + *done = FALSE; + } + return result; +} + +static void cf_socket_get_host(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char **phost, + const char **pdisplay_host, + int *pport) +{ + struct cf_socket_ctx *ctx = cf->ctx; + (void)data; + *phost = cf->conn->host.name; + *pdisplay_host = cf->conn->host.dispname; + *pport = ctx->ip.remote_port; +} + +static void cf_socket_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + if(ctx->sock != CURL_SOCKET_BAD) { + if(!cf->connected) { + Curl_pollset_set_out_only(data, ps, ctx->sock); + CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%" + CURL_FORMAT_SOCKET_T, ctx->sock); + } + else if(!ctx->active) { + Curl_pollset_add_in(data, ps, ctx->sock); + CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%" + CURL_FORMAT_SOCKET_T, ctx->sock); + } + } +} + +static bool cf_socket_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + int readable; + + (void)data; + readable = SOCKET_READABLE(ctx->sock, 0); + return (readable > 0 && (readable & CURL_CSELECT_IN)); +} + +#ifdef USE_WINSOCK + +#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY +#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B +#endif + +static void win_update_sndbuf_size(struct cf_socket_ctx *ctx) +{ + ULONG ideal; + DWORD ideallen; + struct curltime n = Curl_now(); + + if(Curl_timediff(n, ctx->last_sndbuf_query_at) > 1000) { + if(!WSAIoctl(ctx->sock, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0, + &ideal, sizeof(ideal), &ideallen, 0, 0) && + ideal != ctx->sndbuf_size && + !setsockopt(ctx->sock, SOL_SOCKET, SO_SNDBUF, + (const char *)&ideal, sizeof(ideal))) { + ctx->sndbuf_size = ideal; + } + ctx->last_sndbuf_query_at = n; + } +} + +#endif /* USE_WINSOCK */ + +static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_socket_ctx *ctx = cf->ctx; + curl_socket_t fdsave; + ssize_t nwritten; + size_t orig_len = len; + + *err = CURLE_OK; + fdsave = cf->conn->sock[cf->sockindex]; + cf->conn->sock[cf->sockindex] = ctx->sock; + +#ifdef DEBUGBUILD + /* simulate network blocking/partial writes */ + if(ctx->wblock_percent > 0) { + unsigned char c = 0; + Curl_rand(data, &c, 1); + if(c >= ((100-ctx->wblock_percent)*256/100)) { + CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len); + *err = CURLE_AGAIN; + nwritten = -1; + cf->conn->sock[cf->sockindex] = fdsave; + return nwritten; + } + } + if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) { + len = len * ctx->wpartial_percent / 100; + if(!len) + len = 1; + CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes", + orig_len, len); + } +#endif + +#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ + if(cf->conn->bits.tcp_fastopen) { + nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN, + &cf->conn->remote_addr->sa_addr, + cf->conn->remote_addr->addrlen); + cf->conn->bits.tcp_fastopen = FALSE; + } + else +#endif + nwritten = swrite(ctx->sock, buf, len); + + if(-1 == nwritten) { + int sockerr = SOCKERRNO; + + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == sockerr) +#else + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned + due to its inability to send off data without blocking. We therefore + treat both error codes the same here */ + (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) || + (EINPROGRESS == sockerr) +#endif + ) { + /* this is just a case of EWOULDBLOCK */ + *err = CURLE_AGAIN; + } + else { + char buffer[STRERROR_LEN]; + failf(data, "Send failure: %s", + Curl_strerror(sockerr, buffer, sizeof(buffer))); + data->state.os_errno = sockerr; + *err = CURLE_SEND_ERROR; + } + } + +#if defined(USE_WINSOCK) + if(!*err) + win_update_sndbuf_size(ctx); +#endif + + CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d", + orig_len, (int)nwritten, *err); + cf->conn->sock[cf->sockindex] = fdsave; + return nwritten; +} + +static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct cf_socket_ctx *ctx = cf->ctx; + ssize_t nread; + + *err = CURLE_OK; + +#ifdef DEBUGBUILD + /* simulate network blocking/partial reads */ + if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) { + unsigned char c = 0; + Curl_rand(data, &c, 1); + if(c >= ((100-ctx->rblock_percent)*256/100)) { + CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len); + *err = CURLE_AGAIN; + return -1; + } + } + if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) { + size_t orig_len = len; + len = ctx->recv_max; + CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes", + orig_len, len); + } +#endif + + *err = CURLE_OK; + nread = sread(ctx->sock, buf, len); + + if(-1 == nread) { + int sockerr = SOCKERRNO; + + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == sockerr) +#else + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned + due to its inability to send off data without blocking. We therefore + treat both error codes the same here */ + (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) +#endif + ) { + /* this is just a case of EWOULDBLOCK */ + *err = CURLE_AGAIN; + } + else { + char buffer[STRERROR_LEN]; + + failf(data, "Recv failure: %s", + Curl_strerror(sockerr, buffer, sizeof(buffer))); + data->state.os_errno = sockerr; + *err = CURLE_RECV_ERROR; + } + } + + CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread, + *err); + if(nread > 0 && !ctx->got_first_byte) { + ctx->first_byte_at = Curl_now(); + ctx->got_first_byte = TRUE; + } + return nread; +} + +static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + /* use this socket from now on */ + cf->conn->sock[cf->sockindex] = ctx->sock; + set_local_ip(cf, data); + if(cf->sockindex == SECONDARYSOCKET) + cf->conn->secondary = ctx->ip; + else + cf->conn->primary = ctx->ip; + /* the first socket info gets some specials */ + if(cf->sockindex == FIRSTSOCKET) { + cf->conn->remote_addr = &ctx->addr; + #ifdef USE_IPV6 + cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE; + #endif + Curl_persistconninfo(data, cf->conn, &ctx->ip); + } + ctx->active = TRUE; +} + +static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_CONN_INFO_UPDATE: + cf_socket_active(cf, data); + break; + case CF_CTRL_DATA_SETUP: + Curl_persistconninfo(data, cf->conn, &ctx->ip); + break; + case CF_CTRL_FORGET_SOCKET: + ctx->sock = CURL_SOCKET_BAD; + break; + } + return CURLE_OK; +} + +static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_socket_ctx *ctx = cf->ctx; + struct pollfd pfd[1]; + int r; + + *input_pending = FALSE; + (void)data; + if(!ctx || ctx->sock == CURL_SOCKET_BAD) + return FALSE; + + /* Check with 0 timeout if there are any events pending on the socket */ + pfd[0].fd = ctx->sock; + pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; + pfd[0].revents = 0; + + r = Curl_poll(pfd, 1, 0); + if(r < 0) { + CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead"); + return FALSE; + } + else if(r == 0) { + CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive"); + return TRUE; + } + else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) { + CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead"); + return FALSE; + } + + CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive"); + *input_pending = TRUE; + return TRUE; +} + +static CURLcode cf_socket_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + switch(query) { + case CF_QUERY_SOCKET: + DEBUGASSERT(pres2); + *((curl_socket_t *)pres2) = ctx->sock; + return CURLE_OK; + case CF_QUERY_CONNECT_REPLY_MS: + if(ctx->got_first_byte) { + timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); + *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; + } + else + *pres1 = -1; + return CURLE_OK; + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + switch(ctx->transport) { + case TRNSPRT_UDP: + case TRNSPRT_QUIC: + /* Since UDP connected sockets work different from TCP, we use the + * time of the first byte from the peer as the "connect" time. */ + if(ctx->got_first_byte) { + *when = ctx->first_byte_at; + break; + } + FALLTHROUGH(); + default: + *when = ctx->connected_at; + break; + } + return CURLE_OK; + } + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +struct Curl_cftype Curl_cft_tcp = { + "TCP", + CF_TYPE_IP_CONNECT, + CURL_LOG_LVL_NONE, + cf_socket_destroy, + cf_tcp_connect, + cf_socket_close, + cf_socket_shutdown, + cf_socket_get_host, + cf_socket_adjust_pollset, + cf_socket_data_pending, + cf_socket_send, + cf_socket_recv, + cf_socket_cntrl, + cf_socket_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_socket_query, +}; + +CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport) +{ + struct cf_socket_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + (void)data; + (void)conn; + DEBUGASSERT(transport == TRNSPRT_TCP); + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf_socket_ctx_init(ctx, ai, transport); + + result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx); + +out: + *pcf = (!result)? cf : NULL; + if(result) { + Curl_safefree(cf); + Curl_safefree(ctx); + } + + return result; +} + +static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + int rc; + int one = 1; + + (void)one; + + /* QUIC needs a connected socket, nonblocking */ + DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD); + + rc = connect(ctx->sock, &ctx->addr.sa_addr, + (curl_socklen_t)ctx->addr.addrlen); + if(-1 == rc) { + return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO); + } + ctx->sock_connected = TRUE; + set_local_ip(cf, data); + CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T + " connected: [%s:%d] -> [%s:%d]", + (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP", + ctx->sock, ctx->ip.local_ip, ctx->ip.local_port, + ctx->ip.remote_ip, ctx->ip.remote_port); + + /* Currently, cf->ctx->sock is always non-blocking because the only + * caller to cf_udp_setup_quic() is cf_udp_connect() that passes the + * non-blocking socket created by cf_socket_open() to it. Thus, we + * do not need to call curlx_nonblock() in cf_udp_setup_quic() anymore. + */ + switch(ctx->addr.family) { +#if defined(__linux__) && defined(IP_MTU_DISCOVER) + case AF_INET: { + int val = IP_PMTUDISC_DO; + (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, + sizeof(val)); + break; + } +#endif +#if defined(__linux__) && defined(IPV6_MTU_DISCOVER) + case AF_INET6: { + int val = IPV6_PMTUDISC_DO; + (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, + sizeof(val)); + break; + } +#endif + } + +#if defined(__linux__) && defined(UDP_GRO) && \ + (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \ + ((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE)) + (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one, + (socklen_t)sizeof(one)); +#endif + + return CURLE_OK; +} + +static CURLcode cf_udp_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_socket_ctx *ctx = cf->ctx; + CURLcode result = CURLE_COULDNT_CONNECT; + + (void)blocking; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + *done = FALSE; + if(ctx->sock == CURL_SOCKET_BAD) { + result = cf_socket_open(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result); + goto out; + } + + if(ctx->transport == TRNSPRT_QUIC) { + result = cf_udp_setup_quic(cf, data); + if(result) + goto out; + CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" + CURL_FORMAT_SOCKET_T " (%s:%d)", + ctx->sock, ctx->ip.local_ip, ctx->ip.local_port); + } + else { + CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" + CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock); + } + *done = TRUE; + cf->connected = TRUE; + } +out: + return result; +} + +struct Curl_cftype Curl_cft_udp = { + "UDP", + CF_TYPE_IP_CONNECT, + CURL_LOG_LVL_NONE, + cf_socket_destroy, + cf_udp_connect, + cf_socket_close, + cf_socket_shutdown, + cf_socket_get_host, + cf_socket_adjust_pollset, + cf_socket_data_pending, + cf_socket_send, + cf_socket_recv, + cf_socket_cntrl, + cf_socket_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_socket_query, +}; + +CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport) +{ + struct cf_socket_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + (void)data; + (void)conn; + DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC); + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf_socket_ctx_init(ctx, ai, transport); + + result = Curl_cf_create(&cf, &Curl_cft_udp, ctx); + +out: + *pcf = (!result)? cf : NULL; + if(result) { + Curl_safefree(cf); + Curl_safefree(ctx); + } + + return result; +} + +/* this is the TCP filter which can also handle this case */ +struct Curl_cftype Curl_cft_unix = { + "UNIX", + CF_TYPE_IP_CONNECT, + CURL_LOG_LVL_NONE, + cf_socket_destroy, + cf_tcp_connect, + cf_socket_close, + cf_socket_shutdown, + cf_socket_get_host, + cf_socket_adjust_pollset, + cf_socket_data_pending, + cf_socket_send, + cf_socket_recv, + cf_socket_cntrl, + cf_socket_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_socket_query, +}; + +CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport) +{ + struct cf_socket_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + (void)data; + (void)conn; + DEBUGASSERT(transport == TRNSPRT_UNIX); + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf_socket_ctx_init(ctx, ai, transport); + + result = Curl_cf_create(&cf, &Curl_cft_unix, ctx); + +out: + *pcf = (!result)? cf : NULL; + if(result) { + Curl_safefree(cf); + Curl_safefree(ctx); + } + + return result; +} + +static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + /* we start accepted, if we ever close, we cannot go on */ + (void)data; + (void)blocking; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + return CURLE_FAILED_INIT; +} + +struct Curl_cftype Curl_cft_tcp_accept = { + "TCP-ACCEPT", + CF_TYPE_IP_CONNECT, + CURL_LOG_LVL_NONE, + cf_socket_destroy, + cf_tcp_accept_connect, + cf_socket_close, + cf_socket_shutdown, + cf_socket_get_host, /* TODO: not accurate */ + cf_socket_adjust_pollset, + cf_socket_data_pending, + cf_socket_send, + cf_socket_recv, + cf_socket_cntrl, + cf_socket_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_socket_query, +}; + +CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, curl_socket_t *s) +{ + CURLcode result; + struct Curl_cfilter *cf = NULL; + struct cf_socket_ctx *ctx = NULL; + + /* replace any existing */ + Curl_conn_cf_discard_all(data, conn, sockindex); + DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD); + + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->transport = conn->transport; + ctx->sock = *s; + ctx->accepted = FALSE; + result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx); + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); + + conn->sock[sockindex] = ctx->sock; + set_local_ip(cf, data); + ctx->active = TRUE; + ctx->connected_at = Curl_now(); + cf->connected = TRUE; + CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%" + CURL_FORMAT_SOCKET_T ")", ctx->sock); + +out: + if(result) { + Curl_safefree(cf); + Curl_safefree(ctx); + } + return result; +} + +static void set_accepted_remote_ip(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; +#ifdef HAVE_GETPEERNAME + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssrem; + curl_socklen_t plen; + + ctx->ip.remote_ip[0] = 0; + ctx->ip.remote_port = 0; + plen = sizeof(ssrem); + memset(&ssrem, 0, plen); + if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { + int error = SOCKERRNO; + failf(data, "getpeername() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return; + } + if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, + ctx->ip.remote_ip, &ctx->ip.remote_port)) { + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return; + } +#else + ctx->ip.remote_ip[0] = 0; + ctx->ip.remote_port = 0; + (void)data; +#endif +} + +CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, curl_socket_t *s) +{ + struct Curl_cfilter *cf = NULL; + struct cf_socket_ctx *ctx = NULL; + + cf = conn->cfilter[sockindex]; + if(!cf || cf->cft != &Curl_cft_tcp_accept) + return CURLE_FAILED_INIT; + + ctx = cf->ctx; + /* discard the listen socket */ + socket_close(data, conn, TRUE, ctx->sock); + ctx->sock = *s; + conn->sock[sockindex] = ctx->sock; + set_accepted_remote_ip(cf, data); + set_local_ip(cf, data); + ctx->active = TRUE; + ctx->accepted = TRUE; + ctx->connected_at = Curl_now(); + cf->connected = TRUE; + CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T + ", remote=%s port=%d)", + ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port); + + return CURLE_OK; +} + +/** + * Return TRUE iff `cf` is a socket filter. + */ +static bool cf_is_socket(struct Curl_cfilter *cf) +{ + return cf && (cf->cft == &Curl_cft_tcp || + cf->cft == &Curl_cft_udp || + cf->cft == &Curl_cft_unix || + cf->cft == &Curl_cft_tcp_accept); +} + +CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *psock, + const struct Curl_sockaddr_ex **paddr, + struct ip_quadruple *pip) +{ + (void)data; + if(cf_is_socket(cf) && cf->ctx) { + struct cf_socket_ctx *ctx = cf->ctx; + + if(psock) + *psock = ctx->sock; + if(paddr) + *paddr = &ctx->addr; + if(pip) + *pip = ctx->ip; + return CURLE_OK; + } + return CURLE_FAILED_INIT; +} diff --git a/third_party/curl/lib/cf-socket.h b/third_party/curl/lib/cf-socket.h new file mode 100644 index 0000000000..6040058b09 --- /dev/null +++ b/third_party/curl/lib/cf-socket.h @@ -0,0 +1,176 @@ +#ifndef HEADER_CURL_CF_SOCKET_H +#define HEADER_CURL_CF_SOCKET_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */ +#include "sockaddr.h" + +struct Curl_addrinfo; +struct Curl_cfilter; +struct Curl_easy; +struct connectdata; +struct Curl_sockaddr_ex; +struct ip_quadruple; + +/* + * The Curl_sockaddr_ex structure is basically libcurl's external API + * curl_sockaddr structure with enough space available to directly hold any + * protocol-specific address structures. The variable declared here will be + * used to pass / receive data to/from the fopensocket callback if this has + * been set, before that, it is initialized from parameters. + */ +struct Curl_sockaddr_ex { + int family; + int socktype; + int protocol; + unsigned int addrlen; + union { + struct sockaddr addr; + struct Curl_sockaddr_storage buff; + } _sa_ex_u; +}; +#define sa_addr _sa_ex_u.addr + +/* + * Parse interface option, and return the interface name and the host part. +*/ +CURLcode Curl_parse_interface(const char *input, size_t len, + char **dev, char **iface, char **host); + +/* + * Create a socket based on info from 'conn' and 'ai'. + * + * Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open + * socket callback is set, used that! + * + */ +CURLcode Curl_socket_open(struct Curl_easy *data, + const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, + int transport, + curl_socket_t *sockfd); + +int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sock); + +#ifdef USE_WINSOCK +/* When you run a program that uses the Windows Sockets API, you may + experience slow performance when you copy data to a TCP server. + + https://support.microsoft.com/kb/823764 + + Work-around: Make the Socket Send Buffer Size Larger Than the Program Send + Buffer Size + +*/ +void Curl_sndbuf_init(curl_socket_t sockfd); +#else +#define Curl_sndbuf_init(y) Curl_nop_stmt +#endif + +/** + * Assign the address `ai` to the Curl_sockaddr_ex `dest` and + * set the transport used. + */ +void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, + const struct Curl_addrinfo *ai, + int transport); + +/** + * Creates a cfilter that opens a TCP socket to the given address + * when calling its `connect` implementation. + * The filter will not touch any connection/data flags and can be + * used in happy eyeballing. Once selected for use, its `_active()` + * method needs to be called. + */ +CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport); + +/** + * Creates a cfilter that opens a UDP socket to the given address + * when calling its `connect` implementation. + * The filter will not touch any connection/data flags and can be + * used in happy eyeballing. Once selected for use, its `_active()` + * method needs to be called. + */ +CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport); + +/** + * Creates a cfilter that opens a UNIX socket to the given address + * when calling its `connect` implementation. + * The filter will not touch any connection/data flags and can be + * used in happy eyeballing. Once selected for use, its `_active()` + * method needs to be called. + */ +CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport); + +/** + * Creates a cfilter that keeps a listening socket. + */ +CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + curl_socket_t *s); + +/** + * Replace the listen socket with the accept()ed one. + */ +CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + curl_socket_t *s); + +/** + * Peek at the socket and remote ip/port the socket filter is using. + * The filter owns all returned values. + * @param psock pointer to hold socket descriptor or NULL + * @param paddr pointer to hold addr reference or NULL + * @param pip pointer to get IP quadruple or NULL + * Returns error if the filter is of invalid type. + */ +CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *psock, + const struct Curl_sockaddr_ex **paddr, + struct ip_quadruple *pip); + +extern struct Curl_cftype Curl_cft_tcp; +extern struct Curl_cftype Curl_cft_udp; +extern struct Curl_cftype Curl_cft_unix; +extern struct Curl_cftype Curl_cft_tcp_accept; + +#endif /* HEADER_CURL_CF_SOCKET_H */ diff --git a/third_party/curl/lib/cfilters.c b/third_party/curl/lib/cfilters.c new file mode 100644 index 0000000000..9a5c3578d7 --- /dev/null +++ b/third_party/curl/lib/cfilters.c @@ -0,0 +1,966 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "urldata.h" +#include "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 "select.h" +#include "warnless.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#ifdef UNITTESTS +/* used by unit2600.c */ +void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + cf->connected = FALSE; + if(cf->next) + cf->next->cft->do_close(cf->next, data); +} +#endif + +CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + (void)cf; + (void)data; + *done = TRUE; + return CURLE_OK; +} + +static void conn_report_connect_stats(struct Curl_easy *data, + struct connectdata *conn); + +void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, + const char **phost, const char **pdisplay_host, + int *pport) +{ + if(cf->next) + cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); + else { + *phost = cf->conn->host.name; + *pdisplay_host = cf->conn->host.dispname; + *pport = cf->conn->primary.remote_port; + } +} + +void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + /* NOP */ + (void)cf; + (void)data; + (void)ps; +} + +bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + return cf->next? + cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + return cf->next? + cf->next->cft->do_send(cf->next, data, buf, len, err) : + CURLE_RECV_ERROR; +} + +ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + return cf->next? + cf->next->cft->do_recv(cf->next, data, buf, len, err) : + CURLE_SEND_ERROR; +} + +bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + return cf->next? + cf->next->cft->is_alive(cf->next, data, input_pending) : + FALSE; /* pessimistic in absence of data */ +} + +CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + return cf->next? + cf->next->cft->keep_alive(cf->next, data) : + CURLE_OK; +} + +CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, + struct Curl_easy *data) +{ + struct Curl_cfilter *cfn, *cf = *pcf; + + if(cf) { + *pcf = NULL; + while(cf) { + cfn = cf->next; + /* prevent destroying filter to mess with its sub-chain, since + * we have the reference now and will call destroy on it. + */ + cf->next = NULL; + cf->cft->destroy(cf, data); + free(cf); + cf = cfn; + } + } +} + +void Curl_conn_cf_discard_all(struct Curl_easy *data, + struct connectdata *conn, int index) +{ + Curl_conn_cf_discard_chain(&conn->cfilter[index], data); +} + +void Curl_conn_close(struct Curl_easy *data, int index) +{ + struct Curl_cfilter *cf; + + DEBUGASSERT(data->conn); + /* it is valid to call that without filters being present */ + cf = data->conn->cfilter[index]; + if(cf) { + cf->cft->do_close(cf, data); + } + Curl_shutdown_clear(data, index); +} + +CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + timediff_t timeout_ms; + struct curltime now; + + DEBUGASSERT(data->conn); + /* Get the first connected filter that is not shut down already. */ + cf = data->conn->cfilter[sockindex]; + while(cf && (!cf->connected || cf->shutdown)) + cf = cf->next; + + if(!cf) { + *done = TRUE; + return CURLE_OK; + } + + *done = FALSE; + now = Curl_now(); + if(!Curl_shutdown_started(data, sockindex)) { + DEBUGF(infof(data, "shutdown start on%s connection", + sockindex? " secondary" : "")); + Curl_shutdown_start(data, sockindex, &now); + } + else { + timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now); + if(timeout_ms < 0) { + failf(data, "SSL shutdown timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + } + + while(cf) { + if(!cf->shutdown) { + bool cfdone = FALSE; + result = cf->cft->do_shutdown(cf, data, &cfdone); + if(result) { + CURL_TRC_CF(data, cf, "shut down failed with %d", result); + return result; + } + else if(!cfdone) { + CURL_TRC_CF(data, cf, "shut down not done yet"); + return CURLE_OK; + } + CURL_TRC_CF(data, cf, "shut down successfully"); + cf->shutdown = TRUE; + } + cf = cf->next; + } + *done = (!result); + return result; +} + +ssize_t Curl_cf_recv(struct Curl_easy *data, int num, char *buf, + size_t len, CURLcode *code) +{ + struct Curl_cfilter *cf; + + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + *code = CURLE_OK; + cf = data->conn->cfilter[num]; + while(cf && !cf->connected) { + cf = cf->next; + } + if(cf) { + ssize_t nread = cf->cft->do_recv(cf, data, buf, len, code); + DEBUGASSERT(nread >= 0 || *code); + DEBUGASSERT(nread < 0 || !*code); + return nread; + } + failf(data, "recv: no filter connected"); + *code = CURLE_FAILED_INIT; + return -1; +} + +ssize_t Curl_cf_send(struct Curl_easy *data, int num, + const void *mem, size_t len, CURLcode *code) +{ + struct Curl_cfilter *cf; + + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + *code = CURLE_OK; + cf = data->conn->cfilter[num]; + while(cf && !cf->connected) { + cf = cf->next; + } + if(cf) { + ssize_t nwritten = cf->cft->do_send(cf, data, mem, len, code); + DEBUGASSERT(nwritten >= 0 || *code); + DEBUGASSERT(nwritten < 0 || !*code || !len); + return nwritten; + } + failf(data, "send: no filter connected"); + DEBUGASSERT(0); + *code = CURLE_FAILED_INIT; + return -1; +} + +CURLcode Curl_cf_create(struct Curl_cfilter **pcf, + const struct Curl_cftype *cft, + void *ctx) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OUT_OF_MEMORY; + + DEBUGASSERT(cft); + cf = calloc(1, sizeof(*cf)); + if(!cf) + goto out; + + cf->cft = cft; + cf->ctx = ctx; + result = CURLE_OK; +out: + *pcf = cf; + return result; +} + +void Curl_conn_cf_add(struct Curl_easy *data, + struct connectdata *conn, + int index, + struct Curl_cfilter *cf) +{ + (void)data; + DEBUGASSERT(conn); + DEBUGASSERT(!cf->conn); + DEBUGASSERT(!cf->next); + + cf->next = conn->cfilter[index]; + cf->conn = conn; + cf->sockindex = index; + conn->cfilter[index] = cf; + CURL_TRC_CF(data, cf, "added"); +} + +void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at, + struct Curl_cfilter *cf_new) +{ + struct Curl_cfilter *tail, **pnext; + + DEBUGASSERT(cf_at); + DEBUGASSERT(cf_new); + DEBUGASSERT(!cf_new->conn); + + tail = cf_at->next; + cf_at->next = cf_new; + do { + cf_new->conn = cf_at->conn; + cf_new->sockindex = cf_at->sockindex; + pnext = &cf_new->next; + cf_new = cf_new->next; + } while(cf_new); + *pnext = tail; +} + +bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf, + struct Curl_cfilter *discard, + struct Curl_easy *data, + bool destroy_always) +{ + struct Curl_cfilter **pprev = &cf->next; + bool found = FALSE; + + /* remove from sub-chain and destroy */ + DEBUGASSERT(cf); + while(*pprev) { + if(*pprev == cf) { + *pprev = discard->next; + discard->next = NULL; + found = TRUE; + break; + } + pprev = &((*pprev)->next); + } + if(found || destroy_always) { + discard->next = NULL; + discard->cft->destroy(discard, data); + free(discard); + } + return found; +} + +CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + if(cf) + return cf->cft->do_connect(cf, data, blocking, done); + return CURLE_FAILED_INIT; +} + +void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + if(cf) + cf->cft->do_close(cf, data); +} + +ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + if(cf) + return cf->cft->do_send(cf, data, buf, len, err); + *err = CURLE_SEND_ERROR; + return -1; +} + +ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + if(cf) + return cf->cft->do_recv(cf, data, buf, len, err); + *err = CURLE_RECV_ERROR; + return -1; +} + +CURLcode Curl_conn_connect(struct Curl_easy *data, + int sockindex, + bool blocking, + bool *done) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + + cf = data->conn->cfilter[sockindex]; + DEBUGASSERT(cf); + if(!cf) { + *done = FALSE; + return CURLE_FAILED_INIT; + } + + *done = cf->connected; + if(!*done) { + result = cf->cft->do_connect(cf, data, blocking, done); + if(!result && *done) { + Curl_conn_ev_update_info(data, data->conn); + conn_report_connect_stats(data, data->conn); + data->conn->keepalive = Curl_now(); + } + else if(result) { + conn_report_connect_stats(data, data->conn); + } + } + + return result; +} + +bool Curl_conn_is_connected(struct connectdata *conn, int sockindex) +{ + struct Curl_cfilter *cf; + + cf = conn->cfilter[sockindex]; + return cf && cf->connected; +} + +bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf; + + cf = data->conn->cfilter[sockindex]; + while(cf) { + if(cf->connected) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + cf = cf->next; + } + return FALSE; +} + +bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf) +{ + for(; cf; cf = cf->next) { + if(cf->cft->flags & CF_TYPE_SSL) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + } + return FALSE; +} + +bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex) +{ + return conn? Curl_conn_cf_is_ssl(conn->cfilter[sockindex]) : FALSE; +} + +bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) +{ + struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; + + for(; cf; cf = cf->next) { + if(cf->cft->flags & CF_TYPE_MULTIPLEX) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT + || cf->cft->flags & CF_TYPE_SSL) + return FALSE; + } + return FALSE; +} + +bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf; + + (void)data; + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + + cf = data->conn->cfilter[sockindex]; + while(cf && !cf->connected) { + cf = cf->next; + } + if(cf) { + return cf->cft->has_data_pending(cf, data); + } + return FALSE; +} + +void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + /* Get the lowest not-connected filter, if there are any */ + while(cf && !cf->connected && cf->next && !cf->next->connected) + cf = cf->next; + /* Skip all filters that have already shut down */ + while(cf && cf->shutdown) + cf = cf->next; + /* From there on, give all filters a chance to adjust the pollset. + * Lower filters are called later, so they may override */ + while(cf) { + cf->cft->adjust_pollset(cf, data, ps); + cf = cf->next; + } +} + +void Curl_conn_adjust_pollset(struct Curl_easy *data, + struct easy_pollset *ps) +{ + int i; + + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + for(i = 0; i < 2; ++i) { + Curl_conn_cf_adjust_pollset(data->conn->cfilter[i], data, ps); + } +} + +int Curl_conn_cf_poll(struct Curl_cfilter *cf, + struct Curl_easy *data, + timediff_t timeout_ms) +{ + struct easy_pollset ps; + struct pollfd pfds[MAX_SOCKSPEREASYHANDLE]; + unsigned int i, npfds = 0; + + DEBUGASSERT(cf); + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + memset(&ps, 0, sizeof(ps)); + memset(pfds, 0, sizeof(pfds)); + + Curl_conn_cf_adjust_pollset(cf, data, &ps); + DEBUGASSERT(ps.num <= MAX_SOCKSPEREASYHANDLE); + for(i = 0; i < ps.num; ++i) { + short events = 0; + if(ps.actions[i] & CURL_POLL_IN) { + events |= POLLIN; + } + if(ps.actions[i] & CURL_POLL_OUT) { + events |= POLLOUT; + } + if(events) { + pfds[npfds].fd = ps.sockets[i]; + pfds[npfds].events = events; + ++npfds; + } + } + + if(!npfds) + DEBUGF(infof(data, "no sockets to poll!")); + return Curl_poll(pfds, npfds, timeout_ms); +} + +void Curl_conn_get_host(struct Curl_easy *data, int sockindex, + const char **phost, const char **pdisplay_host, + int *pport) +{ + struct Curl_cfilter *cf; + + DEBUGASSERT(data->conn); + cf = data->conn->cfilter[sockindex]; + if(cf) { + cf->cft->get_host(cf, data, phost, pdisplay_host, pport); + } + else { + /* Some filter ask during shutdown for this, mainly for debugging + * purposes. We hand out the defaults, however this is not always + * accurate, as the connection might be tunneled, etc. But all that + * state is already gone here. */ + *phost = data->conn->host.name; + *pdisplay_host = data->conn->host.dispname; + *pport = data->conn->remote_port; + } +} + +CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + (void)cf; + (void)data; + (void)event; + (void)arg1; + (void)arg2; + return CURLE_OK; +} + +CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool ignore_result, + int event, int arg1, void *arg2) +{ + CURLcode result = CURLE_OK; + + for(; cf; cf = cf->next) { + if(Curl_cf_def_cntrl == cf->cft->cntrl) + continue; + result = cf->cft->cntrl(cf, data, event, arg1, arg2); + if(!ignore_result && result) + break; + } + return result; +} + +curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + curl_socket_t sock; + if(cf && !cf->cft->query(cf, data, CF_QUERY_SOCKET, NULL, &sock)) + return sock; + return CURL_SOCKET_BAD; +} + +curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf; + + cf = data->conn? data->conn->cfilter[sockindex] : NULL; + /* if the top filter has not connected, ask it (and its sub-filters) + * for the socket. Otherwise conn->sock[sockindex] should have it. + */ + if(cf && !cf->connected) + return Curl_conn_cf_get_socket(cf, data); + return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD; +} + +void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex) +{ + if(data->conn) { + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + if(cf) + (void)Curl_conn_cf_cntrl(cf, data, TRUE, + CF_CTRL_FORGET_SOCKET, 0, NULL); + fake_sclose(data->conn->sock[sockindex]); + data->conn->sock[sockindex] = CURL_SOCKET_BAD; + } +} + +static CURLcode cf_cntrl_all(struct connectdata *conn, + struct Curl_easy *data, + bool ignore_result, + int event, int arg1, void *arg2) +{ + CURLcode result = CURLE_OK; + size_t i; + + for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) { + result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result, + event, arg1, arg2); + if(!ignore_result && result) + break; + } + return result; +} + +void Curl_conn_ev_data_attach(struct connectdata *conn, + struct Curl_easy *data) +{ + cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_ATTACH, 0, NULL); +} + +void Curl_conn_ev_data_detach(struct connectdata *conn, + struct Curl_easy *data) +{ + cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_DETACH, 0, NULL); +} + +CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data) +{ + return cf_cntrl_all(data->conn, data, FALSE, + CF_CTRL_DATA_SETUP, 0, NULL); +} + +CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data) +{ + return cf_cntrl_all(data->conn, data, FALSE, + CF_CTRL_DATA_IDLE, 0, NULL); +} + +/** + * Notify connection filters that the transfer represented by `data` + * is done with sending data (e.g. has uploaded everything). + */ +void Curl_conn_ev_data_done_send(struct Curl_easy *data) +{ + cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE_SEND, 0, NULL); +} + +/** + * Notify connection filters that the transfer represented by `data` + * is finished - eventually premature, e.g. before being complete. + */ +void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature) +{ + cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE, premature, NULL); +} + +CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause) +{ + return cf_cntrl_all(data->conn, data, FALSE, + CF_CTRL_DATA_PAUSE, do_pause, NULL); +} + +void Curl_conn_ev_update_info(struct Curl_easy *data, + struct connectdata *conn) +{ + cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); +} + +/** + * Update connection statistics + */ +static void conn_report_connect_stats(struct Curl_easy *data, + struct connectdata *conn) +{ + struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET]; + if(cf) { + struct curltime connected; + struct curltime appconnected; + + memset(&connected, 0, sizeof(connected)); + cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); + if(connected.tv_sec || connected.tv_usec) + Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); + + memset(&appconnected, 0, sizeof(appconnected)); + cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); + if(appconnected.tv_sec || appconnected.tv_usec) + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); + } +} + +bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn, + bool *input_pending) +{ + struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET]; + return cf && !cf->conn->bits.close && + cf->cft->is_alive(cf, data, input_pending); +} + +CURLcode Curl_conn_keep_alive(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + return cf? cf->cft->keep_alive(cf, data) : CURLE_OK; +} + +size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + CURLcode result; + int n = 0; + + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + result = cf? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT, + &n, NULL) : CURLE_UNKNOWN_OPTION; + return (result || n <= 0)? 1 : (size_t)n; +} + +int Curl_conn_get_stream_error(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + CURLcode result; + int n = 0; + + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + result = cf? cf->cft->query(cf, data, CF_QUERY_STREAM_ERROR, + &n, NULL) : CURLE_UNKNOWN_OPTION; + return (result || n < 0)? 0 : n; +} + +int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd) +{ + if(data && data->conn && + sockfd != CURL_SOCKET_BAD && sockfd == data->conn->sock[SECONDARYSOCKET]) + return SECONDARYSOCKET; + return FIRSTSOCKET; +} + +CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, + char *buf, size_t blen, ssize_t *n) +{ + CURLcode result = CURLE_OK; + ssize_t nread; + + DEBUGASSERT(data->conn); + nread = data->conn->recv[sockindex](data, sockindex, buf, blen, &result); + DEBUGASSERT(nread >= 0 || result); + DEBUGASSERT(nread < 0 || !result); + *n = (nread >= 0)? (size_t)nread : 0; + return result; +} + +CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, + const void *buf, size_t blen, + size_t *pnwritten) +{ + ssize_t nwritten; + CURLcode result = CURLE_OK; + struct connectdata *conn; + + DEBUGASSERT(sockindex >= 0 && sockindex < 2); + DEBUGASSERT(pnwritten); + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + conn = data->conn; +#ifdef DEBUGBUILD + { + /* Allow debug builds to override this logic to force short sends + */ + char *p = getenv("CURL_SMALLSENDS"); + if(p) { + size_t altsize = (size_t)strtoul(p, NULL, 10); + if(altsize) + blen = CURLMIN(blen, altsize); + } + } +#endif + nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result); + DEBUGASSERT((nwritten >= 0) || result); + *pnwritten = (nwritten < 0)? 0 : (size_t)nwritten; + return result; +} + +void Curl_pollset_reset(struct Curl_easy *data, + struct easy_pollset *ps) +{ + size_t i; + (void)data; + memset(ps, 0, sizeof(*ps)); + for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) + ps->sockets[i] = CURL_SOCKET_BAD; +} + +/** + * + */ +void Curl_pollset_change(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + int add_flags, int remove_flags) +{ + unsigned int i; + + (void)data; + DEBUGASSERT(VALID_SOCK(sock)); + if(!VALID_SOCK(sock)) + return; + + DEBUGASSERT(add_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); + DEBUGASSERT(remove_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); + DEBUGASSERT((add_flags&remove_flags) == 0); /* no overlap */ + for(i = 0; i < ps->num; ++i) { + if(ps->sockets[i] == sock) { + ps->actions[i] &= (unsigned char)(~remove_flags); + ps->actions[i] |= (unsigned char)add_flags; + /* all gone? remove socket */ + if(!ps->actions[i]) { + if((i + 1) < ps->num) { + memmove(&ps->sockets[i], &ps->sockets[i + 1], + (ps->num - (i + 1)) * sizeof(ps->sockets[0])); + memmove(&ps->actions[i], &ps->actions[i + 1], + (ps->num - (i + 1)) * sizeof(ps->actions[0])); + } + --ps->num; + } + return; + } + } + /* not present */ + if(add_flags) { + /* Having more SOCKETS per easy handle than what is defined + * is a programming error. This indicates that we need + * to raise this limit, making easy_pollset larger. + * Since we use this in tight loops, we do not want to make + * the pollset dynamic unnecessarily. + * The current maximum in practise is HTTP/3 eyeballing where + * we have up to 4 sockets involved in connection setup. + */ + DEBUGASSERT(i < MAX_SOCKSPEREASYHANDLE); + if(i < MAX_SOCKSPEREASYHANDLE) { + ps->sockets[i] = sock; + ps->actions[i] = (unsigned char)add_flags; + ps->num = i + 1; + } + } +} + +void Curl_pollset_set(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool do_in, bool do_out) +{ + Curl_pollset_change(data, ps, sock, + (do_in?CURL_POLL_IN:0)|(do_out?CURL_POLL_OUT:0), + (!do_in?CURL_POLL_IN:0)|(!do_out?CURL_POLL_OUT:0)); +} + +static void ps_add(struct Curl_easy *data, struct easy_pollset *ps, + int bitmap, curl_socket_t *socks) +{ + if(bitmap) { + int i; + for(i = 0; i < MAX_SOCKSPEREASYHANDLE; ++i) { + if(!(bitmap & GETSOCK_MASK_RW(i)) || !VALID_SOCK((socks[i]))) { + break; + } + if(bitmap & GETSOCK_READSOCK(i)) { + if(bitmap & GETSOCK_WRITESOCK(i)) + Curl_pollset_add_inout(data, ps, socks[i]); + else + /* is READ, since we checked MASK_RW above */ + Curl_pollset_add_in(data, ps, socks[i]); + } + else + Curl_pollset_add_out(data, ps, socks[i]); + } + } +} + +void Curl_pollset_add_socks(struct Curl_easy *data, + struct easy_pollset *ps, + int (*get_socks_cb)(struct Curl_easy *data, + curl_socket_t *socks)) +{ + curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; + int bitmap; + + bitmap = get_socks_cb(data, socks); + ps_add(data, ps, bitmap, socks); +} + +void Curl_pollset_check(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool *pwant_read, bool *pwant_write) +{ + unsigned int i; + + (void)data; + DEBUGASSERT(VALID_SOCK(sock)); + for(i = 0; i < ps->num; ++i) { + if(ps->sockets[i] == sock) { + *pwant_read = !!(ps->actions[i] & CURL_POLL_IN); + *pwant_write = !!(ps->actions[i] & CURL_POLL_OUT); + return; + } + } + *pwant_read = *pwant_write = FALSE; +} diff --git a/third_party/curl/lib/cfilters.h b/third_party/curl/lib/cfilters.h new file mode 100644 index 0000000000..7d53607f87 --- /dev/null +++ b/third_party/curl/lib/cfilters.h @@ -0,0 +1,675 @@ +#ifndef HEADER_CURL_CFILTERS_H +#define HEADER_CURL_CFILTERS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "timediff.h" + +struct Curl_cfilter; +struct Curl_easy; +struct Curl_dns_entry; +struct connectdata; + +/* Callback to destroy resources held by this filter instance. + * Implementations MUST NOT chain calls to cf->next. + */ +typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf, + struct Curl_easy *data); + +/* Callback to close the connection immediately. */ +typedef void Curl_cft_close(struct Curl_cfilter *cf, + struct Curl_easy *data); + +/* Callback to close the connection filter gracefully, non-blocking. + * Implementations MUST NOT chain calls to cf->next. + */ +typedef CURLcode Curl_cft_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done); + +typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done); + +/* Return the hostname and port the connection goes to. + * This may change with the connection state of filters when tunneling + * is involved. + * @param cf the filter to ask + * @param data the easy handle currently active + * @param phost on return, points to the relevant, real hostname. + * this is owned by the connection. + * @param pdisplay_host on return, points to the printable hostname. + * this is owned by the connection. + * @param pport on return, contains the port number + */ +typedef void Curl_cft_get_host(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char **phost, + const char **pdisplay_host, + int *pport); + +struct easy_pollset; + +/* Passing in an easy_pollset for monitoring of sockets, let + * filters add or remove sockets actions (CURL_POLL_OUT, CURL_POLL_IN). + * This may add a socket or, in case no actions remain, remove + * a socket from the set. + * + * Filter implementations need to call filters "below" *after* they have + * made their adjustments. This allows lower filters to override "upper" + * actions. If a "lower" filter is unable to write, it needs to be able + * to disallow POLL_OUT. + * + * A filter without own restrictions/preferences should not modify + * the pollset. Filters, whose filter "below" is not connected, should + * also do no adjustments. + * + * Examples: a TLS handshake, while ongoing, might remove POLL_IN when it + * needs to write, or vice versa. An HTTP/2 filter might remove POLL_OUT when + * a stream window is exhausted and a WINDOW_UPDATE needs to be received first + * and add instead POLL_IN. + * + * @param cf the filter to ask + * @param data the easy handle the pollset is about + * @param ps the pollset (inout) for the easy handle + */ +typedef void Curl_cft_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); + +typedef bool Curl_cft_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data); + +typedef ssize_t Curl_cft_send(struct Curl_cfilter *cf, + struct Curl_easy *data, /* transfer */ + const void *buf, /* data to write */ + size_t len, /* amount to write */ + CURLcode *err); /* error to return */ + +typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, /* transfer */ + char *buf, /* store data here */ + size_t len, /* amount to read */ + CURLcode *err); /* error to return */ + +typedef bool Curl_cft_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending); + +typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data); + +/** + * Events/controls for connection filters, their arguments and + * return code handling. Filter callbacks are invoked "top down". + * Return code handling: + * "first fail" meaning that the first filter returning != CURLE_OK, will + * abort further event distribution and determine the result. + * "ignored" meaning return values are ignored and the event is distributed + * to all filters in the chain. Overall result is always CURLE_OK. + */ +/* data event arg1 arg2 return */ +#define CF_CTRL_DATA_ATTACH 1 /* 0 NULL ignored */ +#define CF_CTRL_DATA_DETACH 2 /* 0 NULL ignored */ +#define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */ +#define CF_CTRL_DATA_IDLE 5 /* 0 NULL first fail */ +#define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */ +#define CF_CTRL_DATA_DONE 7 /* premature NULL ignored */ +#define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */ +/* update conn info at connection and data */ +#define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */ +#define CF_CTRL_FORGET_SOCKET (256+1) /* 0 NULL ignored */ + +/** + * Handle event/control for the filter. + * Implementations MUST NOT chain calls to cf->next. + */ +typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2); + + +/** + * Queries to ask via a `Curl_cft_query *query` method on a cfilter chain. + * - MAX_CONCURRENT: the maximum number of parallel transfers the filter + * chain expects to handle at the same time. + * default: 1 if no filter overrides. + * - CONNECT_REPLY_MS: milliseconds until the first indication of a server + * response was received on a connect. For TCP, this + * reflects the time until the socket connected. On UDP + * this gives the time the first bytes from the server + * were received. + * -1 if not determined yet. + * - CF_QUERY_SOCKET: the socket used by the filter chain + */ +/* query res1 res2 */ +#define CF_QUERY_MAX_CONCURRENT 1 /* number - */ +#define CF_QUERY_CONNECT_REPLY_MS 2 /* number - */ +#define CF_QUERY_SOCKET 3 /* - curl_socket_t */ +#define CF_QUERY_TIMER_CONNECT 4 /* - struct curltime */ +#define CF_QUERY_TIMER_APPCONNECT 5 /* - struct curltime */ +#define CF_QUERY_STREAM_ERROR 6 /* error code - */ + +/** + * Query the cfilter for properties. Filters ignorant of a query will + * pass it "down" the filter chain. + */ +typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2); + +/** + * Type flags for connection filters. A filter can have none, one or + * many of those. Use to evaluate state/capabilities of a filter chain. + * + * CF_TYPE_IP_CONNECT: provides an IP connection or sth equivalent, like + * a CONNECT tunnel, a UNIX domain socket, a QUIC + * connection, etc. + * CF_TYPE_SSL: provide SSL/TLS + * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles + * CF_TYPE_PROXY provides proxying + */ +#define CF_TYPE_IP_CONNECT (1 << 0) +#define CF_TYPE_SSL (1 << 1) +#define CF_TYPE_MULTIPLEX (1 << 2) +#define CF_TYPE_PROXY (1 << 3) + +/* A connection filter type, e.g. specific implementation. */ +struct Curl_cftype { + const char *name; /* name of the filter type */ + int flags; /* flags of filter type */ + int log_level; /* log level for such filters */ + Curl_cft_destroy_this *destroy; /* destroy resources of this cf */ + Curl_cft_connect *do_connect; /* establish connection */ + Curl_cft_close *do_close; /* close conn */ + Curl_cft_shutdown *do_shutdown; /* shutdown conn */ + Curl_cft_get_host *get_host; /* host filter talks to */ + Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */ + Curl_cft_data_pending *has_data_pending;/* conn has data pending */ + Curl_cft_send *do_send; /* send data */ + Curl_cft_recv *do_recv; /* receive data */ + Curl_cft_cntrl *cntrl; /* events/control */ + Curl_cft_conn_is_alive *is_alive; /* FALSE if conn is dead, Jim! */ + Curl_cft_conn_keep_alive *keep_alive; /* try to keep it alive */ + Curl_cft_query *query; /* query filter chain */ +}; + +/* A connection filter instance, e.g. registered at a connection */ +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; /* the index the filter is installed at */ + BIT(connected); /* != 0 iff this filter is connected */ + BIT(shutdown); /* != 0 iff this filter has shut down */ +}; + +/* Default implementations for the type functions, implementing nop. */ +void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, + struct Curl_easy *data); + +/* Default implementations for the type functions, implementing pass-through + * the filter chain. */ +void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, + const char **phost, const char **pdisplay_host, + int *pport); +void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); +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); +CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2); +bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending); +CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data); +CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2); +CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done); + +/** + * Create a new filter instance, unattached to the filter chain. + * Use Curl_conn_cf_add() to add it to the chain. + * @param pcf on success holds the created instance + * @param cft the filter type + * @param ctx the type specific context to use + */ +CURLcode Curl_cf_create(struct Curl_cfilter **pcf, + const struct Curl_cftype *cft, + void *ctx); + +/** + * Add a filter instance to the `sockindex` filter chain at connection + * `conn`. The filter must not already be attached. It is inserted at + * the start of the chain (top). + */ +void Curl_conn_cf_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_cfilter *cf); + +/** + * Insert a filter (chain) after `cf_at`. + * `cf_new` must not already be attached. + */ +void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at, + struct Curl_cfilter *cf_new); + +/** + * Discard, e.g. remove and destroy `discard` iff + * it still is in the filter chain below `cf`. If `discard` + * is no longer found beneath `cf` return FALSE. + * if `destroy_always` is TRUE, will call `discard`s destroy + * function and free it even if not found in the subchain. + */ +bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf, + struct Curl_cfilter *discard, + struct Curl_easy *data, + bool destroy_always); + +/** + * Discard all cfilters starting with `*pcf` and clearing it afterwards. + */ +void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, + struct Curl_easy *data); + +/** + * Remove and destroy all filters at chain `sockindex` on connection `conn`. + */ +void Curl_conn_cf_discard_all(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + + +CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done); +void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data); +ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err); +ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err); +CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool ignore_result, + int event, int arg1, void *arg2); + +/** + * Determine if the connection filter chain is using SSL to the remote host + * (or will be once connected). + */ +bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf); + +/** + * Get the socket used by the filter chain starting at `cf`. + * Returns CURL_SOCKET_BAD if not available. + */ +curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf, + struct Curl_easy *data); + + +#define CURL_CF_SSL_DEFAULT -1 +#define CURL_CF_SSL_DISABLE 0 +#define CURL_CF_SSL_ENABLE 1 + +/** + * Bring the filter chain at `sockindex` for connection `data->conn` into + * connected state. Which will set `*done` to TRUE. + * This can be called on an already connected chain with no side effects. + * When not `blocking`, calls may return without error and `*done != TRUE`, + * while the individual filters negotiated the connection. + */ +CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, + bool blocking, bool *done); + +/** + * Check if the filter chain at `sockindex` for connection `conn` is + * completely connected. + */ +bool Curl_conn_is_connected(struct connectdata *conn, int sockindex); + +/** + * Determine if we have reached the remote host on IP level, e.g. + * have a TCP connection. This turns TRUE before a possible SSL + * handshake has been started/done. + */ +bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex); + +/** + * Determine if the connection is using SSL to the remote host + * (or will be once connected). This will return FALSE, if SSL + * is only used in proxying and not for the tunnel itself. + */ +bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); + +/** + * Connection provides multiplexing of easy handles at `socketindex`. + */ +bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex); + +/** + * Close the filter chain at `sockindex` for connection `data->conn`. + * Filters remain in place and may be connected again afterwards. + */ +void Curl_conn_close(struct Curl_easy *data, int sockindex); + +/** + * Shutdown the connection at `sockindex` non-blocking, using timeout + * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS. + * Will return CURLE_OK and *done == FALSE if not finished. + */ +CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done); + +/** + * Return if data is pending in some connection filter at chain + * `sockindex` for connection `data->conn`. + */ +bool Curl_conn_data_pending(struct Curl_easy *data, + int sockindex); + +/** + * Return the socket used on data's connection for the index. + * Returns CURL_SOCKET_BAD if not available. + */ +curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex); + +/** + * Tell filters to forget about the socket at sockindex. + */ +void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex); + +/** + * Adjust the pollset for the filter chain startgin at `cf`. + */ +void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); + +/** + * Adjust pollset from filters installed at transfer's connection. + */ +void Curl_conn_adjust_pollset(struct Curl_easy *data, + struct easy_pollset *ps); + +/** + * Curl_poll() the filter chain at `cf` with timeout `timeout_ms`. + * Returns 0 on timeout, negative on error or number of sockets + * with requested poll events. + */ +int Curl_conn_cf_poll(struct Curl_cfilter *cf, + struct Curl_easy *data, + timediff_t timeout_ms); + +/** + * Receive data through the filter chain at `sockindex` for connection + * `data->conn`. Copy at most `len` bytes into `buf`. Return the + * actual number of bytes copied or a negative value on error. + * The error code is placed into `*code`. + */ +ssize_t Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, + size_t len, CURLcode *code); + +/** + * Send `len` bytes of data from `buf` through the filter chain `sockindex` + * at connection `data->conn`. Return the actual number of bytes written + * or a negative value on error. + * The error code is placed into `*code`. + */ +ssize_t Curl_cf_send(struct Curl_easy *data, int sockindex, + const void *buf, size_t len, CURLcode *code); + +/** + * The easy handle `data` is being attached to `conn`. This does + * not mean that data will actually do a transfer. Attachment is + * also used for temporary actions on the connection. + */ +void Curl_conn_ev_data_attach(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_conn_ev_data_detach(struct connectdata *conn, + struct Curl_easy *data); + +/** + * Notify connection filters that they need to setup data for + * a transfer. + */ +CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data); + +/** + * Notify connection filters that now would be a good time to + * perform any idle, e.g. time related, actions. + */ +CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data); + +/** + * Notify connection filters that the transfer represented by `data` + * is done with sending data (e.g. has uploaded everything). + */ +void Curl_conn_ev_data_done_send(struct Curl_easy *data); + +/** + * Notify connection filters that the transfer represented by `data` + * is finished - eventually premature, e.g. before being complete. + */ +void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature); + +/** + * Notify connection filters that the transfer of data is paused/unpaused. + */ +CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause); + +/** + * Inform connection filters to update their info in `conn`. + */ +void Curl_conn_ev_update_info(struct Curl_easy *data, + struct connectdata *conn); + +/** + * Check if FIRSTSOCKET's cfilter chain deems connection alive. + */ +bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn, + bool *input_pending); + +/** + * Try to upkeep the connection filters at sockindex. + */ +CURLcode Curl_conn_keep_alive(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + +#ifdef UNITTESTS +void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); +#endif +void Curl_conn_get_host(struct Curl_easy *data, int sockindex, + const char **phost, const char **pdisplay_host, + int *pport); + +/** + * Get the maximum number of parallel transfers the connection + * expects to be able to handle at `sockindex`. + */ +size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + +/** + * Get the underlying error code for a transfer stream or 0 if not known. + */ +int Curl_conn_get_stream_error(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + +/** + * Get the index of the given socket in the connection's sockets. + * Useful in calling `Curl_conn_send()/Curl_conn_recv()` with the + * correct socket index. + */ +int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd); + +/* + * Receive data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. + * Will return CURLE_AGAIN iff blocked on receiving. + */ +CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, + char *buf, size_t buffersize, + ssize_t *pnread); + +/* + * Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. + * Will return CURLE_AGAIN iff blocked on sending. + */ +CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, + const void *buf, size_t blen, + size_t *pnwritten); + + +void Curl_pollset_reset(struct Curl_easy *data, + struct easy_pollset *ps); + +/* Change the poll flags (CURL_POLL_IN/CURL_POLL_OUT) to the poll set for + * socket `sock`. If the socket is not already part of the poll set, it + * will be added. + * If the socket is present and all poll flags are cleared, it will be removed. + */ +void Curl_pollset_change(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + int add_flags, int remove_flags); + +void Curl_pollset_set(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool do_in, bool do_out); + +#define Curl_pollset_add_in(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), CURL_POLL_IN, 0) +#define Curl_pollset_add_out(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), CURL_POLL_OUT, 0) +#define Curl_pollset_add_inout(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_IN|CURL_POLL_OUT, 0) +#define Curl_pollset_set_in_only(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_IN, CURL_POLL_OUT) +#define Curl_pollset_set_out_only(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_OUT, CURL_POLL_IN) + +void Curl_pollset_add_socks(struct Curl_easy *data, + struct easy_pollset *ps, + int (*get_socks_cb)(struct Curl_easy *data, + curl_socket_t *socks)); + +/** + * Check if the pollset, as is, wants to read and/or write regarding + * the given socket. + */ +void Curl_pollset_check(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool *pwant_read, bool *pwant_write); + +/** + * Types and macros used to keep the current easy handle in filter calls, + * allowing for nested invocations. See #10336. + * + * `cf_call_data` is intended to be a member of the cfilter's `ctx` type. + * A filter defines the macro `CF_CTX_CALL_DATA` to give access to that. + * + * With all values 0, the default, this indicates that there is no cfilter + * call with `data` ongoing. + * Macro `CF_DATA_SAVE` preserves the current `cf_call_data` in a local + * variable and sets the `data` given, incrementing the `depth` counter. + * + * Macro `CF_DATA_RESTORE` restores the old values from the local variable, + * while checking that `depth` values are as expected (debug build), catching + * cases where a "lower" RESTORE was not called. + * + * Finally, macro `CF_DATA_CURRENT` gives the easy handle of the current + * invocation. + */ +struct cf_call_data { + struct Curl_easy *data; +#ifdef DEBUGBUILD + int depth; +#endif +}; + +/** + * define to access the `struct cf_call_data for a cfilter. Normally + * a member in the cfilter's `ctx`. + * + * #define CF_CTX_CALL_DATA(cf) -> struct cf_call_data instance +*/ + +#ifdef DEBUGBUILD + +#define CF_DATA_SAVE(save, cf, data) \ + do { \ + (save) = CF_CTX_CALL_DATA(cf); \ + DEBUGASSERT((save).data == NULL || (save).depth > 0); \ + CF_CTX_CALL_DATA(cf).depth++; \ + CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \ + } while(0) + +#define CF_DATA_RESTORE(cf, save) \ + do { \ + DEBUGASSERT(CF_CTX_CALL_DATA(cf).depth == (save).depth + 1); \ + DEBUGASSERT((save).data == NULL || (save).depth > 0); \ + CF_CTX_CALL_DATA(cf) = (save); \ + } while(0) + +#else /* DEBUGBUILD */ + +#define CF_DATA_SAVE(save, cf, data) \ + do { \ + (save) = CF_CTX_CALL_DATA(cf); \ + CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \ + } while(0) + +#define CF_DATA_RESTORE(cf, save) \ + do { \ + CF_CTX_CALL_DATA(cf) = (save); \ + } while(0) + +#endif /* !DEBUGBUILD */ + +#define CF_DATA_CURRENT(cf) \ + ((cf)? (CF_CTX_CALL_DATA(cf).data) : NULL) + +#endif /* HEADER_CURL_CFILTERS_H */ diff --git a/third_party/curl/lib/config-amigaos.h b/third_party/curl/lib/config-amigaos.h new file mode 100644 index 0000000000..d168b446b6 --- /dev/null +++ b/third_party/curl/lib/config-amigaos.h @@ -0,0 +1,129 @@ +#ifndef HEADER_CURL_CONFIG_AMIGAOS_H +#define HEADER_CURL_CONFIG_AMIGAOS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* ================================================================ */ +/* Hand crafted config file for AmigaOS */ +/* ================================================================ */ + +#ifdef __AMIGA__ /* Any AmigaOS flavour */ + +#define HAVE_ARPA_INET_H 1 +#define HAVE_CLOSESOCKET_CAMEL 1 +#define HAVE_IOCTLSOCKET_CAMEL 1 +#define HAVE_IOCTLSOCKET_CAMEL_FIONBIO 1 +#define HAVE_LONGLONG 1 +#define HAVE_NETDB_H 1 +#define HAVE_NETINET_IN_H 1 +#define HAVE_NET_IF_H 1 +#define HAVE_PWD_H 1 +#define HAVE_SELECT 1 +#define HAVE_SIGNAL 1 +#define HAVE_SOCKET 1 +#define HAVE_STRCASECMP 1 +#define HAVE_STRDUP 1 +#define HAVE_STRICMP 1 +#define HAVE_STRINGS_H 1 +#define HAVE_STRUCT_TIMEVAL 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_SOCKET_H 1 +#define HAVE_SYS_SOCKIO_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_UTIME 1 +#define HAVE_UTIME_H 1 +#define HAVE_WRITABLE_ARGV 1 +#define HAVE_SYS_IOCTL_H 1 + +#define NEED_MALLOC_H 1 + +#define SIZEOF_INT 4 +#define SIZEOF_SIZE_T 4 + +#ifndef SIZEOF_CURL_OFF_T +#define SIZEOF_CURL_OFF_T 8 +#endif + +#define USE_MANUAL 1 +#define CURL_DISABLE_LDAP 1 + +#ifndef OS +#define OS "AmigaOS" +#endif + +#define PACKAGE "curl" +#define PACKAGE_BUGREPORT "a suitable mailing list: https://curl.se/mail/" +#define PACKAGE_NAME "curl" +#define PACKAGE_STRING "curl -" +#define PACKAGE_TARNAME "curl" +#define PACKAGE_VERSION "-" + +#if defined(USE_AMISSL) +#define CURL_CA_PATH "AmiSSL:Certs" +#elif defined(__MORPHOS__) +#define CURL_CA_BUNDLE "MOSSYS:Data/SSL/curl-ca-bundle.crt" +#else +#define CURL_CA_BUNDLE "s:curl-ca-bundle.crt" +#endif + +#define STDC_HEADERS 1 + +#define in_addr_t int + +#ifndef F_OK +# define F_OK 0 +#endif + +#ifndef O_RDONLY +# define O_RDONLY 0x0000 +#endif + +#ifndef LONG_MAX +# define LONG_MAX 0x7fffffffL +#endif + +#ifndef LONG_MIN +# define LONG_MIN (-0x7fffffffL-1) +#endif + +#define HAVE_RECV 1 +#define RECV_TYPE_ARG1 long +#define RECV_TYPE_ARG2 char * +#define RECV_TYPE_ARG3 long +#define RECV_TYPE_ARG4 long +#define RECV_TYPE_RETV long + +#define HAVE_SEND 1 +#define SEND_TYPE_ARG1 int +#define SEND_QUAL_ARG2 const +#define SEND_TYPE_ARG2 char * +#define SEND_TYPE_ARG3 int +#define SEND_TYPE_ARG4 int +#define SEND_TYPE_RETV int + +#endif /* __AMIGA__ */ +#endif /* HEADER_CURL_CONFIG_AMIGAOS_H */ diff --git a/third_party/curl/lib/config-dos.h b/third_party/curl/lib/config-dos.h new file mode 100644 index 0000000000..c6fbba796a --- /dev/null +++ b/third_party/curl/lib/config-dos.h @@ -0,0 +1,138 @@ +#ifndef HEADER_CURL_CONFIG_DOS_H +#define HEADER_CURL_CONFIG_DOS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + + +/* ================================================================ */ +/* lib/config-dos.h - Hand crafted config file for DOS */ +/* ================================================================ */ + +#ifndef OS +#if defined(DJGPP) + #define OS "MSDOS/djgpp" +#elif defined(__HIGHC__) + #define OS "MSDOS/HighC" +#else + #define OS "MSDOS/?" +#endif +#endif + +#define PACKAGE "curl" + +#define USE_MANUAL 1 + +#define HAVE_ARPA_INET_H 1 +#define HAVE_FCNTL_H 1 +#define HAVE_FREEADDRINFO 1 +#define HAVE_GETADDRINFO 1 +#define HAVE_GETTIMEOFDAY 1 +#define HAVE_IO_H 1 +#define HAVE_IOCTL_FIONBIO 1 +#define HAVE_IOCTLSOCKET 1 +#define HAVE_IOCTLSOCKET_FIONBIO 1 +#define HAVE_LOCALE_H 1 +#define HAVE_LONGLONG 1 +#define HAVE_NETDB_H 1 +#define HAVE_NETINET_IN_H 1 +#define HAVE_NETINET_TCP_H 1 +#define HAVE_NET_IF_H 1 +#define HAVE_RECV 1 +#define HAVE_SELECT 1 +#define HAVE_SEND 1 +#define HAVE_SETLOCALE 1 +#define HAVE_SETMODE 1 +#define HAVE_SIGNAL 1 +#define HAVE_SOCKET 1 +#define HAVE_STRDUP 1 +#define HAVE_STRICMP 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRUCT_TIMEVAL 1 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_SYS_SOCKET_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_UNISTD_H 1 + +#define NEED_MALLOC_H 1 + +#define SIZEOF_INT 4 +#define SIZEOF_LONG 4 +#define SIZEOF_SIZE_T 4 +#define SIZEOF_CURL_OFF_T 8 +#define STDC_HEADERS 1 + +/* Qualifiers for send() and recv() */ + +#define SEND_TYPE_ARG1 int +#define SEND_QUAL_ARG2 const +#define SEND_TYPE_ARG2 void * +#define SEND_TYPE_ARG3 int +#define SEND_TYPE_ARG4 int +#define SEND_TYPE_RETV int + +#define RECV_TYPE_ARG1 int +#define RECV_TYPE_ARG2 void * +#define RECV_TYPE_ARG3 int +#define RECV_TYPE_ARG4 int +#define RECV_TYPE_RETV int + +#define BSD + +/* CURLDEBUG definition enables memory tracking */ +/* #define CURLDEBUG */ + +/* to disable LDAP */ +#define CURL_DISABLE_LDAP 1 + +#define in_addr_t u_long + +#if defined(__HIGHC__) || \ + (defined(__GNUC__) && (__GNUC__ < 4)) + #define ssize_t int +#endif + +/* Target HAVE_x section */ + +#if defined(DJGPP) + #define HAVE_BASENAME 1 + #define HAVE_STRCASECMP 1 + #define HAVE_SIGACTION 1 + #define HAVE_SIGSETJMP 1 + #define HAVE_SYS_TIME_H 1 + #define HAVE_TERMIOS_H 1 + +#elif defined(__HIGHC__) + #define HAVE_SYS_TIME_H 1 + #define strerror(e) strerror_s_((e)) +#endif + +#ifdef MSDOS /* Watt-32 */ + #define HAVE_CLOSE_S 1 +#endif + +#undef word +#undef byte + +#endif /* HEADER_CURL_CONFIG_DOS_H */ diff --git a/third_party/curl/lib/config-mac.h b/third_party/curl/lib/config-mac.h new file mode 100644 index 0000000000..c29888f8f7 --- /dev/null +++ b/third_party/curl/lib/config-mac.h @@ -0,0 +1,103 @@ +#ifndef HEADER_CURL_CONFIG_MAC_H +#define HEADER_CURL_CONFIG_MAC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* =================================================================== */ +/* Hand crafted config file for Mac OS 9 */ +/* =================================================================== */ +/* On Mac OS X you must run configure to generate curl_config.h file */ +/* =================================================================== */ + +#ifndef OS +#define OS "mac" +#endif + +#include +#if TYPE_LONGLONG +#define HAVE_LONGLONG 1 +#endif + +/* Define if you want the built-in manual */ +#define USE_MANUAL 1 + +#define HAVE_NETINET_IN_H 1 +#define HAVE_SYS_SOCKET_H 1 +#define HAVE_NETDB_H 1 +#define HAVE_ARPA_INET_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_NET_IF_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_GETTIMEOFDAY 1 +#define HAVE_FCNTL_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_UTIME_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_UTIME_H 1 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_ALARM 1 +#define HAVE_FTRUNCATE 1 +#define HAVE_UTIME 1 +#define HAVE_SELECT 1 +#define HAVE_SOCKET 1 +#define HAVE_STRUCT_TIMEVAL 1 + +#define HAVE_SIGACTION 1 + +#ifdef MACOS_SSL_SUPPORT +# define USE_OPENSSL 1 +#endif + +#define CURL_DISABLE_LDAP 1 + +#define HAVE_IOCTL_FIONBIO 1 + +#define SIZEOF_INT 4 +#define SIZEOF_LONG 4 +#define SIZEOF_SIZE_T 4 +#ifdef HAVE_LONGLONG +#define SIZEOF_CURL_OFF_T 8 +#else +#define SIZEOF_CURL_OFF_T 4 +#endif + +#define HAVE_RECV 1 +#define RECV_TYPE_ARG1 int +#define RECV_TYPE_ARG2 void * +#define RECV_TYPE_ARG3 size_t +#define RECV_TYPE_ARG4 int +#define RECV_TYPE_RETV ssize_t + +#define HAVE_SEND 1 +#define SEND_TYPE_ARG1 int +#define SEND_QUAL_ARG2 const +#define SEND_TYPE_ARG2 void * +#define SEND_TYPE_ARG3 size_t +#define SEND_TYPE_ARG4 int +#define SEND_TYPE_RETV ssize_t + +#define HAVE_EXTRA_STRICMP_H 1 +#define HAVE_EXTRA_STRDUP_H 1 + +#endif /* HEADER_CURL_CONFIG_MAC_H */ diff --git a/third_party/curl/lib/config-os400.h b/third_party/curl/lib/config-os400.h new file mode 100644 index 0000000000..ec83be9236 --- /dev/null +++ b/third_party/curl/lib/config-os400.h @@ -0,0 +1,331 @@ +#ifndef HEADER_CURL_CONFIG_OS400_H +#define HEADER_CURL_CONFIG_OS400_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* ================================================================ */ +/* Hand crafted config file for OS/400 */ +/* ================================================================ */ + +#pragma enum(int) + +#undef PACKAGE + +/* Version number of this archive. */ +#undef VERSION + +/* Define cpu-machine-OS */ +#ifndef OS +#define OS "OS/400" +#endif + +/* OS400 supports a 3-argument ASCII version of gethostbyaddr_r(), but its + * prototype is incompatible with the "standard" one (1st argument is not + * const). However, getaddrinfo() is supported (ASCII version defined as + * a local wrapper in setup-os400.h) in a threadsafe way: we can then + * configure getaddrinfo() as such and get rid of gethostbyname_r() without + * loss of threadsafeness. */ +#undef HAVE_GETHOSTBYNAME_R +#undef HAVE_GETHOSTBYNAME_R_3 +#undef HAVE_GETHOSTBYNAME_R_5 +#undef HAVE_GETHOSTBYNAME_R_6 +#define HAVE_GETADDRINFO +#define HAVE_GETADDRINFO_THREADSAFE + +/* Define if you need the _REENTRANT define for some functions */ +#undef NEED_REENTRANT + +/* Define if you want to enable IPv6 support */ +#define USE_IPV6 + +/* Define if struct sockaddr_in6 has the sin6_scope_id member */ +#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 + +/* Define this to 'int' if ssize_t is not an available typedefed type */ +#undef ssize_t + +/* Define this as a suitable file to read random data from */ +#undef RANDOM_FILE + +/* Define to 1 if you have the alarm function. */ +#define HAVE_ALARM 1 + +/* Define if you have the header file. */ +#define HAVE_ARPA_INET_H + +/* Define if you have the `closesocket' function. */ +#undef HAVE_CLOSESOCKET + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H + +/* Define if you have the `geteuid' function. */ +#define HAVE_GETEUID + +/* Define if you have the `gethostname' function. */ +#define HAVE_GETHOSTNAME + +/* Define if you have the `getpass_r' function. */ +#undef HAVE_GETPASS_R + +/* Define to 1 if you have the getpeername function. */ +#define HAVE_GETPEERNAME 1 + +/* Define if you have the `getpwuid' function. */ +#define HAVE_GETPWUID + +/* Define to 1 if you have the getsockname function. */ +#define HAVE_GETSOCKNAME 1 + +/* Define if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY + +/* Define if you have the `timeval' struct. */ +#define HAVE_STRUCT_TIMEVAL + +/* Define if you have the header file. */ +#undef HAVE_IO_H + +/* Define if you have GSS API. */ +#define HAVE_GSSAPI + +/* Define if you have the GNU gssapi libraries */ +#undef HAVE_GSSGNU + +/* Define if you need the malloc.h header file even with stdlib.h */ +/* #define NEED_MALLOC_H 1 */ + +/* Define if you have the header file. */ +#define HAVE_NETDB_H + +/* Define if you have the header file. */ +#define HAVE_NETINET_IN_H + +/* Define if you have the header file. */ +#define HAVE_NET_IF_H + +/* Define if you have the header file. */ +#define HAVE_PWD_H + +/* Define if you have the `select' function. */ +#define HAVE_SELECT + +/* Define if you have the `sigaction' function. */ +#define HAVE_SIGACTION + +/* Define if you have the `signal' function. */ +#undef HAVE_SIGNAL + +/* Define if you have the `socket' function. */ +#define HAVE_SOCKET + + +/* The following define is needed on OS400 to enable strcmpi(), stricmp() and + strdup(). */ +#define __cplusplus__strings__ + +/* Define if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define if you have the `strcmpi' function. */ +#define HAVE_STRCMPI + +/* Define if you have the `stricmp' function. */ +#define HAVE_STRICMP + +/* Define if you have the `strdup' function. */ +#define HAVE_STRDUP + +/* Define if you have the header file. */ +#define HAVE_STRINGS_H + +/* Define if you have the header file. */ +#undef HAVE_STROPTS_H + +/* Define if you have the `strtok_r' function. */ +#define HAVE_STRTOK_R + +/* Define if you have the `strtoll' function. */ +#undef HAVE_STRTOLL /* Allows ASCII compile on V5R1. */ + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define if you have the header file. */ +#define HAVE_SYS_SOCKET_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H + +/* Define if you have the header file. */ +#define HAVE_SYS_UN_H + +/* Define if you have the header file. */ +#define HAVE_SYS_IOCTL_H + +/* Define if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define if you have the header file. */ +#undef HAVE_TERMIO_H + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H + +/* Name of package */ +#undef PACKAGE + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* Define if the compiler supports the 'long long' data type. */ +#define HAVE_LONGLONG + +/* The size of a `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 8 + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of `size_t', as computed by sizeof. */ +#define SIZEOF_SIZE_T 4 + +/* The size of `curl_off_t', as computed by sizeof. */ +#define SIZEOF_CURL_OFF_T 8 + +/* Define this if you have struct sockaddr_storage */ +#define HAVE_STRUCT_SOCKADDR_STORAGE + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS + +/* Define to enable HTTP3 support (experimental, requires NGTCP2, QUICHE or + MSH3) */ +#undef USE_HTTP3 + +/* Version number of package */ +#undef VERSION + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#define _LARGE_FILES + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* type to use in place of in_addr_t if not defined */ +#define in_addr_t unsigned long + +/* Define to `unsigned' if does not define. */ +#undef size_t + +/* Define if you have a working ioctl FIONBIO function. */ +#define HAVE_IOCTL_FIONBIO + +/* Define if you have a working ioctl SIOCGIFADDR function. */ +#define HAVE_IOCTL_SIOCGIFADDR + +/* To disable LDAP */ +#undef CURL_DISABLE_LDAP + +/* Definition to make a library symbol externally visible. */ +#define CURL_EXTERN_SYMBOL + +/* Define if you have the ldap_url_parse procedure. */ +/* #define HAVE_LDAP_URL_PARSE */ /* Disabled because of an IBM bug. */ + +/* Define if you have the recv function. */ +#define HAVE_RECV + +/* Define to the type of arg 1 for recv. */ +#define RECV_TYPE_ARG1 int + +/* Define to the type of arg 2 for recv. */ +#define RECV_TYPE_ARG2 char * + +/* Define to the type of arg 3 for recv. */ +#define RECV_TYPE_ARG3 int + +/* Define to the type of arg 4 for recv. */ +#define RECV_TYPE_ARG4 int + +/* Define to the function return type for recv. */ +#define RECV_TYPE_RETV int + +/* Define if you have the send function. */ +#define HAVE_SEND + +/* Define to the type of arg 1 for send. */ +#define SEND_TYPE_ARG1 int + +/* Define to the type qualifier of arg 2 for send. */ +#define SEND_QUAL_ARG2 + +/* Define to the type of arg 2 for send. */ +#define SEND_TYPE_ARG2 char * + +/* Define to the type of arg 3 for send. */ +#define SEND_TYPE_ARG3 int + +/* Define to the type of arg 4 for send. */ +#define SEND_TYPE_ARG4 int + +/* Define to the function return type for send. */ +#define SEND_TYPE_RETV int + +/* Define to use the OS/400 crypto library. */ +#define USE_OS400CRYPTO + +/* Define to use Unix sockets. */ +#define USE_UNIX_SOCKETS + +/* Use the system keyring as the default CA bundle. */ +#define CURL_CA_BUNDLE "/QIBM/UserData/ICSS/Cert/Server/DEFAULT.KDB" + +/* ---------------------------------------------------------------- */ +/* ADDITIONAL DEFINITIONS */ +/* ---------------------------------------------------------------- */ + +/* The following must be defined BEFORE system header files inclusion. */ + +#define __ptr128 /* No teraspace. */ +#define qadrt_use_fputc_inline /* Generate fputc() wrapper inline. */ +#define qadrt_use_fread_inline /* Generate fread() wrapper inline. */ +#define qadrt_use_fwrite_inline /* Generate fwrite() wrapper inline. */ + +#endif /* HEADER_CURL_CONFIG_OS400_H */ diff --git a/third_party/curl/lib/config-plan9.h b/third_party/curl/lib/config-plan9.h new file mode 100644 index 0000000000..6f3a15a5ef --- /dev/null +++ b/third_party/curl/lib/config-plan9.h @@ -0,0 +1,147 @@ +#ifndef HEADER_CURL_CONFIG_PLAN9_H +#define HEADER_CURL_CONFIG_PLAN9_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#define BUILDING_LIBCURL 1 +#define CURL_CA_BUNDLE "/sys/lib/tls/ca.pem" +#define CURL_CA_PATH "/sys/lib/tls" +#define CURL_STATICLIB 1 +#define USE_IPV6 1 +#define CURL_DISABLE_LDAP 1 + +#define NEED_REENTRANT 1 +#ifndef OS +#define OS "plan9" +#endif +#define PACKAGE "curl" +#define PACKAGE_NAME "curl" +#define PACKAGE_BUGREPORT "a suitable mailing list: https://curl.se/mail/" +#define PACKAGE_STRING "curl -" +#define PACKAGE_TARNAME "curl" +#define PACKAGE_VERSION "-" +#define RANDOM_FILE "/dev/random" +#define VERSION "0.0.0" /* TODO */ + +#define STDC_HEADERS 1 + +#ifdef _BITS64 +#error not implement +#else +#define SIZEOF_INT 4 +#define SIZEOF_LONG 4 +#define SIZEOF_OFF_T 8 +#define SIZEOF_CURL_OFF_T 4 /* curl_off_t = timediff_t = int */ +#define SIZEOF_SIZE_T 4 +#define SIZEOF_TIME_T 4 +#endif + +#define HAVE_RECV 1 +#define RECV_TYPE_ARG1 int +#define RECV_TYPE_ARG2 void * +#define RECV_TYPE_ARG3 int +#define RECV_TYPE_ARG4 int +#define RECV_TYPE_RETV int + +#define HAVE_SELECT 1 + +#define HAVE_SEND 1 +#define SEND_TYPE_ARG1 int +#define SEND_TYPE_ARG2 void * +#define SEND_QUAL_ARG2 +#define SEND_TYPE_ARG3 int +#define SEND_TYPE_ARG4 int +#define SEND_TYPE_RETV int + +#define HAVE_ALARM 1 +#define HAVE_ARPA_INET_H 1 +#define HAVE_BASENAME 1 +#define HAVE_BOOL_T 1 +#define HAVE_FCNTL 1 +#define HAVE_FCNTL_H 1 +#define HAVE_FREEADDRINFO 1 +#define HAVE_FTRUNCATE 1 +#define HAVE_GETADDRINFO 1 +#define HAVE_GETEUID 1 +#define HAVE_GETHOSTNAME 1 +#define HAVE_GETPPID 1 +#define HAVE_GETPWUID 1 +#define HAVE_GETTIMEOFDAY 1 +#define HAVE_GMTIME_R 1 +#define HAVE_INET_NTOP 1 +#define HAVE_INET_PTON 1 +#define HAVE_LIBGEN_H 1 +#define HAVE_LIBZ 1 +#define HAVE_LOCALE_H 1 +#define HAVE_LONGLONG 1 +#define HAVE_NETDB_H 1 +#define HAVE_NETINET_IN_H 1 +#define HAVE_NETINET_TCP_H 1 +#define HAVE_PWD_H 1 +#define HAVE_SYS_SELECT_H 1 + +#define USE_OPENSSL 1 + +#define HAVE_PIPE 1 +#define HAVE_POLL_FINE 1 +#define HAVE_POLL_H 1 +#define HAVE_PTHREAD_H 1 +#define HAVE_SETLOCALE 1 + +#define HAVE_SIGACTION 1 +#define HAVE_SIGNAL 1 +#define HAVE_SIGSETJMP 1 +#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 +#define HAVE_SOCKET 1 +#define HAVE_SSL_GET_SHUTDOWN 1 +#define HAVE_STDBOOL_H 1 +#define HAVE_STRCASECMP 1 +#define HAVE_STRDUP 1 +#define HAVE_STRTOK_R 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRUCT_TIMEVAL 1 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_RESOURCE_H 1 +#define HAVE_SYS_SOCKET_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_UN_H 1 +#define HAVE_TERMIOS_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_UTIME 1 +#define HAVE_UTIME_H 1 + +#define HAVE_POSIX_STRERROR_R 1 +#define HAVE_STRERROR_R 1 +#define USE_MANUAL 1 + +#define __attribute__(x) + +#ifndef __cplusplus +#undef inline +#endif + +#endif /* HEADER_CURL_CONFIG_PLAN9_H */ diff --git a/third_party/curl/lib/config-riscos.h b/third_party/curl/lib/config-riscos.h new file mode 100644 index 0000000000..056f55405b --- /dev/null +++ b/third_party/curl/lib/config-riscos.h @@ -0,0 +1,274 @@ +#ifndef HEADER_CURL_CONFIG_RISCOS_H +#define HEADER_CURL_CONFIG_RISCOS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* ================================================================ */ +/* Hand crafted config file for RISC OS */ +/* ================================================================ */ + +/* Name of this package! */ +#undef PACKAGE + +/* Version number of this archive. */ +#undef VERSION + +/* Define cpu-machine-OS */ +#ifndef OS +#define OS "ARM-RISC OS" +#endif + +/* Define if you want the built-in manual */ +#define USE_MANUAL + +/* Define if you have the gethostbyname_r() function with 3 arguments */ +#undef HAVE_GETHOSTBYNAME_R_3 + +/* Define if you have the gethostbyname_r() function with 5 arguments */ +#undef HAVE_GETHOSTBYNAME_R_5 + +/* Define if you have the gethostbyname_r() function with 6 arguments */ +#undef HAVE_GETHOSTBYNAME_R_6 + +/* Define if you need the _REENTRANT define for some functions */ +#undef NEED_REENTRANT + +/* Define if you want to enable IPv6 support */ +#undef USE_IPV6 + +/* Define if struct sockaddr_in6 has the sin6_scope_id member */ +#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 + +/* Define this to 'int' if ssize_t is not an available typedefed type */ +#undef ssize_t + +/* Define this as a suitable file to read random data from */ +#undef RANDOM_FILE + +/* Define if you have the alarm function. */ +#define HAVE_ALARM + +/* Define if you have the header file. */ +#define HAVE_ARPA_INET_H + +/* Define if you have the `closesocket' function. */ +#undef HAVE_CLOSESOCKET + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H + +/* Define if you have the `ftruncate' function. */ +#define HAVE_FTRUNCATE + +/* Define if getaddrinfo exists and works */ +#define HAVE_GETADDRINFO + +/* Define if you have the `geteuid' function. */ +#undef HAVE_GETEUID + +/* Define if you have the `gethostbyname_r' function. */ +#undef HAVE_GETHOSTBYNAME_R + +/* Define if you have the `gethostname' function. */ +#define HAVE_GETHOSTNAME + +/* Define if you have the `getpass_r' function. */ +#undef HAVE_GETPASS_R + +/* Define if you have the `getpwuid' function. */ +#undef HAVE_GETPWUID + +/* Define if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY + +/* Define if you have the `timeval' struct. */ +#define HAVE_STRUCT_TIMEVAL + +/* Define if you have the header file. */ +#undef HAVE_IO_H + +/* Define if you need the malloc.h header file even with stdlib.h */ +/* #define NEED_MALLOC_H 1 */ + +/* Define if you have the header file. */ +#define HAVE_NETDB_H + +/* Define if you have the header file. */ +#define HAVE_NETINET_IN_H + +/* Define if you have the header file. */ +#define HAVE_NET_IF_H + +/* Define if you have the header file. */ +#undef HAVE_PWD_H + +/* Define if you have the `select' function. */ +#define HAVE_SELECT + +/* Define if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define if you have the `signal' function. */ +#define HAVE_SIGNAL + +/* Define if you have the `socket' function. */ +#define HAVE_SOCKET + +/* Define if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define if you have the `strcmpi' function. */ +#undef HAVE_STRCMPI + +/* Define if you have the `strdup' function. */ +#define HAVE_STRDUP + +/* Define if you have the `stricmp' function. */ +#define HAVE_STRICMP + +/* Define if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define if you have the `strtok_r' function. */ +#undef HAVE_STRTOK_R + +/* Define if you have the `strtoll' function. */ +#undef HAVE_STRTOLL + +/* Define if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define if you have the header file. */ +#define HAVE_SYS_SOCKET_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H + +/* Define if you have the header file. */ +#define HAVE_TERMIOS_H + +/* Define if you have the header file. */ +#undef HAVE_TERMIO_H + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H + +/* Name of package */ +#undef PACKAGE + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of `long long', as computed by sizeof. */ +#undef SIZEOF_LONG_LONG + +/* The size of `size_t', as computed by sizeof. */ +#define SIZEOF_SIZE_T 4 + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `unsigned' if does not define. */ +#undef size_t + +/* Define to `int' if does not define. */ +#undef ssize_t + +/* Define if you have a working ioctl FIONBIO function. */ +#define HAVE_IOCTL_FIONBIO + +/* to disable LDAP */ +#define CURL_DISABLE_LDAP + +/* Define if you have the recv function. */ +#define HAVE_RECV 1 + +/* Define to the type of arg 1 for recv. */ +#define RECV_TYPE_ARG1 int + +/* Define to the type of arg 2 for recv. */ +#define RECV_TYPE_ARG2 void * + +/* Define to the type of arg 3 for recv. */ +#define RECV_TYPE_ARG3 size_t + +/* Define to the type of arg 4 for recv. */ +#define RECV_TYPE_ARG4 int + +/* Define to the function return type for recv. */ +#define RECV_TYPE_RETV ssize_t + +/* Define if you have the send function. */ +#define HAVE_SEND 1 + +/* Define to the type of arg 1 for send. */ +#define SEND_TYPE_ARG1 int + +/* Define to the type qualifier of arg 2 for send. */ +#define SEND_QUAL_ARG2 const + +/* Define to the type of arg 2 for send. */ +#define SEND_TYPE_ARG2 void * + +/* Define to the type of arg 3 for send. */ +#define SEND_TYPE_ARG3 size_t + +/* Define to the type of arg 4 for send. */ +#define SEND_TYPE_ARG4 int + +/* Define to the function return type for send. */ +#define SEND_TYPE_RETV ssize_t + +#endif /* HEADER_CURL_CONFIG_RISCOS_H */ diff --git a/third_party/curl/lib/config-win32.h b/third_party/curl/lib/config-win32.h new file mode 100644 index 0000000000..d1341933b3 --- /dev/null +++ b/third_party/curl/lib/config-win32.h @@ -0,0 +1,512 @@ +#ifndef HEADER_CURL_CONFIG_WIN32_H +#define HEADER_CURL_CONFIG_WIN32_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* ================================================================ */ +/* Hand crafted config file for Windows */ +/* ================================================================ */ + +/* ---------------------------------------------------------------- */ +/* HEADER FILES */ +/* ---------------------------------------------------------------- */ + +/* Define if you have the header file. */ +/* #define HAVE_ARPA_INET_H 1 */ + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_IO_H 1 + +/* Define if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define if you need header even with header file. */ +#define NEED_MALLOC_H 1 + +/* Define if you have the header file. */ +/* #define HAVE_NETDB_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_NETINET_IN_H 1 */ + +/* Define to 1 if you have the header file. */ +#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__) +#define HAVE_STDBOOL_H 1 +#endif + +/* Define if you have the header file. */ +#if defined(__MINGW32__) +#define HAVE_SYS_PARAM_H 1 +#endif + +/* Define if you have the header file. */ +/* #define HAVE_SYS_SELECT_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_SOCKET_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_SOCKIO_H 1 */ + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +#if defined(__MINGW32__) +#define HAVE_SYS_TIME_H 1 +#endif + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_UTIME_H 1 + +/* Define if you have the header file. */ +/* #define HAVE_TERMIO_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_TERMIOS_H 1 */ + +/* Define if you have the header file. */ +#if defined(__MINGW32__) +#define HAVE_UNISTD_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#if defined(__MINGW32__) +#define HAVE_LIBGEN_H 1 +#endif + +/* ---------------------------------------------------------------- */ +/* OTHER HEADER INFO */ +/* ---------------------------------------------------------------- */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if bool is an available type. */ +#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__) +#define HAVE_BOOL_T 1 +#endif + +/* ---------------------------------------------------------------- */ +/* FUNCTIONS */ +/* ---------------------------------------------------------------- */ + +/* Define if you have the closesocket function. */ +#define HAVE_CLOSESOCKET 1 + +/* Define if you have the ftruncate function. */ +#if defined(__MINGW32__) +#define HAVE_FTRUNCATE 1 +#endif + +/* Define to 1 if you have the `getpeername' function. */ +#define HAVE_GETPEERNAME 1 + +/* Define to 1 if you have the getsockname function. */ +#define HAVE_GETSOCKNAME 1 + +/* Define if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define if you have the gettimeofday function. */ +#if defined(__MINGW32__) +#define HAVE_GETTIMEOFDAY 1 +#endif + +/* Define if you have the ioctlsocket function. */ +#define HAVE_IOCTLSOCKET 1 + +/* Define if you have a working ioctlsocket FIONBIO function. */ +#define HAVE_IOCTLSOCKET_FIONBIO 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the setlocale function. */ +#define HAVE_SETLOCALE 1 + +/* Define if you have the setmode function. */ +#define HAVE_SETMODE 1 + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the strcasecmp function. */ +#if defined(__MINGW32__) +#define HAVE_STRCASECMP 1 +#endif + +/* Define if you have the strdup function. */ +#define HAVE_STRDUP 1 + +/* Define if you have the stricmp function. */ +#define HAVE_STRICMP 1 + +/* Define if you have the strtoll function. */ +#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__) +#define HAVE_STRTOLL 1 +#endif + +/* Define if you have the utime function. */ +#define HAVE_UTIME 1 + +/* Define if you have the recv function. */ +#define HAVE_RECV 1 + +/* Define to the type of arg 1 for recv. */ +#define RECV_TYPE_ARG1 SOCKET + +/* Define to the type of arg 2 for recv. */ +#define RECV_TYPE_ARG2 char * + +/* Define to the type of arg 3 for recv. */ +#define RECV_TYPE_ARG3 int + +/* Define to the type of arg 4 for recv. */ +#define RECV_TYPE_ARG4 int + +/* Define to the function return type for recv. */ +#define RECV_TYPE_RETV int + +/* Define if you have the send function. */ +#define HAVE_SEND 1 + +/* Define to the type of arg 1 for send. */ +#define SEND_TYPE_ARG1 SOCKET + +/* Define to the type qualifier of arg 2 for send. */ +#define SEND_QUAL_ARG2 const + +/* Define to the type of arg 2 for send. */ +#define SEND_TYPE_ARG2 char * + +/* Define to the type of arg 3 for send. */ +#define SEND_TYPE_ARG3 int + +/* Define to the type of arg 4 for send. */ +#define SEND_TYPE_ARG4 int + +/* Define to the function return type for send. */ +#define SEND_TYPE_RETV int + +/* Define to 1 if you have the snprintf function. */ +#if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || defined(__MINGW32__) +#define HAVE_SNPRINTF 1 +#endif + +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 /* Vista */ +/* Define to 1 if you have a IPv6 capable working inet_ntop function. */ +#define HAVE_INET_NTOP 1 +/* Define to 1 if you have a IPv6 capable working inet_pton function. */ +#define HAVE_INET_PTON 1 +#endif + +/* Define to 1 if you have the `basename' function. */ +#if defined(__MINGW32__) +#define HAVE_BASENAME 1 +#endif + +/* Define to 1 if you have the strtok_r function. */ +#if defined(__MINGW32__) +#define HAVE_STRTOK_R 1 +#endif + +/* Define to 1 if you have the signal function. */ +#define HAVE_SIGNAL 1 + +/* ---------------------------------------------------------------- */ +/* TYPEDEF REPLACEMENTS */ +/* ---------------------------------------------------------------- */ + +/* Define if in_addr_t is not an available 'typedefed' type. */ +#define in_addr_t unsigned long + +/* Define if ssize_t is not an available 'typedefed' type. */ +#ifndef _SSIZE_T_DEFINED +# if defined(__MINGW32__) +# elif defined(_WIN64) +# define _SSIZE_T_DEFINED +# define ssize_t __int64 +# else +# define _SSIZE_T_DEFINED +# define ssize_t int +# endif +#endif + +/* ---------------------------------------------------------------- */ +/* TYPE SIZES */ +/* ---------------------------------------------------------------- */ + +/* Define to the size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* Define to the size of `long long', as computed by sizeof. */ +/* #define SIZEOF_LONG_LONG 8 */ + +/* Define to the size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* Define to the size of `size_t', as computed by sizeof. */ +#if defined(_WIN64) +# define SIZEOF_SIZE_T 8 +#else +# define SIZEOF_SIZE_T 4 +#endif + +/* Define to the size of `curl_off_t', as computed by sizeof. */ +#define SIZEOF_CURL_OFF_T 8 + +/* ---------------------------------------------------------------- */ +/* COMPILER SPECIFIC */ +/* ---------------------------------------------------------------- */ + +/* Define to nothing if compiler does not support 'const' qualifier. */ +/* #define const */ + +/* Define to nothing if compiler does not support 'volatile' qualifier. */ +/* #define volatile */ + +/* Windows should not have HAVE_GMTIME_R defined */ +/* #undef HAVE_GMTIME_R */ + +/* Define if the compiler supports the 'long long' data type. */ +#if (defined(_MSC_VER) && (_MSC_VER >= 1310)) || defined(__MINGW32__) +#define HAVE_LONGLONG 1 +#endif + +/* Define to avoid VS2005 complaining about portable C functions. */ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#endif + +/* mingw-w64 and visual studio >= 2005 (MSVCR80) + all default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */ +#if (defined(_MSC_VER) && (_MSC_VER >= 1400)) || defined(__MINGW32__) +# ifndef _USE_32BIT_TIME_T +# define SIZEOF_TIME_T 8 +# else +# define SIZEOF_TIME_T 4 +# endif +#endif + +/* Define some minimum and default build targets for Visual Studio */ +#if defined(_MSC_VER) + /* Officially, Microsoft's Windows SDK versions 6.X does not support Windows + 2000 as a supported build target. VS2008 default installations provides + an embedded Windows SDK v6.0A along with the claim that Windows 2000 is a + valid build target for VS2008. Popular belief is that binaries built with + VS2008 using Windows SDK versions v6.X and Windows 2000 as a build target + are functional. */ +# define VS2008_MIN_TARGET 0x0500 + + /* The minimum build target for VS2012 is Vista unless Update 1 is installed + and the v110_xp toolset is chosen. */ +# if defined(_USING_V110_SDK71_) +# define VS2012_MIN_TARGET 0x0501 +# else +# define VS2012_MIN_TARGET 0x0600 +# endif + + /* VS2008 default build target is Windows Vista. We override default target + to be Windows XP. */ +# define VS2008_DEF_TARGET 0x0501 + + /* VS2012 default build target is Windows Vista unless Update 1 is installed + and the v110_xp toolset is chosen. */ +# if defined(_USING_V110_SDK71_) +# define VS2012_DEF_TARGET 0x0501 +# else +# define VS2012_DEF_TARGET 0x0600 +# endif +#endif + +/* VS2008 default target settings and minimum build target check. */ +#if defined(_MSC_VER) && (_MSC_VER >= 1500) && (_MSC_VER <= 1600) +# ifndef _WIN32_WINNT +# define _WIN32_WINNT VS2008_DEF_TARGET +# endif +# ifndef WINVER +# define WINVER VS2008_DEF_TARGET +# endif +# if (_WIN32_WINNT < VS2008_MIN_TARGET) || (WINVER < VS2008_MIN_TARGET) +# error VS2008 does not support Windows build targets prior to Windows 2000 +# endif +#endif + +/* VS2012 default target settings and minimum build target check. */ +#if defined(_MSC_VER) && (_MSC_VER >= 1700) +# ifndef _WIN32_WINNT +# define _WIN32_WINNT VS2012_DEF_TARGET +# endif +# ifndef WINVER +# define WINVER VS2012_DEF_TARGET +# endif +# if (_WIN32_WINNT < VS2012_MIN_TARGET) || (WINVER < VS2012_MIN_TARGET) +# if defined(_USING_V110_SDK71_) +# error VS2012 does not support Windows build targets prior to Windows XP +# else +# error VS2012 does not support Windows build targets prior to Windows \ +Vista +# endif +# endif +#endif + +/* Windows XP is required for freeaddrinfo, getaddrinfo */ +#define HAVE_FREEADDRINFO 1 +#define HAVE_GETADDRINFO 1 +#define HAVE_GETADDRINFO_THREADSAFE 1 + +/* ---------------------------------------------------------------- */ +/* STRUCT RELATED */ +/* ---------------------------------------------------------------- */ + +/* Define if you have struct sockaddr_storage. */ +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define if you have struct timeval. */ +#define HAVE_STRUCT_TIMEVAL 1 + +/* Define if struct sockaddr_in6 has the sin6_scope_id member. */ +#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 + +/* ---------------------------------------------------------------- */ +/* LARGE FILE SUPPORT */ +/* ---------------------------------------------------------------- */ + +#if defined(_MSC_VER) && !defined(_WIN32_WCE) +# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# define USE_WIN32_LARGE_FILES +# else +# define USE_WIN32_SMALL_FILES +# endif +#endif + +#if defined(__MINGW32__) && !defined(USE_WIN32_LARGE_FILES) +# define USE_WIN32_LARGE_FILES +#endif + +#if !defined(USE_WIN32_LARGE_FILES) && !defined(USE_WIN32_SMALL_FILES) +# define USE_WIN32_SMALL_FILES +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#if defined(USE_WIN32_LARGE_FILES) && defined(__MINGW32__) +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif +#endif + +#ifdef USE_WIN32_LARGE_FILES +#define HAVE__FSEEKI64 +#endif + +/* Define to the size of `off_t', as computed by sizeof. */ +#if defined(__MINGW32__) && \ + defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64) +# define SIZEOF_OFF_T 8 +#else +# define SIZEOF_OFF_T 4 +#endif + +/* ---------------------------------------------------------------- */ +/* DNS RESOLVER SPECIALTY */ +/* ---------------------------------------------------------------- */ + +/* + * Undefine both USE_ARES and USE_THREADS_WIN32 for synchronous DNS. + */ + +/* Define to enable c-ares asynchronous DNS lookups. */ +/* #define USE_ARES 1 */ + +/* Default define to enable threaded asynchronous DNS lookups. */ +#if !defined(USE_SYNC_DNS) && !defined(USE_ARES) && \ + !defined(USE_THREADS_WIN32) +# define USE_THREADS_WIN32 1 +#endif + +#if defined(USE_ARES) && defined(USE_THREADS_WIN32) +# error "Only one DNS lookup specialty may be defined at most" +#endif + +/* ---------------------------------------------------------------- */ +/* LDAP SUPPORT */ +/* ---------------------------------------------------------------- */ + +#if defined(CURL_HAS_NOVELL_LDAPSDK) +#undef USE_WIN32_LDAP +#define HAVE_LDAP_SSL_H 1 +#define HAVE_LDAP_URL_PARSE 1 +#elif defined(CURL_HAS_OPENLDAP_LDAPSDK) +#undef USE_WIN32_LDAP +#define HAVE_LDAP_URL_PARSE 1 +#else +#undef HAVE_LDAP_URL_PARSE +#define HAVE_LDAP_SSL 1 +#define USE_WIN32_LDAP 1 +#endif + +/* Define to use the Windows crypto library. */ +#if !defined(CURL_WINDOWS_APP) +#define USE_WIN32_CRYPTO +#endif + +/* Define to use Unix sockets. */ +#define USE_UNIX_SOCKETS + +/* ---------------------------------------------------------------- */ +/* ADDITIONAL DEFINITIONS */ +/* ---------------------------------------------------------------- */ + +/* Define cpu-machine-OS */ +#ifndef OS +#if defined(_M_IX86) || defined(__i386__) /* x86 (MSVC or gcc) */ +#define OS "i386-pc-win32" +#elif defined(_M_X64) || defined(__x86_64__) /* x86_64 (MSVC >=2005 or gcc) */ +#define OS "x86_64-pc-win32" +#elif defined(_M_IA64) || defined(__ia64__) /* Itanium */ +#define OS "ia64-pc-win32" +#elif defined(_M_ARM_NT) || defined(__arm__) /* ARMv7-Thumb2 (Windows RT) */ +#define OS "thumbv7a-pc-win32" +#elif defined(_M_ARM64) || defined(__aarch64__) /* ARM64 (Windows 10) */ +#define OS "aarch64-pc-win32" +#else +#define OS "unknown-pc-win32" +#endif +#endif + +/* Name of package */ +#define PACKAGE "curl" + +/* If you want to build curl with the built-in manual */ +#define USE_MANUAL 1 + +#endif /* HEADER_CURL_CONFIG_WIN32_H */ diff --git a/third_party/curl/lib/config-win32ce.h b/third_party/curl/lib/config-win32ce.h new file mode 100644 index 0000000000..ae3ca290c3 --- /dev/null +++ b/third_party/curl/lib/config-win32ce.h @@ -0,0 +1,303 @@ +#ifndef HEADER_CURL_CONFIG_WIN32CE_H +#define HEADER_CURL_CONFIG_WIN32CE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* ================================================================ */ +/* lib/config-win32ce.h - Hand crafted config file for windows ce */ +/* ================================================================ */ + +/* ---------------------------------------------------------------- */ +/* HEADER FILES */ +/* ---------------------------------------------------------------- */ + +/* Define if you have the header file. */ +/* #define HAVE_ARPA_INET_H 1 */ + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_IO_H 1 + +/* Define if you need the malloc.h header file even with stdlib.h */ +#define NEED_MALLOC_H 1 + +/* Define if you have the header file. */ +/* #define HAVE_NETDB_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_NETINET_IN_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_PARAM_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_SELECT_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_SOCKET_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_SOCKIO_H 1 */ + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file */ +/* #define HAVE_SYS_TIME_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_TYPES_H 1 */ + +/* Define if you have the header file */ +#define HAVE_SYS_UTIME_H 1 + +/* Define if you have the header file. */ +/* #define HAVE_TERMIO_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_TERMIOS_H 1 */ + +/* Define if you have the header file. */ +#if defined(__MINGW32__) +#define HAVE_UNISTD_H 1 +#endif + +/* ---------------------------------------------------------------- */ +/* OTHER HEADER INFO */ +/* ---------------------------------------------------------------- */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* ---------------------------------------------------------------- */ +/* FUNCTIONS */ +/* ---------------------------------------------------------------- */ + +/* Define if you have the closesocket function. */ +#define HAVE_CLOSESOCKET 1 + +/* Define if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define if you have the gettimeofday function. */ +/* #define HAVE_GETTIMEOFDAY 1 */ + +/* Define if you have the ioctlsocket function. */ +#define HAVE_IOCTLSOCKET 1 + +/* Define if you have a working ioctlsocket FIONBIO function. */ +#define HAVE_IOCTLSOCKET_FIONBIO 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the strcasecmp function. */ +/* #define HAVE_STRCASECMP 1 */ + +/* Define if you have the strdup function. */ +/* #define HAVE_STRDUP 1 */ + +/* Define if you have the stricmp function. */ +/* #define HAVE_STRICMP 1 */ + +/* Define if you have the strtoll function. */ +#if defined(__MINGW32__) +#define HAVE_STRTOLL 1 +#endif + +/* Define if you have the utime function */ +#define HAVE_UTIME 1 + +/* Define if you have the recv function. */ +#define HAVE_RECV 1 + +/* Define to the type of arg 1 for recv. */ +#define RECV_TYPE_ARG1 SOCKET + +/* Define to the type of arg 2 for recv. */ +#define RECV_TYPE_ARG2 char * + +/* Define to the type of arg 3 for recv. */ +#define RECV_TYPE_ARG3 int + +/* Define to the type of arg 4 for recv. */ +#define RECV_TYPE_ARG4 int + +/* Define to the function return type for recv. */ +#define RECV_TYPE_RETV int + +/* Define if you have the send function. */ +#define HAVE_SEND 1 + +/* Define to the type of arg 1 for send. */ +#define SEND_TYPE_ARG1 SOCKET + +/* Define to the type qualifier of arg 2 for send. */ +#define SEND_QUAL_ARG2 const + +/* Define to the type of arg 2 for send. */ +#define SEND_TYPE_ARG2 char * + +/* Define to the type of arg 3 for send. */ +#define SEND_TYPE_ARG3 int + +/* Define to the type of arg 4 for send. */ +#define SEND_TYPE_ARG4 int + +/* Define to the function return type for send. */ +#define SEND_TYPE_RETV int + +/* ---------------------------------------------------------------- */ +/* TYPEDEF REPLACEMENTS */ +/* ---------------------------------------------------------------- */ + +/* Define this if in_addr_t is not an available 'typedefed' type */ +#define in_addr_t unsigned long + +/* Define ssize_t if it is not an available 'typedefed' type */ +#if defined(_WIN64) +#define ssize_t __int64 +#else +#define ssize_t int +#endif + +/* ---------------------------------------------------------------- */ +/* TYPE SIZES */ +/* ---------------------------------------------------------------- */ + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of `long long', as computed by sizeof. */ +/* #define SIZEOF_LONG_LONG 8 */ + +/* Define to the size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of `size_t', as computed by sizeof. */ +#if defined(_WIN64) +# define SIZEOF_SIZE_T 8 +#else +# define SIZEOF_SIZE_T 4 +#endif + +/* ---------------------------------------------------------------- */ +/* STRUCT RELATED */ +/* ---------------------------------------------------------------- */ + +/* Define this if you have struct sockaddr_storage */ +/* #define HAVE_STRUCT_SOCKADDR_STORAGE 1 */ + +/* Define this if you have struct timeval */ +#define HAVE_STRUCT_TIMEVAL 1 + +/* Define this if struct sockaddr_in6 has the sin6_scope_id member */ +#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 + +/* ---------------------------------------------------------------- */ +/* COMPILER SPECIFIC */ +/* ---------------------------------------------------------------- */ + +/* Undef keyword 'const' if it does not work. */ +/* #undef const */ + +/* Define to avoid VS2005 complaining about portable C functions */ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#endif + +/* VS2005 and later default size for time_t is 64-bit, unless */ +/* _USE_32BIT_TIME_T has been defined to get a 32-bit time_t. */ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +# ifndef _USE_32BIT_TIME_T +# define SIZEOF_TIME_T 8 +# else +# define SIZEOF_TIME_T 4 +# endif +#endif + +/* ---------------------------------------------------------------- */ +/* LARGE FILE SUPPORT */ +/* ---------------------------------------------------------------- */ + +#if defined(_MSC_VER) && !defined(_WIN32_WCE) +# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# define USE_WIN32_LARGE_FILES +# else +# define USE_WIN32_SMALL_FILES +# endif +#endif + +#if !defined(USE_WIN32_LARGE_FILES) && !defined(USE_WIN32_SMALL_FILES) +# define USE_WIN32_SMALL_FILES +#endif + +/* ---------------------------------------------------------------- */ +/* LDAP SUPPORT */ +/* ---------------------------------------------------------------- */ + +#define USE_WIN32_LDAP 1 +#undef HAVE_LDAP_URL_PARSE + +/* ---------------------------------------------------------------- */ +/* ADDITIONAL DEFINITIONS */ +/* ---------------------------------------------------------------- */ + +/* Define cpu-machine-OS */ +#ifndef OS +#define OS "i386-pc-win32ce" +#endif + +/* Name of package */ +#define PACKAGE "curl" + +/* ---------------------------------------------------------------- */ +/* WinCE */ +/* ---------------------------------------------------------------- */ + +#ifndef UNICODE +# define UNICODE +#endif + +#ifndef _UNICODE +# define _UNICODE +#endif + +#define CURL_DISABLE_FILE 1 +#define CURL_DISABLE_TELNET 1 +#define CURL_DISABLE_LDAP 1 + +#define ENOSPC 1 +#define ENOMEM 2 +#define EAGAIN 3 + +extern int stat(const char *path, struct stat *buffer); + +#endif /* HEADER_CURL_CONFIG_WIN32CE_H */ diff --git a/third_party/curl/lib/conncache.c b/third_party/curl/lib/conncache.c new file mode 100644 index 0000000000..92fd60c4df --- /dev/null +++ b/third_party/curl/lib/conncache.c @@ -0,0 +1,1189 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Linus Nielsen Feltzing, + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "urldata.h" +#include "url.h" +#include "cfilters.h" +#include "progress.h" +#include "multiif.h" +#include "sendf.h" +#include "conncache.h" +#include "http_negotiate.h" +#include "http_ntlm.h" +#include "share.h" +#include "sigpipe.h" +#include "connect.h" +#include "select.h" +#include "strcase.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define HASHKEY_SIZE 128 + +static void connc_discard_conn(struct conncache *connc, + struct Curl_easy *last_data, + struct connectdata *conn, + bool aborted); +static void connc_disconnect(struct Curl_easy *data, + struct connectdata *conn, + struct conncache *connc, + bool do_shutdown); +static void connc_run_conn_shutdown(struct Curl_easy *data, + struct connectdata *conn, + bool *done); +static void connc_run_conn_shutdown_handler(struct Curl_easy *data, + struct connectdata *conn); +static CURLMcode connc_update_shutdown_ev(struct Curl_multi *multi, + struct Curl_easy *data, + struct connectdata *conn); +static void connc_shutdown_all(struct conncache *connc, int timeout_ms); + +static CURLcode bundle_create(struct connectbundle **bundlep) +{ + DEBUGASSERT(*bundlep == NULL); + *bundlep = malloc(sizeof(struct connectbundle)); + if(!*bundlep) + return CURLE_OUT_OF_MEMORY; + + (*bundlep)->num_connections = 0; + (*bundlep)->multiuse = BUNDLE_UNKNOWN; + + Curl_llist_init(&(*bundlep)->conn_list, NULL); + return CURLE_OK; +} + +static void bundle_destroy(struct connectbundle *bundle) +{ + free(bundle); +} + +/* Add a connection to a bundle */ +static void bundle_add_conn(struct connectbundle *bundle, + struct connectdata *conn) +{ + Curl_llist_append(&bundle->conn_list, conn, &conn->bundle_node); + conn->bundle = bundle; + bundle->num_connections++; +} + +/* Remove a connection from a bundle */ +static int bundle_remove_conn(struct connectbundle *bundle, + struct connectdata *conn) +{ + struct Curl_llist_element *curr; + + curr = bundle->conn_list.head; + while(curr) { + if(curr->ptr == conn) { + Curl_llist_remove(&bundle->conn_list, curr, NULL); + bundle->num_connections--; + conn->bundle = NULL; + return 1; /* we removed a handle */ + } + curr = curr->next; + } + DEBUGASSERT(0); + return 0; +} + +static void free_bundle_hash_entry(void *freethis) +{ + struct connectbundle *b = (struct connectbundle *) freethis; + + bundle_destroy(b); +} + +int Curl_conncache_init(struct conncache *connc, + struct Curl_multi *multi, size_t size) +{ + /* allocate a new easy handle to use when closing cached connections */ + connc->closure_handle = curl_easy_init(); + if(!connc->closure_handle) + return 1; /* bad */ + connc->closure_handle->state.internal = true; + #ifdef DEBUGBUILD + if(getenv("CURL_DEBUG")) + connc->closure_handle->set.verbose = true; +#endif + + Curl_hash_init(&connc->hash, size, Curl_hash_str, + Curl_str_key_compare, free_bundle_hash_entry); + connc->closure_handle->state.conn_cache = connc; + connc->multi = multi; + Curl_llist_init(&connc->shutdowns.conn_list, NULL); + + return 0; /* good */ +} + +void Curl_conncache_destroy(struct conncache *connc) +{ + if(connc) { + Curl_hash_destroy(&connc->hash); + connc->multi = NULL; + DEBUGASSERT(!Curl_llist_count(&connc->shutdowns.conn_list)); + } +} + +/* creates a key to find a bundle for this connection */ +static void hashkey(struct connectdata *conn, char *buf, size_t len) +{ + const char *hostname; + long port = conn->remote_port; + DEBUGASSERT(len >= HASHKEY_SIZE); +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + hostname = conn->http_proxy.host.name; + port = conn->primary.remote_port; + } + else +#endif + if(conn->bits.conn_to_host) + hostname = conn->conn_to_host.name; + else + hostname = conn->host.name; + + /* put the numbers first so that the hostname gets cut off if too long */ +#ifdef USE_IPV6 + msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname); +#else + msnprintf(buf, len, "%ld/%s", port, hostname); +#endif + Curl_strntolower(buf, buf, len); +} + +/* Returns number of connections currently held in the connection cache. + Locks/unlocks the cache itself! +*/ +size_t Curl_conncache_size(struct Curl_easy *data) +{ + size_t num; + CONNCACHE_LOCK(data); + num = data->state.conn_cache->num_conn; + CONNCACHE_UNLOCK(data); + return num; +} + +/* Look up the bundle with all the connections to the same host this + connectdata struct is setup to use. + + **NOTE**: When it returns, it holds the connection cache lock! */ +struct connectbundle * +Curl_conncache_find_bundle(struct Curl_easy *data, + struct connectdata *conn, + struct conncache *connc) +{ + struct connectbundle *bundle = NULL; + CONNCACHE_LOCK(data); + if(connc) { + char key[HASHKEY_SIZE]; + hashkey(conn, key, sizeof(key)); + bundle = Curl_hash_pick(&connc->hash, key, strlen(key)); + } + + return bundle; +} + +static void *connc_add_bundle(struct conncache *connc, + char *key, struct connectbundle *bundle) +{ + return Curl_hash_add(&connc->hash, key, strlen(key), bundle); +} + +static void connc_remove_bundle(struct conncache *connc, + struct connectbundle *bundle) +{ + struct Curl_hash_iterator iter; + struct Curl_hash_element *he; + + if(!connc) + return; + + Curl_hash_start_iterate(&connc->hash, &iter); + + he = Curl_hash_next_element(&iter); + while(he) { + if(he->ptr == bundle) { + /* The bundle is destroyed by the hash destructor function, + free_bundle_hash_entry() */ + Curl_hash_delete(&connc->hash, he->key, he->key_len); + return; + } + + he = Curl_hash_next_element(&iter); + } +} + +CURLcode Curl_conncache_add_conn(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct connectbundle *bundle = NULL; + struct connectdata *conn = data->conn; + struct conncache *connc = data->state.conn_cache; + DEBUGASSERT(conn); + + /* *find_bundle() locks the connection cache */ + bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache); + if(!bundle) { + char key[HASHKEY_SIZE]; + + result = bundle_create(&bundle); + if(result) { + goto unlock; + } + + hashkey(conn, key, sizeof(key)); + + if(!connc_add_bundle(data->state.conn_cache, key, bundle)) { + bundle_destroy(bundle); + result = CURLE_OUT_OF_MEMORY; + goto unlock; + } + } + + bundle_add_conn(bundle, conn); + conn->connection_id = connc->next_connection_id++; + connc->num_conn++; + + DEBUGF(infof(data, "Added connection %" CURL_FORMAT_CURL_OFF_T ". " + "The cache now contains %zu members", + conn->connection_id, connc->num_conn)); + +unlock: + CONNCACHE_UNLOCK(data); + + return result; +} + +static void connc_remove_conn(struct conncache *connc, + struct connectdata *conn) +{ + struct connectbundle *bundle = conn->bundle; + + /* The bundle pointer can be NULL, since this function can be called + due to a failed connection attempt, before being added to a bundle */ + if(bundle) { + bundle_remove_conn(bundle, conn); + if(connc && bundle->num_connections == 0) + connc_remove_bundle(connc, bundle); + conn->bundle = NULL; /* removed from it */ + if(connc) + connc->num_conn--; + } +} + +/* + * Removes the connectdata object from the connection cache, but the transfer + * still owns this connection. + * + * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function + * already holds the lock or not. + */ +void Curl_conncache_remove_conn(struct Curl_easy *data, + struct connectdata *conn, bool lock) +{ + struct conncache *connc = data->state.conn_cache; + + if(lock) + CONNCACHE_LOCK(data); + connc_remove_conn(connc, conn); + if(lock) + CONNCACHE_UNLOCK(data); + if(connc) + DEBUGF(infof(data, "The cache now contains %zu members", + connc->num_conn)); +} + +/* This function iterates the entire connection cache and calls the function + func() with the connection pointer as the first argument and the supplied + 'param' argument as the other. + + The conncache lock is still held when the callback is called. It needs it, + so that it can safely continue traversing the lists once the callback + returns. + + Returns 1 if the loop was aborted due to the callback's return code. + + Return 0 from func() to continue the loop, return 1 to abort it. + */ +bool Curl_conncache_foreach(struct Curl_easy *data, + struct conncache *connc, + void *param, + int (*func)(struct Curl_easy *data, + struct connectdata *conn, void *param)) +{ + struct Curl_hash_iterator iter; + struct Curl_llist_element *curr; + struct Curl_hash_element *he; + + if(!connc) + return FALSE; + + CONNCACHE_LOCK(data); + Curl_hash_start_iterate(&connc->hash, &iter); + + he = Curl_hash_next_element(&iter); + while(he) { + struct connectbundle *bundle; + + bundle = he->ptr; + he = Curl_hash_next_element(&iter); + + curr = bundle->conn_list.head; + while(curr) { + /* Yes, we need to update curr before calling func(), because func() + might decide to remove the connection */ + struct connectdata *conn = curr->ptr; + curr = curr->next; + + if(1 == func(data, conn, param)) { + CONNCACHE_UNLOCK(data); + return TRUE; + } + } + } + CONNCACHE_UNLOCK(data); + return FALSE; +} + +/* Return the first connection found in the cache. Used when closing all + connections. + + NOTE: no locking is done here as this is presumably only done when cleaning + up a cache! +*/ +static struct connectdata * +connc_find_first_connection(struct conncache *connc) +{ + struct Curl_hash_iterator iter; + struct Curl_hash_element *he; + struct connectbundle *bundle; + + Curl_hash_start_iterate(&connc->hash, &iter); + + he = Curl_hash_next_element(&iter); + while(he) { + struct Curl_llist_element *curr; + bundle = he->ptr; + + curr = bundle->conn_list.head; + if(curr) { + return curr->ptr; + } + + he = Curl_hash_next_element(&iter); + } + + return NULL; +} + +/* + * Give ownership of a connection back to the connection cache. Might + * disconnect the oldest existing in there to make space. + * + * Return TRUE if stored, FALSE if closed. + */ +bool Curl_conncache_return_conn(struct Curl_easy *data, + struct connectdata *conn) +{ + unsigned int maxconnects = !data->multi->maxconnects ? + data->multi->num_easy * 4: data->multi->maxconnects; + struct connectdata *conn_candidate = NULL; + + conn->lastused = Curl_now(); /* it was used up until now */ + if(maxconnects && Curl_conncache_size(data) > maxconnects) { + infof(data, "Connection cache is full, closing the oldest one"); + + conn_candidate = Curl_conncache_extract_oldest(data); + if(conn_candidate) { + /* Use the closure handle for this disconnect so that anything that + happens during the disconnect is not stored and associated with the + 'data' handle which already just finished a transfer and it is + important that details from this (unrelated) disconnect does not + taint meta-data in the data handle. */ + struct conncache *connc = data->state.conn_cache; + connc_disconnect(NULL, conn_candidate, connc, TRUE); + } + } + + return (conn_candidate == conn) ? FALSE : TRUE; + +} + +/* + * This function finds the connection in the connection bundle that has been + * unused for the longest time. + * + * Does not lock the connection cache! + * + * Returns the pointer to the oldest idle connection, or NULL if none was + * found. + */ +struct connectdata * +Curl_conncache_extract_bundle(struct Curl_easy *data, + struct connectbundle *bundle) +{ + struct Curl_llist_element *curr; + timediff_t highscore = -1; + timediff_t score; + struct curltime now; + struct connectdata *conn_candidate = NULL; + struct connectdata *conn; + + (void)data; + + now = Curl_now(); + + curr = bundle->conn_list.head; + while(curr) { + conn = curr->ptr; + + if(!CONN_INUSE(conn)) { + /* Set higher score for the age passed since the connection was used */ + score = Curl_timediff(now, conn->lastused); + + if(score > highscore) { + highscore = score; + conn_candidate = conn; + } + } + curr = curr->next; + } + if(conn_candidate) { + /* remove it to prevent another thread from nicking it */ + bundle_remove_conn(bundle, conn_candidate); + data->state.conn_cache->num_conn--; + DEBUGF(infof(data, "The cache now contains %zu members", + data->state.conn_cache->num_conn)); + } + + return conn_candidate; +} + +/* + * This function finds the connection in the connection cache that has been + * unused for the longest time and extracts that from the bundle. + * + * Returns the pointer to the connection, or NULL if none was found. + */ +struct connectdata * +Curl_conncache_extract_oldest(struct Curl_easy *data) +{ + struct conncache *connc = data->state.conn_cache; + struct Curl_hash_iterator iter; + struct Curl_llist_element *curr; + struct Curl_hash_element *he; + timediff_t highscore =- 1; + timediff_t score; + struct curltime now; + struct connectdata *conn_candidate = NULL; + struct connectbundle *bundle; + struct connectbundle *bundle_candidate = NULL; + + now = Curl_now(); + + CONNCACHE_LOCK(data); + Curl_hash_start_iterate(&connc->hash, &iter); + + he = Curl_hash_next_element(&iter); + while(he) { + struct connectdata *conn; + + bundle = he->ptr; + + curr = bundle->conn_list.head; + while(curr) { + conn = curr->ptr; + + if(!CONN_INUSE(conn) && !conn->bits.close && + !conn->connect_only) { + /* Set higher score for the age passed since the connection was used */ + score = Curl_timediff(now, conn->lastused); + + if(score > highscore) { + highscore = score; + conn_candidate = conn; + bundle_candidate = bundle; + } + } + curr = curr->next; + } + + he = Curl_hash_next_element(&iter); + } + if(conn_candidate) { + /* remove it to prevent another thread from nicking it */ + bundle_remove_conn(bundle_candidate, conn_candidate); + connc->num_conn--; + DEBUGF(infof(data, "The cache now contains %zu members", + connc->num_conn)); + } + CONNCACHE_UNLOCK(data); + + return conn_candidate; +} + +static void connc_shutdown_discard_all(struct conncache *connc) +{ + struct Curl_llist_element *e = connc->shutdowns.conn_list.head; + struct connectdata *conn; + + if(!e) + return; + + DEBUGF(infof(connc->closure_handle, "conncache_shutdown_discard_all")); + DEBUGASSERT(!connc->shutdowns.iter_locked); + connc->shutdowns.iter_locked = TRUE; + while(e) { + conn = e->ptr; + Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL); + DEBUGF(infof(connc->closure_handle, "discard connection #%" + CURL_FORMAT_CURL_OFF_T, conn->connection_id)); + connc_disconnect(NULL, conn, connc, FALSE); + e = connc->shutdowns.conn_list.head; + } + connc->shutdowns.iter_locked = FALSE; +} + +static void connc_close_all(struct conncache *connc) +{ + struct Curl_easy *data = connc->closure_handle; + struct connectdata *conn; + int timeout_ms = 0; + SIGPIPE_VARIABLE(pipe_st); + + if(!data) + return; + + /* Move all connections to the shutdown list */ + sigpipe_init(&pipe_st); + conn = connc_find_first_connection(connc); + while(conn) { + connc_remove_conn(connc, conn); + sigpipe_apply(data, &pipe_st); + /* This will remove the connection from the cache */ + connclose(conn, "kill all"); + Curl_conncache_remove_conn(connc->closure_handle, conn, TRUE); + connc_discard_conn(connc, connc->closure_handle, conn, FALSE); + + conn = connc_find_first_connection(connc); + } + + /* Just for testing, run graceful shutdown */ +#ifdef DEBUGBUILD + { + char *p = getenv("CURL_GRACEFUL_SHUTDOWN"); + if(p) { + long l = strtol(p, NULL, 10); + if(l > 0 && l < INT_MAX) + timeout_ms = (int)l; + } + } +#endif + connc_shutdown_all(connc, timeout_ms); + + /* discard all connections in the shutdown list */ + connc_shutdown_discard_all(connc); + + sigpipe_apply(data, &pipe_st); + Curl_hostcache_clean(data, data->dns.hostcache); + Curl_close(&data); + sigpipe_restore(&pipe_st); +} + +void Curl_conncache_close_all_connections(struct conncache *connc) +{ + connc_close_all(connc); +} + +static void connc_shutdown_discard_oldest(struct conncache *connc) +{ + struct Curl_llist_element *e; + struct connectdata *conn; + + DEBUGASSERT(!connc->shutdowns.iter_locked); + if(connc->shutdowns.iter_locked) + return; + + e = connc->shutdowns.conn_list.head; + if(e) { + SIGPIPE_VARIABLE(pipe_st); + conn = e->ptr; + Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL); + sigpipe_init(&pipe_st); + sigpipe_apply(connc->closure_handle, &pipe_st); + connc_disconnect(NULL, conn, connc, FALSE); + sigpipe_restore(&pipe_st); + } +} + +static void connc_discard_conn(struct conncache *connc, + struct Curl_easy *last_data, + struct connectdata *conn, + bool aborted) +{ + /* `last_data`, if present, is the transfer that last worked with + * the connection. It is present when the connection is being shut down + * via `Curl_conncache_discard_conn()`, e.g. when the transfer failed + * or does not allow connection reuse. + * Using the original handle is necessary for shutting down the protocol + * handler belonging to the connection. Protocols like 'file:' rely on + * being invoked to clean up their allocations in the easy handle. + * When a connection comes from the cache, the transfer is no longer + * there and we use the cache is own closure handle. + */ + struct Curl_easy *data = last_data? last_data : connc->closure_handle; + bool done = FALSE; + + DEBUGASSERT(data); + DEBUGASSERT(connc); + DEBUGASSERT(!conn->bundle); + + /* + * If this connection is not marked to force-close, leave it open if there + * are other users of it + */ + if(CONN_INUSE(conn) && !aborted) { + DEBUGF(infof(data, "[CCACHE] not discarding #%" CURL_FORMAT_CURL_OFF_T + " still in use by %zu transfers", conn->connection_id, + CONN_INUSE(conn))); + return; + } + + /* treat the connection as aborted in CONNECT_ONLY situations, we do + * not know what the APP did with it. */ + if(conn->connect_only) + aborted = TRUE; + conn->bits.aborted = aborted; + + /* We do not shutdown dead connections. The term 'dead' can be misleading + * here, as we also mark errored connections/transfers as 'dead'. + * If we do a shutdown for an aborted transfer, the server might think + * it was successful otherwise (for example an ftps: upload). This is + * not what we want. */ + if(aborted) + done = TRUE; + if(!done) { + /* Attempt to shutdown the connection right away. */ + Curl_attach_connection(data, conn); + connc_run_conn_shutdown(data, conn, &done); + DEBUGF(infof(data, "[CCACHE] shutdown #%" CURL_FORMAT_CURL_OFF_T + ", done=%d",conn->connection_id, done)); + Curl_detach_connection(data); + } + + if(done) { + connc_disconnect(data, conn, connc, FALSE); + return; + } + + DEBUGASSERT(!connc->shutdowns.iter_locked); + if(connc->shutdowns.iter_locked) { + DEBUGF(infof(data, "[CCACHE] discarding #%" CURL_FORMAT_CURL_OFF_T + ", list locked", conn->connection_id)); + connc_disconnect(data, conn, connc, FALSE); + return; + } + + /* Add the connection to our shutdown list for non-blocking shutdown + * during multi processing. */ + if(data->multi && data->multi->max_shutdown_connections > 0 && + (data->multi->max_shutdown_connections >= + (long)Curl_llist_count(&connc->shutdowns.conn_list))) { + DEBUGF(infof(data, "[CCACHE] discarding oldest shutdown connection " + "due to limit of %ld", + data->multi->max_shutdown_connections)); + connc_shutdown_discard_oldest(connc); + } + + if(data->multi && data->multi->socket_cb) { + DEBUGASSERT(connc == &data->multi->conn_cache); + /* Start with an empty shutdown pollset, so out internal closure handle + * is added to the sockets. */ + memset(&conn->shutdown_poll, 0, sizeof(conn->shutdown_poll)); + if(connc_update_shutdown_ev(data->multi, connc->closure_handle, conn)) { + DEBUGF(infof(data, "[CCACHE] update events for shutdown failed, " + "discarding #%" CURL_FORMAT_CURL_OFF_T, + conn->connection_id)); + connc_disconnect(data, conn, connc, FALSE); + return; + } + } + + Curl_llist_append(&connc->shutdowns.conn_list, conn, &conn->bundle_node); + DEBUGF(infof(data, "[CCACHE] added #%" CURL_FORMAT_CURL_OFF_T + " to shutdown list of length %zu", conn->connection_id, + Curl_llist_count(&connc->shutdowns.conn_list))); +} + +void Curl_conncache_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool aborted) +{ + DEBUGASSERT(data); + /* Connection must no longer be in and connection cache */ + DEBUGASSERT(!conn->bundle); + + if(data->multi) { + /* Add it to the multi's conncache for shutdown handling */ + infof(data, "%s connection #%" CURL_FORMAT_CURL_OFF_T, + aborted? "closing" : "shutting down", conn->connection_id); + connc_discard_conn(&data->multi->conn_cache, data, conn, aborted); + } + else { + /* No multi available. Make a best-effort shutdown + close */ + infof(data, "closing connection #%" CURL_FORMAT_CURL_OFF_T, + conn->connection_id); + DEBUGASSERT(!conn->bundle); + connc_run_conn_shutdown_handler(data, conn); + connc_disconnect(data, conn, NULL, !aborted); + } +} + +static void connc_run_conn_shutdown_handler(struct Curl_easy *data, + struct connectdata *conn) +{ + if(!conn->bits.shutdown_handler) { + if(conn->dns_entry) { + Curl_resolv_unlock(data, conn->dns_entry); + conn->dns_entry = NULL; + } + + /* Cleanup NTLM connection-related data */ + Curl_http_auth_cleanup_ntlm(conn); + + /* Cleanup NEGOTIATE connection-related data */ + Curl_http_auth_cleanup_negotiate(conn); + + if(conn->handler && conn->handler->disconnect) { + /* This is set if protocol-specific cleanups should be made */ + DEBUGF(infof(data, "connection #%" CURL_FORMAT_CURL_OFF_T + ", shutdown protocol handler (aborted=%d)", + conn->connection_id, conn->bits.aborted)); + conn->handler->disconnect(data, conn, conn->bits.aborted); + } + + /* possible left-overs from the async name resolvers */ + Curl_resolver_cancel(data); + + conn->bits.shutdown_handler = TRUE; + } +} + +static void connc_run_conn_shutdown(struct Curl_easy *data, + struct connectdata *conn, + bool *done) +{ + CURLcode r1, r2; + bool done1, done2; + + /* We expect to be attached when called */ + DEBUGASSERT(data->conn == conn); + + connc_run_conn_shutdown_handler(data, conn); + + if(conn->bits.shutdown_filters) { + *done = TRUE; + return; + } + + if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET)) + r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1); + else { + r1 = CURLE_OK; + done1 = TRUE; + } + + if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET)) + r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2); + else { + r2 = CURLE_OK; + done2 = TRUE; + } + + /* we are done when any failed or both report success */ + *done = (r1 || r2 || (done1 && done2)); + if(*done) + conn->bits.shutdown_filters = TRUE; +} + +CURLcode Curl_conncache_add_pollfds(struct conncache *connc, + struct curl_pollfds *cpfds) +{ + CURLcode result = CURLE_OK; + + DEBUGASSERT(!connc->shutdowns.iter_locked); + connc->shutdowns.iter_locked = TRUE; + if(connc->shutdowns.conn_list.head) { + struct Curl_llist_element *e; + struct easy_pollset ps; + struct connectdata *conn; + + for(e = connc->shutdowns.conn_list.head; e; e = e->next) { + conn = e->ptr; + memset(&ps, 0, sizeof(ps)); + Curl_attach_connection(connc->closure_handle, conn); + Curl_conn_adjust_pollset(connc->closure_handle, &ps); + Curl_detach_connection(connc->closure_handle); + + result = Curl_pollfds_add_ps(cpfds, &ps); + if(result) { + Curl_pollfds_cleanup(cpfds); + goto out; + } + } + } +out: + connc->shutdowns.iter_locked = FALSE; + return result; +} + +CURLcode Curl_conncache_add_waitfds(struct conncache *connc, + struct curl_waitfds *cwfds) +{ + CURLcode result = CURLE_OK; + + DEBUGASSERT(!connc->shutdowns.iter_locked); + connc->shutdowns.iter_locked = TRUE; + if(connc->shutdowns.conn_list.head) { + struct Curl_llist_element *e; + struct easy_pollset ps; + struct connectdata *conn; + + for(e = connc->shutdowns.conn_list.head; e; e = e->next) { + conn = e->ptr; + memset(&ps, 0, sizeof(ps)); + Curl_attach_connection(connc->closure_handle, conn); + Curl_conn_adjust_pollset(connc->closure_handle, &ps); + Curl_detach_connection(connc->closure_handle); + + result = Curl_waitfds_add_ps(cwfds, &ps); + if(result) + goto out; + } + } +out: + connc->shutdowns.iter_locked = FALSE; + return result; +} + +static void connc_perform(struct conncache *connc) +{ + struct Curl_easy *data = connc->closure_handle; + struct Curl_llist_element *e = connc->shutdowns.conn_list.head; + struct Curl_llist_element *enext; + struct connectdata *conn; + struct curltime *nowp = NULL; + struct curltime now; + timediff_t next_from_now_ms = 0, ms; + bool done; + + if(!e) + return; + + DEBUGASSERT(data); + DEBUGASSERT(!connc->shutdowns.iter_locked); + DEBUGF(infof(data, "[CCACHE] perform, %zu connections being shutdown", + Curl_llist_count(&connc->shutdowns.conn_list))); + connc->shutdowns.iter_locked = TRUE; + while(e) { + enext = e->next; + conn = e->ptr; + Curl_attach_connection(data, conn); + connc_run_conn_shutdown(data, conn, &done); + DEBUGF(infof(data, "[CCACHE] shutdown #%" CURL_FORMAT_CURL_OFF_T + ", done=%d", conn->connection_id, done)); + Curl_detach_connection(data); + if(done) { + Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL); + connc_disconnect(NULL, conn, connc, FALSE); + } + else { + /* Not done, when does this connection time out? */ + if(!nowp) { + now = Curl_now(); + nowp = &now; + } + ms = Curl_conn_shutdown_timeleft(conn, nowp); + if(ms && ms < next_from_now_ms) + next_from_now_ms = ms; + } + e = enext; + } + connc->shutdowns.iter_locked = FALSE; + + if(next_from_now_ms) + Curl_expire(data, next_from_now_ms, EXPIRE_RUN_NOW); +} + +void Curl_conncache_multi_perform(struct Curl_multi *multi) +{ + connc_perform(&multi->conn_cache); +} + + +/* + * Disconnects the given connection. Note the connection may not be the + * primary connection, like when freeing room in the connection cache or + * killing of a dead old connection. + * + * A connection needs an easy handle when closing down. We support this passed + * in separately since the connection to get closed here is often already + * disassociated from an easy handle. + * + * This function MUST NOT reset state in the Curl_easy struct if that + * is not strictly bound to the life-time of *this* particular connection. + * + */ +static void connc_disconnect(struct Curl_easy *data, + struct connectdata *conn, + struct conncache *connc, + bool do_shutdown) +{ + bool done; + + /* there must be a connection to close */ + DEBUGASSERT(conn); + /* it must be removed from the connection cache */ + DEBUGASSERT(!conn->bundle); + /* there must be an associated transfer */ + DEBUGASSERT(data || connc); + if(!data) + data = connc->closure_handle; + + /* the transfer must be detached from the connection */ + DEBUGASSERT(data && !data->conn); + + Curl_attach_connection(data, conn); + + if(connc && connc->multi && connc->multi->socket_cb) { + struct easy_pollset ps; + /* With an empty pollset, all previously polled sockets will be removed + * via the multi_socket API callback. */ + memset(&ps, 0, sizeof(ps)); + (void)Curl_multi_pollset_ev(connc->multi, data, &ps, &conn->shutdown_poll); + } + + connc_run_conn_shutdown_handler(data, conn); + if(do_shutdown) { + /* Make a last attempt to shutdown handlers and filters, if + * not done so already. */ + connc_run_conn_shutdown(data, conn, &done); + } + + if(connc) + DEBUGF(infof(data, "[CCACHE] closing #%" CURL_FORMAT_CURL_OFF_T, + conn->connection_id)); + else + DEBUGF(infof(data, "closing connection #%" CURL_FORMAT_CURL_OFF_T, + conn->connection_id)); + Curl_conn_close(data, SECONDARYSOCKET); + Curl_conn_close(data, FIRSTSOCKET); + Curl_detach_connection(data); + + Curl_conn_free(data, conn); +} + + +static CURLMcode connc_update_shutdown_ev(struct Curl_multi *multi, + struct Curl_easy *data, + struct connectdata *conn) +{ + struct easy_pollset ps; + CURLMcode mresult; + + DEBUGASSERT(data); + DEBUGASSERT(multi); + DEBUGASSERT(multi->socket_cb); + + memset(&ps, 0, sizeof(ps)); + Curl_attach_connection(data, conn); + Curl_conn_adjust_pollset(data, &ps); + Curl_detach_connection(data); + + mresult = Curl_multi_pollset_ev(multi, data, &ps, &conn->shutdown_poll); + + if(!mresult) /* Remember for next time */ + memcpy(&conn->shutdown_poll, &ps, sizeof(ps)); + return mresult; +} + +void Curl_conncache_multi_socket(struct Curl_multi *multi, + curl_socket_t s, int ev_bitmask) +{ + struct conncache *connc = &multi->conn_cache; + struct Curl_easy *data = connc->closure_handle; + struct Curl_llist_element *e = connc->shutdowns.conn_list.head; + struct connectdata *conn; + bool done; + + (void)ev_bitmask; + DEBUGASSERT(multi->socket_cb); + if(!e) + return; + + connc->shutdowns.iter_locked = TRUE; + while(e) { + conn = e->ptr; + if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) { + Curl_attach_connection(data, conn); + connc_run_conn_shutdown(data, conn, &done); + DEBUGF(infof(data, "[CCACHE] shutdown #%" CURL_FORMAT_CURL_OFF_T + ", done=%d", conn->connection_id, done)); + Curl_detach_connection(data); + if(done || connc_update_shutdown_ev(multi, data, conn)) { + Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL); + connc_disconnect(NULL, conn, connc, FALSE); + } + break; + } + e = e->next; + } + connc->shutdowns.iter_locked = FALSE; +} + +void Curl_conncache_multi_close_all(struct Curl_multi *multi) +{ + connc_close_all(&multi->conn_cache); +} + + +#define NUM_POLLS_ON_STACK 10 + +static CURLcode connc_shutdown_wait(struct conncache *connc, int timeout_ms) +{ + struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; + struct curl_pollfds cpfds; + CURLcode result; + + Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK); + + result = Curl_conncache_add_pollfds(connc, &cpfds); + if(result) + goto out; + + Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000)); + +out: + Curl_pollfds_cleanup(&cpfds); + return result; +} + +static void connc_shutdown_all(struct conncache *connc, int timeout_ms) +{ + struct Curl_easy *data = connc->closure_handle; + struct connectdata *conn; + struct curltime started = Curl_now(); + + if(!data) + return; + (void)data; + + DEBUGF(infof(data, "conncache shutdown all")); + + /* Move all connections into the shutdown queue */ + conn = connc_find_first_connection(connc); + while(conn) { + /* This will remove the connection from the cache */ + DEBUGF(infof(data, "moving connection %" CURL_FORMAT_CURL_OFF_T + " to shutdown queue", conn->connection_id)); + connc_remove_conn(connc, conn); + connc_discard_conn(connc, NULL, conn, FALSE); + conn = connc_find_first_connection(connc); + } + + DEBUGASSERT(!connc->shutdowns.iter_locked); + while(connc->shutdowns.conn_list.head) { + timediff_t timespent; + int remain_ms; + + connc_perform(connc); + + if(!connc->shutdowns.conn_list.head) { + DEBUGF(infof(data, "conncache shutdown ok")); + break; + } + + /* wait for activity, timeout or "nothing" */ + timespent = Curl_timediff(Curl_now(), started); + if(timespent >= (timediff_t)timeout_ms) { + DEBUGF(infof(data, "conncache shutdown %s", + (timeout_ms > 0)? "timeout" : "best effort done")); + break; + } + + remain_ms = timeout_ms - (int)timespent; + if(connc_shutdown_wait(connc, remain_ms)) { + DEBUGF(infof(data, "conncache shutdown all, abort")); + break; + } + } + + /* Due to errors/timeout, we might come here without being full ydone. */ + connc_shutdown_discard_all(connc); +} + +#if 0 +/* Useful for debugging the connection cache */ +void Curl_conncache_print(struct conncache *connc) +{ + struct Curl_hash_iterator iter; + struct Curl_llist_element *curr; + struct Curl_hash_element *he; + + if(!connc) + return; + + fprintf(stderr, "=Bundle cache=\n"); + + Curl_hash_start_iterate(connc->hash, &iter); + + he = Curl_hash_next_element(&iter); + while(he) { + struct connectbundle *bundle; + struct connectdata *conn; + + bundle = he->ptr; + + fprintf(stderr, "%s -", he->key); + curr = bundle->conn_list->head; + while(curr) { + conn = curr->ptr; + + fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse); + curr = curr->next; + } + fprintf(stderr, "\n"); + + he = Curl_hash_next_element(&iter); + } +} +#endif diff --git a/third_party/curl/lib/conncache.h b/third_party/curl/lib/conncache.h new file mode 100644 index 0000000000..30cc2e2599 --- /dev/null +++ b/third_party/curl/lib/conncache.h @@ -0,0 +1,164 @@ +#ifndef HEADER_CURL_CONNCACHE_H +#define HEADER_CURL_CONNCACHE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Linus Nielsen Feltzing, + * + * 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 + * + ***************************************************************************/ + +/* + * All accesses to struct fields and changing of data in the connection cache + * and connectbundles must be done with the conncache LOCKED. The cache might + * be shared. + */ + +#include +#include "timeval.h" + +struct connectdata; +struct curl_pollfds; +struct curl_waitfds; +struct Curl_multi; + +struct connshutdowns { + struct Curl_llist conn_list; /* The connectdata to shut down */ + BIT(iter_locked); /* TRUE while iterating the list */ +}; + +struct conncache { + struct Curl_hash hash; + size_t num_conn; + curl_off_t next_connection_id; + curl_off_t next_easy_id; + struct curltime last_cleanup; + struct connshutdowns shutdowns; + /* handle used for closing cached connections */ + struct Curl_easy *closure_handle; + struct Curl_multi *multi; /* Optional, set if cache belongs to multi */ +}; + +#define BUNDLE_NO_MULTIUSE -1 +#define BUNDLE_UNKNOWN 0 /* initial value */ +#define BUNDLE_MULTIPLEX 2 + +#ifdef DEBUGBUILD +/* the debug versions of these macros make extra certain that the lock is + never doubly locked or unlocked */ +#define CONNCACHE_LOCK(x) \ + do { \ + if((x)->share) { \ + Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, \ + CURL_LOCK_ACCESS_SINGLE); \ + DEBUGASSERT(!(x)->state.conncache_lock); \ + (x)->state.conncache_lock = TRUE; \ + } \ + } while(0) + +#define CONNCACHE_UNLOCK(x) \ + do { \ + if((x)->share) { \ + DEBUGASSERT((x)->state.conncache_lock); \ + (x)->state.conncache_lock = FALSE; \ + Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT); \ + } \ + } while(0) +#else +#define CONNCACHE_LOCK(x) if((x)->share) \ + Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE) +#define CONNCACHE_UNLOCK(x) if((x)->share) \ + Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT) +#endif + +struct connectbundle { + int multiuse; /* supports multi-use */ + size_t num_connections; /* Number of connections in the bundle */ + struct Curl_llist conn_list; /* The connectdata members of the bundle */ +}; + +/* Init the cache, pass multi only if cache is owned by it. + * returns 1 on error, 0 is fine. + */ +int Curl_conncache_init(struct conncache *, + struct Curl_multi *multi, + size_t size); +void Curl_conncache_destroy(struct conncache *connc); + +/* return the correct bundle, to a host or a proxy */ +struct connectbundle *Curl_conncache_find_bundle(struct Curl_easy *data, + struct connectdata *conn, + struct conncache *connc); +/* returns number of connections currently held in the connection cache */ +size_t Curl_conncache_size(struct Curl_easy *data); + +bool Curl_conncache_return_conn(struct Curl_easy *data, + struct connectdata *conn); +CURLcode Curl_conncache_add_conn(struct Curl_easy *data) WARN_UNUSED_RESULT; +void Curl_conncache_remove_conn(struct Curl_easy *data, + struct connectdata *conn, + bool lock); +bool Curl_conncache_foreach(struct Curl_easy *data, + struct conncache *connc, + void *param, + int (*func)(struct Curl_easy *data, + struct connectdata *conn, + void *param)); + +struct connectdata * +Curl_conncache_find_first_connection(struct conncache *connc); + +struct connectdata * +Curl_conncache_extract_bundle(struct Curl_easy *data, + struct connectbundle *bundle); +struct connectdata * +Curl_conncache_extract_oldest(struct Curl_easy *data); +void Curl_conncache_close_all_connections(struct conncache *connc); +void Curl_conncache_print(struct conncache *connc); + +/** + * Tear down the connection. If `aborted` is FALSE, the connection + * will be shut down first before discarding. If the shutdown + * is not immediately complete, the connection + * will be placed into the cache is shutdown queue. + */ +void Curl_conncache_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool aborted); + +/** + * Add sockets and POLLIN/OUT flags for connections handled by the cache. + */ +CURLcode Curl_conncache_add_pollfds(struct conncache *connc, + struct curl_pollfds *cpfds); +CURLcode Curl_conncache_add_waitfds(struct conncache *connc, + struct curl_waitfds *cwfds); + +/** + * Perform maintenance on connections in the cache. Specifically, + * progress the shutdown of connections in the queue. + */ +void Curl_conncache_multi_perform(struct Curl_multi *multi); + +void Curl_conncache_multi_socket(struct Curl_multi *multi, + curl_socket_t s, int ev_bitmask); +void Curl_conncache_multi_close_all(struct Curl_multi *multi); + +#endif /* HEADER_CURL_CONNCACHE_H */ diff --git a/third_party/curl/lib/connect.c b/third_party/curl/lib/connect.c new file mode 100644 index 0000000000..9b68f15da5 --- /dev/null +++ b/third_party/curl/lib/connect.c @@ -0,0 +1,1555 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include /* may need it */ +#endif +#ifdef HAVE_SYS_UN_H +#include /* for sockaddr_un */ +#endif +#ifdef HAVE_LINUX_TCP_H +#include +#elif defined(HAVE_NETINET_TCP_H) +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#ifdef __VMS +#include +#include +#endif + +#include "urldata.h" +#include "sendf.h" +#include "if2ip.h" +#include "strerror.h" +#include "cfilters.h" +#include "connect.h" +#include "cf-haproxy.h" +#include "cf-https-connect.h" +#include "cf-socket.h" +#include "select.h" +#include "url.h" /* for Curl_safefree() */ +#include "multiif.h" +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "inet_ntop.h" +#include "inet_pton.h" +#include "vtls/vtls.h" /* for vtsl cfilters */ +#include "progress.h" +#include "warnless.h" +#include "conncache.h" +#include "multihandle.h" +#include "share.h" +#include "version_win32.h" +#include "vquic/vquic.h" /* for quic cfilters */ +#include "http_proxy.h" +#include "socks.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +/* + * Curl_timeleft() returns the amount of milliseconds left allowed for the + * transfer/connection. If the value is 0, there is no timeout (ie there is + * infinite time left). If the value is negative, the timeout time has already + * elapsed. + * @param data the transfer to check on + * @param nowp timestamp to use for calculation, NULL to use Curl_now() + * @param duringconnect TRUE iff connect timeout is also taken into account. + * @unittest: 1303 + */ +timediff_t Curl_timeleft(struct Curl_easy *data, + struct curltime *nowp, + bool duringconnect) +{ + timediff_t timeleft_ms = 0; + timediff_t ctimeleft_ms = 0; + struct curltime now; + + /* The duration of a connect and the total transfer are calculated from two + different time-stamps. It can end up with the total timeout being reached + before the connect timeout expires and we must acknowledge whichever + timeout that is reached first. The total timeout is set per entire + operation, while the connect timeout is set per connect. */ + if(data->set.timeout <= 0 && !duringconnect) + return 0; /* no timeout in place or checked, return "no limit" */ + + if(!nowp) { + now = Curl_now(); + nowp = &now; + } + + if(data->set.timeout > 0) { + timeleft_ms = data->set.timeout - + Curl_timediff(*nowp, data->progress.t_startop); + if(!timeleft_ms) + timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ + if(!duringconnect) + return timeleft_ms; /* no connect check, this is it */ + } + + if(duringconnect) { + timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ? + data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT; + ctimeleft_ms = ctimeout_ms - + Curl_timediff(*nowp, data->progress.t_startsingle); + if(!ctimeleft_ms) + ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ + if(!timeleft_ms) + return ctimeleft_ms; /* no general timeout, this is it */ + } + /* return minimal time left or max amount already expired */ + return (ctimeleft_ms < timeleft_ms)? ctimeleft_ms : timeleft_ms; +} + +void Curl_shutdown_start(struct Curl_easy *data, int sockindex, + struct curltime *nowp) +{ + struct curltime now; + + DEBUGASSERT(data->conn); + if(!nowp) { + now = Curl_now(); + nowp = &now; + } + data->conn->shutdown.start[sockindex] = *nowp; + data->conn->shutdown.timeout_ms = (data->set.shutdowntimeout > 0) ? + data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS; +} + +timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex, + struct curltime *nowp) +{ + struct curltime now; + timediff_t left_ms; + + if(!conn->shutdown.start[sockindex].tv_sec || !conn->shutdown.timeout_ms) + return 0; /* not started or no limits */ + + if(!nowp) { + now = Curl_now(); + nowp = &now; + } + left_ms = conn->shutdown.timeout_ms - + Curl_timediff(*nowp, conn->shutdown.start[sockindex]); + return left_ms? left_ms : -1; +} + +timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn, + struct curltime *nowp) +{ + timediff_t left_ms = 0, ms; + struct curltime now; + int i; + + for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) { + if(!conn->shutdown.start[i].tv_sec) + continue; + if(!nowp) { + now = Curl_now(); + nowp = &now; + } + ms = Curl_shutdown_timeleft(conn, i, nowp); + if(ms && (!left_ms || ms < left_ms)) + left_ms = ms; + } + return left_ms; +} + +void Curl_shutdown_clear(struct Curl_easy *data, int sockindex) +{ + struct curltime *pt = &data->conn->shutdown.start[sockindex]; + memset(pt, 0, sizeof(*pt)); +} + +bool Curl_shutdown_started(struct Curl_easy *data, int sockindex) +{ + struct curltime *pt = &data->conn->shutdown.start[sockindex]; + return (pt->tv_sec > 0) || (pt->tv_usec > 0); +} + +/* Copies connection info into the transfer handle to make it available when + the transfer handle is no longer associated with the connection. */ +void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, + struct ip_quadruple *ip) +{ + if(ip) + data->info.primary = *ip; + else { + memset(&data->info.primary, 0, sizeof(data->info.primary)); + data->info.primary.remote_port = -1; + data->info.primary.local_port = -1; + } + data->info.conn_scheme = conn->handler->scheme; + /* conn_protocol can only provide "old" protocols */ + data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; + data->info.conn_remote_port = conn->remote_port; + data->info.used_proxy = +#ifdef CURL_DISABLE_PROXY + 0 +#else + conn->bits.proxy +#endif + ; +} + +static const struct Curl_addrinfo * +addr_first_match(const struct Curl_addrinfo *addr, int family) +{ + while(addr) { + if(addr->ai_family == family) + return addr; + addr = addr->ai_next; + } + return NULL; +} + +static const struct Curl_addrinfo * +addr_next_match(const struct Curl_addrinfo *addr, int family) +{ + while(addr && addr->ai_next) { + addr = addr->ai_next; + if(addr->ai_family == family) + return addr; + } + return NULL; +} + +/* retrieves ip address and port from a sockaddr structure. + note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */ +bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, int *port) +{ + struct sockaddr_in *si = NULL; +#ifdef USE_IPV6 + struct sockaddr_in6 *si6 = NULL; +#endif +#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX) + struct sockaddr_un *su = NULL; +#else + (void)salen; +#endif + + switch(sa->sa_family) { + case AF_INET: + si = (struct sockaddr_in *)(void *) sa; + if(Curl_inet_ntop(sa->sa_family, &si->sin_addr, + addr, MAX_IPADR_LEN)) { + unsigned short us_port = ntohs(si->sin_port); + *port = us_port; + return TRUE; + } + break; +#ifdef USE_IPV6 + case AF_INET6: + si6 = (struct sockaddr_in6 *)(void *) sa; + if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr, + addr, MAX_IPADR_LEN)) { + unsigned short us_port = ntohs(si6->sin6_port); + *port = us_port; + return TRUE; + } + break; +#endif +#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX) + case AF_UNIX: + if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) { + su = (struct sockaddr_un*)sa; + msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); + } + else + addr[0] = 0; /* socket with no name */ + *port = 0; + return TRUE; +#endif + default: + break; + } + + addr[0] = '\0'; + *port = 0; + errno = EAFNOSUPPORT; + return FALSE; +} + +struct connfind { + curl_off_t id_tofind; + struct connectdata *found; +}; + +static int conn_is_conn(struct Curl_easy *data, + struct connectdata *conn, void *param) +{ + struct connfind *f = (struct connfind *)param; + (void)data; + if(conn->connection_id == f->id_tofind) { + f->found = conn; + return 1; + } + return 0; +} + +/* + * Used to extract socket and connectdata struct for the most recent + * transfer on the given Curl_easy. + * + * The returned socket will be CURL_SOCKET_BAD in case of failure! + */ +curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, + struct connectdata **connp) +{ + DEBUGASSERT(data); + + /* this works for an easy handle: + * - that has been used for curl_easy_perform() + * - that is associated with a multi handle, and whose connection + * was detached with CURLOPT_CONNECT_ONLY + */ + if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) { + struct connectdata *c; + struct connfind find; + find.id_tofind = data->state.lastconnect_id; + find.found = NULL; + + Curl_conncache_foreach(data, + data->share && (data->share->specifier + & (1<< CURL_LOCK_DATA_CONNECT))? + &data->share->conn_cache: + data->multi_easy? + &data->multi_easy->conn_cache: + &data->multi->conn_cache, &find, conn_is_conn); + + if(!find.found) { + data->state.lastconnect_id = -1; + return CURL_SOCKET_BAD; + } + + c = find.found; + if(connp) + /* only store this if the caller cares for it */ + *connp = c; + return c->sock[FIRSTSOCKET]; + } + return CURL_SOCKET_BAD; +} + +/* + * Curl_conncontrol() marks streams or connection for closure. + */ +void Curl_conncontrol(struct connectdata *conn, + int ctrl /* see defines in header */ +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + , const char *reason +#endif + ) +{ + /* close if a connection, or a stream that is not multiplexed. */ + /* This function will be called both before and after this connection is + associated with a transfer. */ + bool closeit, is_multiplex; + DEBUGASSERT(conn); +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + (void)reason; /* useful for debugging */ +#endif + is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET); + closeit = (ctrl == CONNCTRL_CONNECTION) || + ((ctrl == CONNCTRL_STREAM) && !is_multiplex); + if((ctrl == CONNCTRL_STREAM) && is_multiplex) + ; /* stream signal on multiplex conn never affects close state */ + else if((bit)closeit != conn->bits.close) { + conn->bits.close = closeit; /* the only place in the source code that + should assign this bit */ + } +} + +/** + * job walking the matching addr infos, creating a sub-cfilter with the + * provided method `cf_create` and running setup/connect on it. + */ +struct eyeballer { + const char *name; + const struct Curl_addrinfo *first; /* complete address list, not owned */ + const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */ + int ai_family; /* matching address family only */ + cf_ip_connect_create *cf_create; /* for creating cf */ + struct Curl_cfilter *cf; /* current sub-cfilter connecting */ + struct eyeballer *primary; /* eyeballer this one is backup for */ + timediff_t delay_ms; /* delay until start */ + struct curltime started; /* start of current attempt */ + timediff_t timeoutms; /* timeout for current attempt */ + expire_id timeout_id; /* ID for Curl_expire() */ + CURLcode result; + int error; + BIT(rewinded); /* if we rewinded the addr list */ + BIT(has_started); /* attempts have started */ + BIT(is_done); /* out of addresses/time */ + BIT(connected); /* cf has connected */ + BIT(shutdown); /* cf has shutdown */ + BIT(inconclusive); /* connect was not a hard failure, we + * might talk to a restarting server */ +}; + + +typedef enum { + SCFST_INIT, + SCFST_WAITING, + SCFST_DONE +} cf_connect_state; + +struct cf_he_ctx { + int transport; + cf_ip_connect_create *cf_create; + const struct Curl_dns_entry *remotehost; + cf_connect_state state; + struct eyeballer *baller[2]; + struct eyeballer *winner; + struct curltime started; +}; + +/* when there are more than one IP address left to use, this macro returns how + much of the given timeout to spend on *this* attempt */ +#define TIMEOUT_LARGE 600 +#define USETIME(ms) ((ms > TIMEOUT_LARGE) ? (ms / 2) : ms) + +static CURLcode eyeballer_new(struct eyeballer **pballer, + cf_ip_connect_create *cf_create, + const struct Curl_addrinfo *addr, + int ai_family, + struct eyeballer *primary, + timediff_t delay_ms, + timediff_t timeout_ms, + expire_id timeout_id) +{ + struct eyeballer *baller; + + *pballer = NULL; + baller = calloc(1, sizeof(*baller)); + if(!baller) + return CURLE_OUT_OF_MEMORY; + + baller->name = ((ai_family == AF_INET)? "ipv4" : ( +#ifdef USE_IPV6 + (ai_family == AF_INET6)? "ipv6" : +#endif + "ip")); + baller->cf_create = cf_create; + baller->first = baller->addr = addr; + baller->ai_family = ai_family; + baller->primary = primary; + baller->delay_ms = delay_ms; + baller->timeoutms = addr_next_match(baller->addr, baller->ai_family)? + USETIME(timeout_ms) : timeout_ms; + baller->timeout_id = timeout_id; + baller->result = CURLE_COULDNT_CONNECT; + + *pballer = baller; + return CURLE_OK; +} + +static void baller_close(struct eyeballer *baller, + struct Curl_easy *data) +{ + if(baller && baller->cf) { + Curl_conn_cf_discard_chain(&baller->cf, data); + } +} + +static void baller_free(struct eyeballer *baller, + struct Curl_easy *data) +{ + if(baller) { + baller_close(baller, data); + free(baller); + } +} + +static void baller_rewind(struct eyeballer *baller) +{ + baller->rewinded = TRUE; + baller->addr = baller->first; + baller->inconclusive = FALSE; +} + +static void baller_next_addr(struct eyeballer *baller) +{ + baller->addr = addr_next_match(baller->addr, baller->ai_family); +} + +/* + * Initiate a connect attempt walk. + * + * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to + * CURL_SOCKET_BAD. Other errors will however return proper errors. + */ +static void baller_initiate(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct eyeballer *baller) +{ + struct cf_he_ctx *ctx = cf->ctx; + struct Curl_cfilter *cf_prev = baller->cf; + struct Curl_cfilter *wcf; + CURLcode result; + + + /* Do not close a previous cfilter yet to ensure that the next IP's + socket gets a different file descriptor, which can prevent bugs when + the curl_multi_socket_action interface is used with certain select() + replacements such as kqueue. */ + result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr, + ctx->transport); + if(result) + goto out; + + /* the new filter might have sub-filters */ + for(wcf = baller->cf; wcf; wcf = wcf->next) { + wcf->conn = cf->conn; + wcf->sockindex = cf->sockindex; + } + + if(addr_next_match(baller->addr, baller->ai_family)) { + Curl_expire(data, baller->timeoutms, baller->timeout_id); + } + +out: + if(result) { + CURL_TRC_CF(data, cf, "%s failed", baller->name); + baller_close(baller, data); + } + if(cf_prev) + Curl_conn_cf_discard_chain(&cf_prev, data); + baller->result = result; +} + +/** + * Start a connection attempt on the current baller address. + * Will return CURLE_OK on the first address where a socket + * could be created and the non-blocking connect started. + * Returns error when all remaining addresses have been tried. + */ +static CURLcode baller_start(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct eyeballer *baller, + timediff_t timeoutms) +{ + baller->error = 0; + baller->connected = FALSE; + baller->has_started = TRUE; + + while(baller->addr) { + baller->started = Curl_now(); + baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ? + USETIME(timeoutms) : timeoutms; + baller_initiate(cf, data, baller); + if(!baller->result) + break; + baller_next_addr(baller); + } + if(!baller->addr) { + baller->is_done = TRUE; + } + return baller->result; +} + + +/* Used within the multi interface. Try next IP address, returns error if no + more address exists or error */ +static CURLcode baller_start_next(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct eyeballer *baller, + timediff_t timeoutms) +{ + if(cf->sockindex == FIRSTSOCKET) { + baller_next_addr(baller); + /* If we get inconclusive answers from the server(s), we make + * a second iteration over the address list */ + if(!baller->addr && baller->inconclusive && !baller->rewinded) + baller_rewind(baller); + baller_start(cf, data, baller, timeoutms); + } + else { + baller->error = 0; + baller->connected = FALSE; + baller->has_started = TRUE; + baller->is_done = TRUE; + baller->result = CURLE_COULDNT_CONNECT; + } + return baller->result; +} + +static CURLcode baller_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct eyeballer *baller, + struct curltime *now, + bool *connected) +{ + (void)cf; + *connected = baller->connected; + if(!baller->result && !*connected) { + /* evaluate again */ + baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected); + + if(!baller->result) { + if(*connected) { + baller->connected = TRUE; + baller->is_done = TRUE; + } + else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) { + infof(data, "%s connect timeout after %" CURL_FORMAT_TIMEDIFF_T + "ms, move on!", baller->name, baller->timeoutms); +#if defined(ETIMEDOUT) + baller->error = ETIMEDOUT; +#endif + baller->result = CURLE_OPERATION_TIMEDOUT; + } + } + else if(baller->result == CURLE_WEIRD_SERVER_REPLY) + baller->inconclusive = TRUE; + } + return baller->result; +} + +/* + * is_connected() checks if the socket has connected. + */ +static CURLcode is_connected(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *connected) +{ + struct cf_he_ctx *ctx = cf->ctx; + struct connectdata *conn = cf->conn; + CURLcode result; + struct curltime now; + size_t i; + int ongoing, not_started; + const char *hostname; + + /* Check if any of the conn->tempsock we use for establishing connections + * succeeded and, if so, close any ongoing other ones. + * Transfer the successful conn->tempsock to conn->sock[sockindex] + * and set conn->tempsock to CURL_SOCKET_BAD. + * If transport is QUIC, we need to shutdown the ongoing 'other' + * cot ballers in a QUIC appropriate way. */ +evaluate: + *connected = FALSE; /* a very negative world view is best */ + now = Curl_now(); + ongoing = not_started = 0; + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + + if(!baller || baller->is_done) + continue; + + if(!baller->has_started) { + ++not_started; + continue; + } + baller->result = baller_connect(cf, data, baller, &now, connected); + CURL_TRC_CF(data, cf, "%s connect -> %d, connected=%d", + baller->name, baller->result, *connected); + + if(!baller->result) { + if(*connected) { + /* connected, declare the winner */ + ctx->winner = baller; + ctx->baller[i] = NULL; + break; + } + else { /* still waiting */ + ++ongoing; + } + } + else if(!baller->is_done) { + /* The baller failed to connect, start its next attempt */ + if(baller->error) { + data->state.os_errno = baller->error; + SET_SOCKERRNO(baller->error); + } + baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE)); + if(baller->is_done) { + CURL_TRC_CF(data, cf, "%s done", baller->name); + } + else { + /* next attempt was started */ + CURL_TRC_CF(data, cf, "%s trying next", baller->name); + ++ongoing; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + } + } + + if(ctx->winner) { + *connected = TRUE; + return CURLE_OK; + } + + /* Nothing connected, check the time before we might + * start new ballers or return ok. */ + if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) { + failf(data, "Connection timeout after %" CURL_FORMAT_CURL_OFF_T " ms", + Curl_timediff(now, data->progress.t_startsingle)); + return CURLE_OPERATION_TIMEDOUT; + } + + /* Check if we have any waiting ballers to start now. */ + if(not_started > 0) { + int added = 0; + + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + + if(!baller || baller->has_started) + continue; + /* We start its primary baller has failed to connect or if + * its start delay_ms have expired */ + if((baller->primary && baller->primary->is_done) || + Curl_timediff(now, ctx->started) >= baller->delay_ms) { + baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE)); + if(baller->is_done) { + CURL_TRC_CF(data, cf, "%s done", baller->name); + } + else { + CURL_TRC_CF(data, cf, "%s starting (timeout=%" + CURL_FORMAT_TIMEDIFF_T "ms)", + baller->name, baller->timeoutms); + ++ongoing; + ++added; + } + } + } + if(added > 0) + goto evaluate; + } + + if(ongoing > 0) { + /* We are still trying, return for more waiting */ + *connected = FALSE; + return CURLE_OK; + } + + /* all ballers have failed to connect. */ + CURL_TRC_CF(data, cf, "all eyeballers failed"); + result = CURLE_COULDNT_CONNECT; + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + if(!baller) + continue; + CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d", + baller->name, baller->has_started, baller->result); + if(baller->has_started && baller->result) { + result = baller->result; + break; + } + } + +#ifndef CURL_DISABLE_PROXY + if(conn->bits.socksproxy) + hostname = conn->socks_proxy.host.name; + else if(conn->bits.httpproxy) + hostname = conn->http_proxy.host.name; + else +#endif + if(conn->bits.conn_to_host) + hostname = conn->conn_to_host.name; + else + hostname = conn->host.name; + + failf(data, "Failed to connect to %s port %u after " + "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", + hostname, conn->primary.remote_port, + Curl_timediff(now, data->progress.t_startsingle), + curl_easy_strerror(result)); + +#ifdef WSAETIMEDOUT + if(WSAETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; +#elif defined(ETIMEDOUT) + if(ETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; +#endif + + return result; +} + +/* + * Connect to the given host with timeout, proxy or remote does not matter. + * There might be more than one IP address to try out. + */ +static CURLcode start_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost) +{ + struct cf_he_ctx *ctx = cf->ctx; + struct connectdata *conn = cf->conn; + CURLcode result = CURLE_COULDNT_CONNECT; + int ai_family0, ai_family1; + timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + const struct Curl_addrinfo *addr0, *addr1; + + if(timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "Connection time-out"); + return CURLE_OPERATION_TIMEDOUT; + } + + ctx->started = Curl_now(); + + /* remotehost->addr is the list of addresses from the resolver, each + * with an address family. The list has at least one entry, possibly + * many more. + * We try at most 2 at a time, until we either get a connection or + * run out of addresses to try. Since likelihood of success is tied + * to the address family (e.g. IPV6 might not work at all ), we want + * the 2 connect attempt ballers to try different families, if possible. + * + */ + if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) { + /* any IP version is allowed */ + ai_family0 = remotehost->addr? + remotehost->addr->ai_family : 0; +#ifdef USE_IPV6 + ai_family1 = ai_family0 == AF_INET6 ? + AF_INET : AF_INET6; +#else + ai_family1 = AF_UNSPEC; +#endif + } + else { + /* only one IP version is allowed */ + ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ? + AF_INET : +#ifdef USE_IPV6 + AF_INET6; +#else + AF_UNSPEC; +#endif + ai_family1 = AF_UNSPEC; + } + + /* Get the first address in the list that matches the family, + * this might give NULL, if we do not have any matches. */ + addr0 = addr_first_match(remotehost->addr, ai_family0); + addr1 = addr_first_match(remotehost->addr, ai_family1); + if(!addr0 && addr1) { + /* switch around, so a single baller always uses addr0 */ + addr0 = addr1; + ai_family0 = ai_family1; + addr1 = NULL; + } + + /* We found no address that matches our criteria, we cannot connect */ + if(!addr0) { + return CURLE_COULDNT_CONNECT; + } + + memset(ctx->baller, 0, sizeof(ctx->baller)); + result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0, + NULL, 0, /* no primary/delay, start now */ + timeout_ms, EXPIRE_DNS_PER_NAME); + if(result) + return result; + CURL_TRC_CF(data, cf, "created %s (timeout %" + CURL_FORMAT_TIMEDIFF_T "ms)", + ctx->baller[0]->name, ctx->baller[0]->timeoutms); + if(addr1) { + /* second one gets a delayed start */ + result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1, + ctx->baller[0], /* wait on that to fail */ + /* or start this delayed */ + data->set.happy_eyeballs_timeout, + timeout_ms, EXPIRE_DNS_PER_NAME2); + if(result) + return result; + CURL_TRC_CF(data, cf, "created %s (timeout %" + CURL_FORMAT_TIMEDIFF_T "ms)", + ctx->baller[1]->name, ctx->baller[1]->timeoutms); + Curl_expire(data, data->set.happy_eyeballs_timeout, + EXPIRE_HAPPY_EYEBALLS); + } + + return CURLE_OK; +} + +static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_he_ctx *ctx = cf->ctx; + size_t i; + + DEBUGASSERT(ctx); + DEBUGASSERT(data); + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + baller_free(ctx->baller[i], data); + ctx->baller[i] = NULL; + } + baller_free(ctx->winner, data); + ctx->winner = NULL; +} + +static CURLcode cf_he_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_he_ctx *ctx = cf->ctx; + size_t i; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* shutdown all ballers that have not done so already. If one fails, + * continue shutting down others until all are shutdown. */ + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + bool bdone = FALSE; + if(!baller || !baller->cf || baller->shutdown) + continue; + baller->result = baller->cf->cft->do_shutdown(baller->cf, data, &bdone); + if(baller->result || bdone) + baller->shutdown = TRUE; /* treat a failed shutdown as done */ + } + + *done = TRUE; + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + if(ctx->baller[i] && !ctx->baller[i]->shutdown) + *done = FALSE; + } + if(*done) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + if(ctx->baller[i] && ctx->baller[i]->result) + result = ctx->baller[i]->result; + } + } + CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); + return result; +} + +static void cf_he_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_he_ctx *ctx = cf->ctx; + size_t i; + + if(!cf->connected) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + if(!baller || !baller->cf) + continue; + Curl_conn_cf_adjust_pollset(baller->cf, data, ps); + } + CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); + } +} + +static CURLcode cf_he_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_he_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + (void)blocking; /* TODO: do we want to support this? */ + DEBUGASSERT(ctx); + *done = FALSE; + + switch(ctx->state) { + case SCFST_INIT: + DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data)); + DEBUGASSERT(!cf->connected); + result = start_connect(cf, data, ctx->remotehost); + if(result) + return result; + ctx->state = SCFST_WAITING; + FALLTHROUGH(); + case SCFST_WAITING: + result = is_connected(cf, data, done); + if(!result && *done) { + DEBUGASSERT(ctx->winner); + DEBUGASSERT(ctx->winner->cf); + DEBUGASSERT(ctx->winner->cf->connected); + /* we have a winner. Install and activate it. + * close/free all others. */ + ctx->state = SCFST_DONE; + cf->connected = TRUE; + cf->next = ctx->winner->cf; + ctx->winner->cf = NULL; + cf_he_ctx_clear(cf, data); + Curl_conn_cf_cntrl(cf->next, data, TRUE, + CF_CTRL_CONN_INFO_UPDATE, 0, NULL); + + if(cf->conn->handler->protocol & PROTO_FAMILY_SSH) + Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */ + Curl_verboseconnect(data, cf->conn, cf->sockindex); + data->info.numconnects++; /* to track the # of connections made */ + } + break; + case SCFST_DONE: + *done = TRUE; + break; + } + return result; +} + +static void cf_he_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_he_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "close"); + cf_he_ctx_clear(cf, data); + cf->connected = FALSE; + ctx->state = SCFST_INIT; + + if(cf->next) { + cf->next->cft->do_close(cf->next, data); + Curl_conn_cf_discard_chain(&cf->next, data); + } +} + +static bool cf_he_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_he_ctx *ctx = cf->ctx; + size_t i; + + if(cf->connected) + return cf->next->cft->has_data_pending(cf->next, data); + + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + if(!baller || !baller->cf) + continue; + if(baller->cf->cft->has_data_pending(baller->cf, data)) + return TRUE; + } + return FALSE; +} + +static struct curltime get_max_baller_time(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query) +{ + struct cf_he_ctx *ctx = cf->ctx; + struct curltime t, tmax; + size_t i; + + memset(&tmax, 0, sizeof(tmax)); + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + + memset(&t, 0, sizeof(t)); + if(baller && baller->cf && + !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) { + if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) + tmax = t; + } + } + return tmax; +} + +static CURLcode cf_he_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_he_ctx *ctx = cf->ctx; + + if(!cf->connected) { + switch(query) { + case CF_QUERY_CONNECT_REPLY_MS: { + int reply_ms = -1; + size_t i; + + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + int breply_ms; + + if(baller && baller->cf && + !baller->cf->cft->query(baller->cf, data, query, + &breply_ms, NULL)) { + if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms)) + reply_ms = breply_ms; + } + } + *pres1 = reply_ms; + CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1); + return CURLE_OK; + } + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); + return CURLE_OK; + } + default: + break; + } + } + + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_he_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "destroy"); + if(ctx) { + cf_he_ctx_clear(cf, data); + } + /* release any resources held in state */ + Curl_safefree(ctx); +} + +struct Curl_cftype Curl_cft_happy_eyeballs = { + "HAPPY-EYEBALLS", + 0, + CURL_LOG_LVL_NONE, + cf_he_destroy, + cf_he_connect, + cf_he_close, + cf_he_shutdown, + Curl_cf_def_get_host, + cf_he_adjust_pollset, + cf_he_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_he_query, +}; + +/** + * Create a happy eyeball connection filter that uses the, once resolved, + * address information to connect on ip families based on connection + * configuration. + * @param pcf output, the created cfilter + * @param data easy handle used in creation + * @param conn connection the filter is created for + * @param cf_create method to create the sub-filters performing the + * actual connects. + */ +static CURLcode +cf_happy_eyeballs_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + cf_ip_connect_create *cf_create, + const struct Curl_dns_entry *remotehost, + int transport) +{ + struct cf_he_ctx *ctx = NULL; + CURLcode result; + + (void)data; + (void)conn; + *pcf = NULL; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->transport = transport; + ctx->cf_create = cf_create; + ctx->remotehost = remotehost; + + result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx); + +out: + if(result) { + Curl_safefree(*pcf); + Curl_safefree(ctx); + } + return result; +} + +struct transport_provider { + int transport; + cf_ip_connect_create *cf_create; +}; + +static +#ifndef UNITTESTS +const +#endif +struct transport_provider transport_providers[] = { + { TRNSPRT_TCP, Curl_cf_tcp_create }, +#ifdef USE_HTTP3 + { TRNSPRT_QUIC, Curl_cf_quic_create }, +#endif +#ifndef CURL_DISABLE_TFTP + { TRNSPRT_UDP, Curl_cf_udp_create }, +#endif +#ifdef USE_UNIX_SOCKETS + { TRNSPRT_UNIX, Curl_cf_unix_create }, +#endif +}; + +static cf_ip_connect_create *get_cf_create(int transport) +{ + size_t i; + for(i = 0; i < ARRAYSIZE(transport_providers); ++i) { + if(transport == transport_providers[i].transport) + return transport_providers[i].cf_create; + } + return NULL; +} + +static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + int transport) +{ + cf_ip_connect_create *cf_create; + struct Curl_cfilter *cf; + CURLcode result; + + /* Need to be first */ + DEBUGASSERT(cf_at); + cf_create = get_cf_create(transport); + if(!cf_create) { + CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport); + return CURLE_UNSUPPORTED_PROTOCOL; + } + result = cf_happy_eyeballs_create(&cf, data, cf_at->conn, + cf_create, remotehost, + transport); + if(result) + return result; + + Curl_conn_cf_insert_after(cf_at, cf); + return CURLE_OK; +} + +typedef enum { + CF_SETUP_INIT, + CF_SETUP_CNNCT_EYEBALLS, + CF_SETUP_CNNCT_SOCKS, + CF_SETUP_CNNCT_HTTP_PROXY, + CF_SETUP_CNNCT_HAPROXY, + CF_SETUP_CNNCT_SSL, + CF_SETUP_DONE +} cf_setup_state; + +struct cf_setup_ctx { + cf_setup_state state; + const struct Curl_dns_entry *remotehost; + int ssl_mode; + int transport; +}; + +static CURLcode cf_setup_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* connect current sub-chain */ +connect_sub_chain: + if(cf->next && !cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + } + + if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { + result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport); + if(result) + return result; + ctx->state = CF_SETUP_CNNCT_EYEBALLS; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + } + + /* sub-chain connected, do we need to add more? */ +#ifndef CURL_DISABLE_PROXY + if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { + result = Curl_cf_socks_proxy_insert_after(cf, data); + if(result) + return result; + ctx->state = CF_SETUP_CNNCT_SOCKS; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + } + + if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { +#ifdef USE_SSL + if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) + && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { + result = Curl_cf_ssl_proxy_insert_after(cf, data); + if(result) + return result; + } +#endif /* USE_SSL */ + +#if !defined(CURL_DISABLE_HTTP) + if(cf->conn->bits.tunnel_proxy) { + result = Curl_cf_http_proxy_insert_after(cf, data); + if(result) + return result; + } +#endif /* !CURL_DISABLE_HTTP */ + ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + } +#endif /* !CURL_DISABLE_PROXY */ + + if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { +#if !defined(CURL_DISABLE_PROXY) + if(data->set.haproxyprotocol) { + if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) { + failf(data, "haproxy protocol not support with SSL " + "encryption in place (QUIC?)"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + result = Curl_cf_haproxy_insert_after(cf, data); + if(result) + return result; + } +#endif /* !CURL_DISABLE_PROXY */ + ctx->state = CF_SETUP_CNNCT_HAPROXY; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + } + + if(ctx->state < CF_SETUP_CNNCT_SSL) { +#ifdef USE_SSL + if((ctx->ssl_mode == CURL_CF_SSL_ENABLE + || (ctx->ssl_mode != CURL_CF_SSL_DISABLE + && cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */ + && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ + result = Curl_cf_ssl_insert_after(cf, data); + if(result) + return result; + } +#endif /* USE_SSL */ + ctx->state = CF_SETUP_CNNCT_SSL; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + } + + ctx->state = CF_SETUP_DONE; + cf->connected = TRUE; + *done = TRUE; + return CURLE_OK; +} + +static void cf_setup_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "close"); + cf->connected = FALSE; + ctx->state = CF_SETUP_INIT; + + if(cf->next) { + cf->next->cft->do_close(cf->next, data); + Curl_conn_cf_discard_chain(&cf->next, data); + } +} + +static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + + (void)data; + CURL_TRC_CF(data, cf, "destroy"); + Curl_safefree(ctx); +} + + +struct Curl_cftype Curl_cft_setup = { + "SETUP", + 0, + CURL_LOG_LVL_NONE, + cf_setup_destroy, + cf_setup_connect, + cf_setup_close, + Curl_cf_def_shutdown, + Curl_cf_def_get_host, + Curl_cf_def_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_setup_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + int transport, + int ssl_mode) +{ + struct Curl_cfilter *cf = NULL; + struct cf_setup_ctx *ctx; + CURLcode result = CURLE_OK; + + (void)data; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->state = CF_SETUP_INIT; + ctx->remotehost = remotehost; + ctx->ssl_mode = ssl_mode; + ctx->transport = transport; + + result = Curl_cf_create(&cf, &Curl_cft_setup, ctx); + if(result) + goto out; + ctx = NULL; + +out: + *pcf = result? NULL : cf; + free(ctx); + return result; +} + +static CURLcode cf_setup_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + int transport, + int ssl_mode) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode); + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +#ifdef UNITTESTS +/* used by unit2600.c */ +void Curl_debug_set_transport_provider(int transport, + cf_ip_connect_create *cf_create) +{ + size_t i; + for(i = 0; i < ARRAYSIZE(transport_providers); ++i) { + if(transport == transport_providers[i].transport) { + transport_providers[i].cf_create = cf_create; + return; + } + } +} +#endif /* UNITTESTS */ + +CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + int transport, + int ssl_mode) +{ + struct Curl_cfilter *cf; + CURLcode result; + + DEBUGASSERT(data); + result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); +out: + return result; +} + +CURLcode Curl_conn_setup(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + int ssl_mode) +{ + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + DEBUGASSERT(conn->handler); + +#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) + if(!conn->cfilter[sockindex] && + conn->handler->protocol == CURLPROTO_HTTPS) { + DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE); + result = Curl_cf_https_setup(data, conn, sockindex, remotehost); + if(result) + goto out; + } +#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ + + /* Still no cfilter set, apply default. */ + if(!conn->cfilter[sockindex]) { + result = cf_setup_add(data, conn, sockindex, remotehost, + conn->transport, ssl_mode); + if(result) + goto out; + } + + DEBUGASSERT(conn->cfilter[sockindex]); +out: + return result; +} diff --git a/third_party/curl/lib/connect.h b/third_party/curl/lib/connect.h new file mode 100644 index 0000000000..e9a4f9c598 --- /dev/null +++ b/third_party/curl/lib/connect.h @@ -0,0 +1,153 @@ +#ifndef HEADER_CURL_CONNECT_H +#define HEADER_CURL_CONNECT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */ +#include "sockaddr.h" +#include "timeval.h" + +struct Curl_dns_entry; +struct ip_quadruple; + +/* generic function that returns how much time there is left to run, according + to the timeouts set */ +timediff_t Curl_timeleft(struct Curl_easy *data, + struct curltime *nowp, + bool duringconnect); + +#define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ + +#define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000) + +void Curl_shutdown_start(struct Curl_easy *data, int sockindex, + struct curltime *nowp); + +/* return how much time there is left to shutdown the connection at + * sockindex. Returns 0 if there is no limit or shutdown has not started. */ +timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex, + struct curltime *nowp); + +/* return how much time there is left to shutdown the connection. + * Returns 0 if there is no limit or shutdown has not started. */ +timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn, + struct curltime *nowp); + +void Curl_shutdown_clear(struct Curl_easy *data, int sockindex); + +/* TRUE iff shutdown has been started */ +bool Curl_shutdown_started(struct Curl_easy *data, int sockindex); + +/* + * Used to extract socket and connectdata struct for the most recent + * transfer on the given Curl_easy. + * + * The returned socket will be CURL_SOCKET_BAD in case of failure! + */ +curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, + struct connectdata **connp); + +bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, int *port); + +void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, + struct ip_quadruple *ip); + +/* + * Curl_conncontrol() marks the end of a connection/stream. The 'closeit' + * argument specifies if it is the end of a connection or a stream. + * + * For stream-based protocols (such as HTTP/2), a stream close will not cause + * a connection close. Other protocols will close the connection for both + * cases. + * + * It sets the bit.close bit to TRUE (with an explanation for debug builds), + * when the connection will close. + */ + +#define CONNCTRL_KEEP 0 /* undo a marked closure */ +#define CONNCTRL_CONNECTION 1 +#define CONNCTRL_STREAM 2 + +void Curl_conncontrol(struct connectdata *conn, + int closeit +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + , const char *reason +#endif + ); + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#define streamclose(x,y) Curl_conncontrol(x, CONNCTRL_STREAM, y) +#define connclose(x,y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y) +#define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP, y) +#else /* if !DEBUGBUILD || CURL_DISABLE_VERBOSE_STRINGS */ +#define streamclose(x,y) Curl_conncontrol(x, CONNCTRL_STREAM) +#define connclose(x,y) Curl_conncontrol(x, CONNCTRL_CONNECTION) +#define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP) +#endif + +/** + * Create a cfilter for making an "ip" connection to the + * given address, using parameters from `conn`. The "ip" connection + * can be a TCP socket, a UDP socket or even a QUIC connection. + * + * It MUST use only the supplied `ai` for its connection attempt. + * + * Such a filter may be used in "happy eyeball" scenarios, and its + * `connect` implementation needs to support non-blocking. Once connected, + * it MAY be installed in the connection filter chain to serve transfers. + */ +typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport); + +CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + int transport, + int ssl_mode); + +/** + * Setup the cfilters at `sockindex` in connection `conn`. + * If no filter chain is installed yet, inspects the configuration + * in `data` and `conn? to install a suitable filter chain. + */ +CURLcode Curl_conn_setup(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + int ssl_mode); + +extern struct Curl_cftype Curl_cft_happy_eyeballs; +extern struct Curl_cftype Curl_cft_setup; + +#ifdef UNITTESTS +void Curl_debug_set_transport_provider(int transport, + cf_ip_connect_create *cf_create); +#endif + +#endif /* HEADER_CURL_CONNECT_H */ diff --git a/third_party/curl/lib/content_encoding.c b/third_party/curl/lib/content_encoding.c new file mode 100644 index 0000000000..4dae41a896 --- /dev/null +++ b/third_party/curl/lib/content_encoding.c @@ -0,0 +1,1086 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "urldata.h" +#include +#include + +#ifdef HAVE_LIBZ +#include +#endif + +#ifdef HAVE_BROTLI +#if defined(__GNUC__) +/* Ignore -Wvla warnings in brotli headers */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wvla" +#endif +#include +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +#endif + +#ifdef HAVE_ZSTD +#include +#endif + +#include "sendf.h" +#include "http.h" +#include "content_encoding.h" +#include "strdup.h" +#include "strcase.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define CONTENT_ENCODING_DEFAULT "identity" + +#ifndef CURL_DISABLE_HTTP + +/* allow no more than 5 "chained" compression steps */ +#define MAX_ENCODE_STACK 5 + +#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ + + +#ifdef HAVE_LIBZ + +/* Comment this out if zlib is always going to be at least ver. 1.2.0.4 + (doing so will reduce code size slightly). */ +#define OLD_ZLIB_SUPPORT 1 + +#define GZIP_MAGIC_0 0x1f +#define GZIP_MAGIC_1 0x8b + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original filename present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef enum { + ZLIB_UNINIT, /* uninitialized */ + ZLIB_INIT, /* initialized */ + ZLIB_INFLATING, /* inflating started. */ + ZLIB_EXTERNAL_TRAILER, /* reading external trailer */ + ZLIB_GZIP_HEADER, /* reading gzip header */ + ZLIB_GZIP_INFLATING, /* inflating gzip stream */ + ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ +} zlibInitState; + +/* Deflate and gzip writer. */ +struct zlib_writer { + struct Curl_cwriter super; + zlibInitState zlib_init; /* zlib init state */ + uInt trailerlen; /* Remaining trailer byte count. */ + z_stream z; /* State structure for zlib. */ +}; + + +static voidpf +zalloc_cb(voidpf opaque, unsigned int items, unsigned int size) +{ + (void) opaque; + /* not a typo, keep it calloc() */ + return (voidpf) calloc(items, size); +} + +static void +zfree_cb(voidpf opaque, voidpf ptr) +{ + (void) opaque; + free(ptr); +} + +static CURLcode +process_zlib_error(struct Curl_easy *data, z_stream *z) +{ + if(z->msg) + failf(data, "Error while processing content unencoding: %s", + z->msg); + else + failf(data, "Error while processing content unencoding: " + "Unknown failure within decompression software."); + + return CURLE_BAD_CONTENT_ENCODING; +} + +static CURLcode +exit_zlib(struct Curl_easy *data, + z_stream *z, zlibInitState *zlib_init, CURLcode result) +{ + if(*zlib_init == ZLIB_GZIP_HEADER) + Curl_safefree(z->next_in); + + if(*zlib_init != ZLIB_UNINIT) { + if(inflateEnd(z) != Z_OK && result == CURLE_OK) + result = process_zlib_error(data, z); + *zlib_init = ZLIB_UNINIT; + } + + return result; +} + +static CURLcode process_trailer(struct Curl_easy *data, + struct zlib_writer *zp) +{ + z_stream *z = &zp->z; + CURLcode result = CURLE_OK; + uInt len = z->avail_in < zp->trailerlen? z->avail_in: zp->trailerlen; + + /* Consume expected trailer bytes. Terminate stream if exhausted. + Issue an error if unexpected bytes follow. */ + + zp->trailerlen -= len; + z->avail_in -= len; + z->next_in += len; + if(z->avail_in) + result = CURLE_WRITE_ERROR; + if(result || !zp->trailerlen) + result = exit_zlib(data, z, &zp->zlib_init, result); + else { + /* Only occurs for gzip with zlib < 1.2.0.4 or raw deflate. */ + zp->zlib_init = ZLIB_EXTERNAL_TRAILER; + } + return result; +} + +static CURLcode inflate_stream(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + zlibInitState started) +{ + struct zlib_writer *zp = (struct zlib_writer *) writer; + z_stream *z = &zp->z; /* zlib state structure */ + uInt nread = z->avail_in; + Bytef *orig_in = z->next_in; + bool done = FALSE; + CURLcode result = CURLE_OK; /* Curl_client_write status */ + char *decomp; /* Put the decompressed data here. */ + + /* Check state. */ + if(zp->zlib_init != ZLIB_INIT && + zp->zlib_init != ZLIB_INFLATING && + zp->zlib_init != ZLIB_INIT_GZIP && + zp->zlib_init != ZLIB_GZIP_INFLATING) + return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); + + /* Dynamically allocate a buffer for decompression because it is uncommonly + large to hold on the stack */ + decomp = malloc(DSIZ); + if(!decomp) + return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); + + /* because the buffer size is fixed, iteratively decompress and transfer to + the client via next_write function. */ + while(!done) { + int status; /* zlib status */ + done = TRUE; + + /* (re)set buffer for decompressed output for every iteration */ + z->next_out = (Bytef *) decomp; + z->avail_out = DSIZ; + +#ifdef Z_BLOCK + /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */ + status = inflate(z, Z_BLOCK); +#else + /* fallback for zlib ver. < 1.2.0.5 */ + status = inflate(z, Z_SYNC_FLUSH); +#endif + + /* Flush output data if some. */ + if(z->avail_out != DSIZ) { + if(status == Z_OK || status == Z_STREAM_END) { + zp->zlib_init = started; /* Data started. */ + result = Curl_cwriter_write(data, writer->next, type, decomp, + DSIZ - z->avail_out); + if(result) { + exit_zlib(data, z, &zp->zlib_init, result); + break; + } + } + } + + /* Dispatch by inflate() status. */ + switch(status) { + case Z_OK: + /* Always loop: there may be unflushed latched data in zlib state. */ + done = FALSE; + break; + case Z_BUF_ERROR: + /* No more data to flush: just exit loop. */ + break; + case Z_STREAM_END: + result = process_trailer(data, zp); + break; + case Z_DATA_ERROR: + /* some servers seem to not generate zlib headers, so this is an attempt + to fix and continue anyway */ + if(zp->zlib_init == ZLIB_INIT) { + /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */ + (void) inflateEnd(z); /* do not care about the return code */ + if(inflateInit2(z, -MAX_WBITS) == Z_OK) { + z->next_in = orig_in; + z->avail_in = nread; + zp->zlib_init = ZLIB_INFLATING; + zp->trailerlen = 4; /* Tolerate up to 4 unknown trailer bytes. */ + done = FALSE; + break; + } + zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ + } + result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); + break; + default: + result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); + break; + } + } + free(decomp); + + /* We are about to leave this call so the `nread' data bytes will not be seen + again. If we are in a state that would wrongly allow restart in raw mode + at the next call, assume output has already started. */ + if(nread && zp->zlib_init == ZLIB_INIT) + zp->zlib_init = started; /* Cannot restart anymore. */ + + return result; +} + + +/* Deflate handler. */ +static CURLcode deflate_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct zlib_writer *zp = (struct zlib_writer *) writer; + z_stream *z = &zp->z; /* zlib state structure */ + + /* Initialize zlib */ + z->zalloc = (alloc_func) zalloc_cb; + z->zfree = (free_func) zfree_cb; + + if(inflateInit(z) != Z_OK) + return process_zlib_error(data, z); + zp->zlib_init = ZLIB_INIT; + return CURLE_OK; +} + +static CURLcode deflate_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + struct zlib_writer *zp = (struct zlib_writer *) writer; + z_stream *z = &zp->z; /* zlib state structure */ + + if(!(type & CLIENTWRITE_BODY) || !nbytes) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + + /* Set the compressed input when this function is called */ + z->next_in = (Bytef *) buf; + z->avail_in = (uInt) nbytes; + + if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER) + return process_trailer(data, zp); + + /* Now uncompress the data */ + return inflate_stream(data, writer, type, ZLIB_INFLATING); +} + +static void deflate_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct zlib_writer *zp = (struct zlib_writer *) writer; + z_stream *z = &zp->z; /* zlib state structure */ + + exit_zlib(data, z, &zp->zlib_init, CURLE_OK); +} + +static const struct Curl_cwtype deflate_encoding = { + "deflate", + NULL, + deflate_do_init, + deflate_do_write, + deflate_do_close, + sizeof(struct zlib_writer) +}; + + +/* Gzip handler. */ +static CURLcode gzip_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct zlib_writer *zp = (struct zlib_writer *) writer; + z_stream *z = &zp->z; /* zlib state structure */ + + /* Initialize zlib */ + z->zalloc = (alloc_func) zalloc_cb; + z->zfree = (free_func) zfree_cb; + + if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { + /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ + if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { + return process_zlib_error(data, z); + } + zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ + } + else { + /* we must parse the gzip header and trailer ourselves */ + if(inflateInit2(z, -MAX_WBITS) != Z_OK) { + return process_zlib_error(data, z); + } + zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */ + zp->zlib_init = ZLIB_INIT; /* Initial call state */ + } + + return CURLE_OK; +} + +#ifdef OLD_ZLIB_SUPPORT +/* Skip over the gzip header */ +typedef enum { + GZIP_OK, + GZIP_BAD, + GZIP_UNDERFLOW +} gzip_status; + +static gzip_status check_gzip_header(unsigned char const *data, ssize_t len, + ssize_t *headerlen) +{ + int method, flags; + const ssize_t totallen = len; + + /* The shortest header is 10 bytes */ + if(len < 10) + return GZIP_UNDERFLOW; + + if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1)) + return GZIP_BAD; + + method = data[2]; + flags = data[3]; + + if(method != Z_DEFLATED || (flags & RESERVED) != 0) { + /* cannot handle this compression method or unknown flag */ + return GZIP_BAD; + } + + /* Skip over time, xflags, OS code and all previous bytes */ + len -= 10; + data += 10; + + if(flags & EXTRA_FIELD) { + ssize_t extra_len; + + if(len < 2) + return GZIP_UNDERFLOW; + + extra_len = (data[1] << 8) | data[0]; + + if(len < (extra_len + 2)) + return GZIP_UNDERFLOW; + + len -= (extra_len + 2); + data += (extra_len + 2); + } + + if(flags & ORIG_NAME) { + /* Skip over NUL-terminated filename */ + while(len && *data) { + --len; + ++data; + } + if(!len || *data) + return GZIP_UNDERFLOW; + + /* Skip over the NUL */ + --len; + ++data; + } + + if(flags & COMMENT) { + /* Skip over NUL-terminated comment */ + while(len && *data) { + --len; + ++data; + } + if(!len || *data) + return GZIP_UNDERFLOW; + + /* Skip over the NUL */ + --len; + } + + if(flags & HEAD_CRC) { + if(len < 2) + return GZIP_UNDERFLOW; + + len -= 2; + } + + *headerlen = totallen - len; + return GZIP_OK; +} +#endif + +static CURLcode gzip_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + struct zlib_writer *zp = (struct zlib_writer *) writer; + z_stream *z = &zp->z; /* zlib state structure */ + + if(!(type & CLIENTWRITE_BODY) || !nbytes) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + + if(zp->zlib_init == ZLIB_INIT_GZIP) { + /* Let zlib handle the gzip decompression entirely */ + z->next_in = (Bytef *) buf; + z->avail_in = (uInt) nbytes; + /* Now uncompress the data */ + return inflate_stream(data, writer, type, ZLIB_INIT_GZIP); + } + +#ifndef OLD_ZLIB_SUPPORT + /* Support for old zlib versions is compiled away and we are running with + an old version, so return an error. */ + return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); + +#else + /* This next mess is to get around the potential case where there is not + * enough data passed in to skip over the gzip header. If that happens, we + * malloc a block and copy what we have then wait for the next call. If + * there still is not enough (this is definitely a worst-case scenario), we + * make the block bigger, copy the next part in and keep waiting. + * + * This is only required with zlib versions < 1.2.0.4 as newer versions + * can handle the gzip header themselves. + */ + + switch(zp->zlib_init) { + /* Skip over gzip header? */ + case ZLIB_INIT: + { + /* Initial call state */ + ssize_t hlen; + + switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) { + case GZIP_OK: + z->next_in = (Bytef *) buf + hlen; + z->avail_in = (uInt) (nbytes - hlen); + zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ + break; + + case GZIP_UNDERFLOW: + /* We need more data so we can find the end of the gzip header. it is + * possible that the memory block we malloc here will never be freed if + * the transfer abruptly aborts after this point. Since it is unlikely + * that circumstances will be right for this code path to be followed in + * the first place, and it is even more unlikely for a transfer to fail + * immediately afterwards, it should seldom be a problem. + */ + z->avail_in = (uInt) nbytes; + z->next_in = malloc(z->avail_in); + if(!z->next_in) { + return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); + } + memcpy(z->next_in, buf, z->avail_in); + zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ + /* We do not have any data to inflate yet */ + return CURLE_OK; + + case GZIP_BAD: + default: + return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); + } + + } + break; + + case ZLIB_GZIP_HEADER: + { + /* Need more gzip header data state */ + ssize_t hlen; + z->avail_in += (uInt) nbytes; + z->next_in = Curl_saferealloc(z->next_in, z->avail_in); + if(!z->next_in) { + return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); + } + /* Append the new block of data to the previous one */ + memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); + + switch(check_gzip_header(z->next_in, (ssize_t)z->avail_in, &hlen)) { + case GZIP_OK: + /* This is the zlib stream data */ + free(z->next_in); + /* Do not point into the malloced block since we just freed it */ + z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; + z->avail_in = z->avail_in - (uInt)hlen; + zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ + break; + + case GZIP_UNDERFLOW: + /* We still do not have any data to inflate! */ + return CURLE_OK; + + case GZIP_BAD: + default: + return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); + } + + } + break; + + case ZLIB_EXTERNAL_TRAILER: + z->next_in = (Bytef *) buf; + z->avail_in = (uInt) nbytes; + return process_trailer(data, zp); + + case ZLIB_GZIP_INFLATING: + default: + /* Inflating stream state */ + z->next_in = (Bytef *) buf; + z->avail_in = (uInt) nbytes; + break; + } + + if(z->avail_in == 0) { + /* We do not have any data to inflate; wait until next time */ + return CURLE_OK; + } + + /* We have parsed the header, now uncompress the data */ + return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING); +#endif +} + +static void gzip_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct zlib_writer *zp = (struct zlib_writer *) writer; + z_stream *z = &zp->z; /* zlib state structure */ + + exit_zlib(data, z, &zp->zlib_init, CURLE_OK); +} + +static const struct Curl_cwtype gzip_encoding = { + "gzip", + "x-gzip", + gzip_do_init, + gzip_do_write, + gzip_do_close, + sizeof(struct zlib_writer) +}; + +#endif /* HAVE_LIBZ */ + + +#ifdef HAVE_BROTLI +/* Brotli writer. */ +struct brotli_writer { + struct Curl_cwriter super; + BrotliDecoderState *br; /* State structure for brotli. */ +}; + +static CURLcode brotli_map_error(BrotliDecoderErrorCode be) +{ + switch(be) { + case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE: + case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE: + case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET: + case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME: + case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE: + case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE: + case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT: + case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1: + case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2: + case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM: + case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY: + case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: + case BROTLI_DECODER_ERROR_FORMAT_PADDING_1: + case BROTLI_DECODER_ERROR_FORMAT_PADDING_2: +#ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY + case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY: +#endif +#ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET + case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: +#endif + case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: + return CURLE_BAD_CONTENT_ENCODING; + case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: + case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS: + case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP: + case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1: + case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2: + case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES: + return CURLE_OUT_OF_MEMORY; + default: + break; + } + return CURLE_WRITE_ERROR; +} + +static CURLcode brotli_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct brotli_writer *bp = (struct brotli_writer *) writer; + (void) data; + + bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL); + return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY; +} + +static CURLcode brotli_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + struct brotli_writer *bp = (struct brotli_writer *) writer; + const uint8_t *src = (const uint8_t *) buf; + char *decomp; + uint8_t *dst; + size_t dstleft; + CURLcode result = CURLE_OK; + BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + + if(!(type & CLIENTWRITE_BODY) || !nbytes) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + + if(!bp->br) + return CURLE_WRITE_ERROR; /* Stream already ended. */ + + decomp = malloc(DSIZ); + if(!decomp) + return CURLE_OUT_OF_MEMORY; + + while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && + result == CURLE_OK) { + dst = (uint8_t *) decomp; + dstleft = DSIZ; + r = BrotliDecoderDecompressStream(bp->br, + &nbytes, &src, &dstleft, &dst, NULL); + result = Curl_cwriter_write(data, writer->next, type, + decomp, DSIZ - dstleft); + if(result) + break; + switch(r) { + case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: + case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: + break; + case BROTLI_DECODER_RESULT_SUCCESS: + BrotliDecoderDestroyInstance(bp->br); + bp->br = NULL; + if(nbytes) + result = CURLE_WRITE_ERROR; + break; + default: + result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br)); + break; + } + } + free(decomp); + return result; +} + +static void brotli_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct brotli_writer *bp = (struct brotli_writer *) writer; + + (void) data; + + if(bp->br) { + BrotliDecoderDestroyInstance(bp->br); + bp->br = NULL; + } +} + +static const struct Curl_cwtype brotli_encoding = { + "br", + NULL, + brotli_do_init, + brotli_do_write, + brotli_do_close, + sizeof(struct brotli_writer) +}; +#endif + + +#ifdef HAVE_ZSTD +/* Zstd writer. */ +struct zstd_writer { + struct Curl_cwriter super; + ZSTD_DStream *zds; /* State structure for zstd. */ + void *decomp; +}; + +static CURLcode zstd_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct zstd_writer *zp = (struct zstd_writer *) writer; + + (void)data; + + zp->zds = ZSTD_createDStream(); + zp->decomp = NULL; + return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY; +} + +static CURLcode zstd_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + CURLcode result = CURLE_OK; + struct zstd_writer *zp = (struct zstd_writer *) writer; + ZSTD_inBuffer in; + ZSTD_outBuffer out; + size_t errorCode; + + if(!(type & CLIENTWRITE_BODY) || !nbytes) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + + if(!zp->decomp) { + zp->decomp = malloc(DSIZ); + if(!zp->decomp) + return CURLE_OUT_OF_MEMORY; + } + in.pos = 0; + in.src = buf; + in.size = nbytes; + + for(;;) { + out.pos = 0; + out.dst = zp->decomp; + out.size = DSIZ; + + errorCode = ZSTD_decompressStream(zp->zds, &out, &in); + if(ZSTD_isError(errorCode)) { + return CURLE_BAD_CONTENT_ENCODING; + } + if(out.pos > 0) { + result = Curl_cwriter_write(data, writer->next, type, + zp->decomp, out.pos); + if(result) + break; + } + if((in.pos == nbytes) && (out.pos < out.size)) + break; + } + + return result; +} + +static void zstd_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct zstd_writer *zp = (struct zstd_writer *) writer; + + (void)data; + + if(zp->decomp) { + free(zp->decomp); + zp->decomp = NULL; + } + if(zp->zds) { + ZSTD_freeDStream(zp->zds); + zp->zds = NULL; + } +} + +static const struct Curl_cwtype zstd_encoding = { + "zstd", + NULL, + zstd_do_init, + zstd_do_write, + zstd_do_close, + sizeof(struct zstd_writer) +}; +#endif + + +/* Identity handler. */ +static const struct Curl_cwtype identity_encoding = { + "identity", + "none", + Curl_cwriter_def_init, + Curl_cwriter_def_write, + Curl_cwriter_def_close, + sizeof(struct Curl_cwriter) +}; + + +/* supported general content decoders. */ +static const struct Curl_cwtype * const general_unencoders[] = { + &identity_encoding, +#ifdef HAVE_LIBZ + &deflate_encoding, + &gzip_encoding, +#endif +#ifdef HAVE_BROTLI + &brotli_encoding, +#endif +#ifdef HAVE_ZSTD + &zstd_encoding, +#endif + NULL +}; + +/* supported content decoders only for transfer encodings */ +static const struct Curl_cwtype * const transfer_unencoders[] = { +#ifndef CURL_DISABLE_HTTP + &Curl_httpchunk_unencoder, +#endif + NULL +}; + +/* Provide a list of comma-separated names of supported encodings. +*/ +void Curl_all_content_encodings(char *buf, size_t blen) +{ + size_t len = 0; + const struct Curl_cwtype * const *cep; + const struct Curl_cwtype *ce; + + DEBUGASSERT(buf); + DEBUGASSERT(blen); + buf[0] = 0; + + for(cep = general_unencoders; *cep; cep++) { + ce = *cep; + if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) + len += strlen(ce->name) + 2; + } + + if(!len) { + if(blen >= sizeof(CONTENT_ENCODING_DEFAULT)) + strcpy(buf, CONTENT_ENCODING_DEFAULT); + } + else if(blen > len) { + char *p = buf; + for(cep = general_unencoders; *cep; cep++) { + ce = *cep; + if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) { + strcpy(p, ce->name); + p += strlen(p); + *p++ = ','; + *p++ = ' '; + } + } + p[-2] = '\0'; + } +} + +/* Deferred error dummy writer. */ +static CURLcode error_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + (void)data; + (void)writer; + return CURLE_OK; +} + +static CURLcode error_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + char all[256]; + (void)Curl_all_content_encodings(all, sizeof(all)); + + (void) writer; + (void) buf; + (void) nbytes; + + if(!(type & CLIENTWRITE_BODY) || !nbytes) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + + failf(data, "Unrecognized content encoding type. " + "libcurl understands %s content encodings.", all); + return CURLE_BAD_CONTENT_ENCODING; +} + +static void error_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + (void) data; + (void) writer; +} + +static const struct Curl_cwtype error_writer = { + "ce-error", + NULL, + error_do_init, + error_do_write, + error_do_close, + sizeof(struct Curl_cwriter) +}; + +/* Find the content encoding by name. */ +static const struct Curl_cwtype *find_unencode_writer(const char *name, + size_t len, + Curl_cwriter_phase phase) +{ + const struct Curl_cwtype * const *cep; + + if(phase == CURL_CW_TRANSFER_DECODE) { + for(cep = transfer_unencoders; *cep; cep++) { + const struct Curl_cwtype *ce = *cep; + if((strncasecompare(name, ce->name, len) && !ce->name[len]) || + (ce->alias && strncasecompare(name, ce->alias, len) + && !ce->alias[len])) + return ce; + } + } + /* look among the general decoders */ + for(cep = general_unencoders; *cep; cep++) { + const struct Curl_cwtype *ce = *cep; + if((strncasecompare(name, ce->name, len) && !ce->name[len]) || + (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len])) + return ce; + } + return NULL; +} + +/* Setup the unencoding stack from the Content-Encoding header value. + * See RFC 7231 section 3.1.2.2. */ +CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, + const char *enclist, int is_transfer) +{ + Curl_cwriter_phase phase = is_transfer? + CURL_CW_TRANSFER_DECODE:CURL_CW_CONTENT_DECODE; + CURLcode result; + + do { + const char *name; + size_t namelen; + bool is_chunked = FALSE; + + /* Parse a single encoding name. */ + while(ISBLANK(*enclist) || *enclist == ',') + enclist++; + + name = enclist; + + for(namelen = 0; *enclist && *enclist != ','; enclist++) + if(!ISSPACE(*enclist)) + namelen = enclist - name + 1; + + if(namelen) { + const struct Curl_cwtype *cwt; + struct Curl_cwriter *writer; + + CURL_TRC_WRITE(data, "looking for %s decoder: %.*s", + is_transfer? "transfer" : "content", (int)namelen, name); + is_chunked = (is_transfer && (namelen == 7) && + strncasecompare(name, "chunked", 7)); + /* if we skip the decoding in this phase, do not look further. + * Exception is "chunked" transfer-encoding which always must happen */ + if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) || + (!is_transfer && data->set.http_ce_skip)) { + /* not requested, ignore */ + CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s", + (int)namelen, name); + return CURLE_OK; + } + + if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) { + failf(data, "Reject response due to more than %u content encodings", + MAX_ENCODE_STACK); + return CURLE_BAD_CONTENT_ENCODING; + } + + cwt = find_unencode_writer(name, namelen, phase); + if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) { + /* A 'chunked' transfer encoding has already been added. + * Ignore duplicates. See #13451. + * Also RFC 9112, ch. 6.1: + * "A sender MUST NOT apply the chunked transfer coding more than + * once to a message body." + */ + CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder"); + return CURLE_OK; + } + + if(is_transfer && !is_chunked && + Curl_cwriter_get_by_name(data, "chunked")) { + /* RFC 9112, ch. 6.1: + * "If any transfer coding other than chunked is applied to a + * response's content, the sender MUST either apply chunked as the + * final transfer coding or terminate the message by closing the + * connection." + * "chunked" must be the last added to be the first in its phase, + * reject this. + */ + failf(data, "Reject response due to 'chunked' not being the last " + "Transfer-Encoding"); + return CURLE_BAD_CONTENT_ENCODING; + } + + if(!cwt) + cwt = &error_writer; /* Defer error at use. */ + + result = Curl_cwriter_create(&writer, data, cwt, phase); + CURL_TRC_WRITE(data, "added %s decoder %s -> %d", + is_transfer? "transfer" : "content", cwt->name, result); + if(result) + return result; + + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + return result; + } + } + } while(*enclist); + + return CURLE_OK; +} + +#else +/* Stubs for builds without HTTP. */ +CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, + const char *enclist, int is_transfer) +{ + (void) data; + (void) enclist; + (void) is_transfer; + return CURLE_NOT_BUILT_IN; +} + +void Curl_all_content_encodings(char *buf, size_t blen) +{ + DEBUGASSERT(buf); + DEBUGASSERT(blen); + if(blen < sizeof(CONTENT_ENCODING_DEFAULT)) + buf[0] = 0; + else + strcpy(buf, CONTENT_ENCODING_DEFAULT); +} + + +#endif /* CURL_DISABLE_HTTP */ diff --git a/third_party/curl/lib/content_encoding.h b/third_party/curl/lib/content_encoding.h new file mode 100644 index 0000000000..1addf230bb --- /dev/null +++ b/third_party/curl/lib/content_encoding.h @@ -0,0 +1,34 @@ +#ifndef HEADER_CURL_CONTENT_ENCODING_H +#define HEADER_CURL_CONTENT_ENCODING_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +struct Curl_cwriter; + +void Curl_all_content_encodings(char *buf, size_t blen); + +CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, + const char *enclist, int is_transfer); +#endif /* HEADER_CURL_CONTENT_ENCODING_H */ diff --git a/third_party/curl/lib/cookie.c b/third_party/curl/lib/cookie.c new file mode 100644 index 0000000000..b0d8d84bef --- /dev/null +++ b/third_party/curl/lib/cookie.c @@ -0,0 +1,1772 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/*** + + +RECEIVING COOKIE INFORMATION +============================ + +struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, + const char *file, struct CookieInfo *inc, bool newsession); + + Inits a cookie struct to store data in a local file. This is always + called before any cookies are set. + +struct Cookie *Curl_cookie_add(struct Curl_easy *data, + struct CookieInfo *c, bool httpheader, bool noexpire, + char *lineptr, const char *domain, const char *path, + bool secure); + + The 'lineptr' parameter is a full "Set-cookie:" line as + received from a server. + + The function need to replace previously stored lines that this new + line supersedes. + + It may remove lines that are expired. + + It should return an indication of success/error. + + +SENDING COOKIE INFORMATION +========================== + +struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie, + char *host, char *path, bool secure); + + For a given host and path, return a linked list of cookies that + the client should send to the server if used now. The secure + boolean informs the cookie if a secure connection is achieved or + not. + + It shall only return cookies that have not expired. + + +Example set of cookies: + + Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure + Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; + domain=.fidelity.com; path=/ftgw; secure + Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; + domain=.fidelity.com; path=/; secure + Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; + domain=.fidelity.com; path=/; secure + Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; + domain=.fidelity.com; path=/; secure + Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; + domain=.fidelity.com; path=/; secure + Set-cookie: + Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday, + 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure +****/ + + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + +#include "urldata.h" +#include "cookie.h" +#include "psl.h" +#include "strtok.h" +#include "sendf.h" +#include "slist.h" +#include "share.h" +#include "strtoofft.h" +#include "strcase.h" +#include "curl_get_line.h" +#include "curl_memrchr.h" +#include "parsedate.h" +#include "rename.h" +#include "fopen.h" +#include "strdup.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +static void strstore(char **str, const char *newstr, size_t len); + +static void freecookie(struct Cookie *co) +{ + free(co->domain); + free(co->path); + free(co->spath); + free(co->name); + free(co->value); + free(co); +} + +static bool cookie_tailmatch(const char *cookie_domain, + size_t cookie_domain_len, + const char *hostname) +{ + size_t hostname_len = strlen(hostname); + + if(hostname_len < cookie_domain_len) + return FALSE; + + if(!strncasecompare(cookie_domain, + hostname + hostname_len-cookie_domain_len, + cookie_domain_len)) + return FALSE; + + /* + * A lead char of cookie_domain is not '.'. + * RFC6265 4.1.2.3. The Domain Attribute says: + * For example, if the value of the Domain attribute is + * "example.com", the user agent will include the cookie in the Cookie + * header when making HTTP requests to example.com, www.example.com, and + * www.corp.example.com. + */ + if(hostname_len == cookie_domain_len) + return TRUE; + if('.' == *(hostname + hostname_len - cookie_domain_len - 1)) + return TRUE; + return FALSE; +} + +/* + * matching cookie path and URL path + * RFC6265 5.1.4 Paths and Path-Match + */ +static bool pathmatch(const char *cookie_path, const char *request_uri) +{ + size_t cookie_path_len; + size_t uri_path_len; + char *uri_path = NULL; + char *pos; + bool ret = FALSE; + + /* cookie_path must not have last '/' separator. ex: /sample */ + cookie_path_len = strlen(cookie_path); + if(1 == cookie_path_len) { + /* cookie_path must be '/' */ + return TRUE; + } + + uri_path = strdup(request_uri); + if(!uri_path) + return FALSE; + pos = strchr(uri_path, '?'); + if(pos) + *pos = 0x0; + + /* #-fragments are already cut off! */ + if(0 == strlen(uri_path) || uri_path[0] != '/') { + strstore(&uri_path, "/", 1); + if(!uri_path) + return FALSE; + } + + /* + * here, RFC6265 5.1.4 says + * 4. Output the characters of the uri-path from the first character up + * to, but not including, the right-most %x2F ("/"). + * but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site + * without redirect. + * Ignore this algorithm because /hoge is uri path for this case + * (uri path is not /). + */ + + uri_path_len = strlen(uri_path); + + if(uri_path_len < cookie_path_len) { + ret = FALSE; + goto pathmatched; + } + + /* not using checkprefix() because matching should be case-sensitive */ + if(strncmp(cookie_path, uri_path, cookie_path_len)) { + ret = FALSE; + goto pathmatched; + } + + /* The cookie-path and the uri-path are identical. */ + if(cookie_path_len == uri_path_len) { + ret = TRUE; + goto pathmatched; + } + + /* here, cookie_path_len < uri_path_len */ + if(uri_path[cookie_path_len] == '/') { + ret = TRUE; + goto pathmatched; + } + + ret = FALSE; + +pathmatched: + free(uri_path); + return ret; +} + +/* + * Return the top-level domain, for optimal hashing. + */ +static const char *get_top_domain(const char * const domain, size_t *outlen) +{ + size_t len = 0; + const char *first = NULL, *last; + + if(domain) { + len = strlen(domain); + last = memrchr(domain, '.', len); + if(last) { + first = memrchr(domain, '.', (last - domain)); + if(first) + len -= (++first - domain); + } + } + + if(outlen) + *outlen = len; + + return first? first: domain; +} + +/* Avoid C1001, an "internal error" with MSVC14 */ +#if defined(_MSC_VER) && (_MSC_VER == 1900) +#pragma optimize("", off) +#endif + +/* + * A case-insensitive hash for the cookie domains. + */ +static size_t cookie_hash_domain(const char *domain, const size_t len) +{ + const char *end = domain + len; + size_t h = 5381; + + while(domain < end) { + size_t j = (size_t)Curl_raw_toupper(*domain++); + h += h << 5; + h ^= j; + } + + return (h % COOKIE_HASH_SIZE); +} + +#if defined(_MSC_VER) && (_MSC_VER == 1900) +#pragma optimize("", on) +#endif + +/* + * Hash this domain. + */ +static size_t cookiehash(const char * const domain) +{ + const char *top; + size_t len; + + if(!domain || Curl_host_is_ipnum(domain)) + return 0; + + top = get_top_domain(domain, &len); + return cookie_hash_domain(top, len); +} + +/* + * cookie path sanitize + */ +static char *sanitize_cookie_path(const char *cookie_path) +{ + size_t len; + char *new_path = strdup(cookie_path); + if(!new_path) + return NULL; + + /* some stupid site sends path attribute with '"'. */ + len = strlen(new_path); + if(new_path[0] == '\"') { + memmove(new_path, new_path + 1, len); + len--; + } + if(len && (new_path[len - 1] == '\"')) { + new_path[--len] = 0x0; + } + + /* RFC6265 5.2.4 The Path Attribute */ + if(new_path[0] != '/') { + /* Let cookie-path be the default-path. */ + strstore(&new_path, "/", 1); + return new_path; + } + + /* convert /hoge/ to /hoge */ + if(len && new_path[len - 1] == '/') { + new_path[len - 1] = 0x0; + } + + return new_path; +} + +/* + * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). + * + * NOTE: OOM or cookie parsing failures are ignored. + */ +void Curl_cookie_loadfiles(struct Curl_easy *data) +{ + struct curl_slist *list = data->state.cookielist; + if(list) { + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + while(list) { + struct CookieInfo *newcookies = + Curl_cookie_init(data, list->data, data->cookies, + data->set.cookiesession); + if(!newcookies) + /* + * Failure may be due to OOM or a bad cookie; both are ignored + * but only the first should be + */ + infof(data, "ignoring failed cookie_init for %s", list->data); + else + data->cookies = newcookies; + list = list->next; + } + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } +} + +/* + * strstore + * + * A thin wrapper around strdup which ensures that any memory allocated at + * *str will be freed before the string allocated by strdup is stored there. + * The intended usecase is repeated assignments to the same variable during + * parsing in a last-wins scenario. The caller is responsible for checking + * for OOM errors. + */ +static void strstore(char **str, const char *newstr, size_t len) +{ + DEBUGASSERT(newstr); + DEBUGASSERT(str); + free(*str); + *str = Curl_memdup0(newstr, len); +} + +/* + * remove_expired + * + * Remove expired cookies from the hash by inspecting the expires timestamp on + * each cookie in the hash, freeing and deleting any where the timestamp is in + * the past. If the cookiejar has recorded the next timestamp at which one or + * more cookies expire, then processing will exit early in case this timestamp + * is in the future. + */ +static void remove_expired(struct CookieInfo *cookies) +{ + struct Cookie *co, *nx; + curl_off_t now = (curl_off_t)time(NULL); + unsigned int i; + + /* + * If the earliest expiration timestamp in the jar is in the future we can + * skip scanning the whole jar and instead exit early as there will not be + * any cookies to evict. If we need to evict however, reset the + * next_expiration counter in order to track the next one. In case the + * recorded first expiration is the max offset, then perform the safe + * fallback of checking all cookies. + */ + if(now < cookies->next_expiration && + cookies->next_expiration != CURL_OFF_T_MAX) + return; + else + cookies->next_expiration = CURL_OFF_T_MAX; + + for(i = 0; i < COOKIE_HASH_SIZE; i++) { + struct Cookie *pv = NULL; + co = cookies->cookies[i]; + while(co) { + nx = co->next; + if(co->expires && co->expires < now) { + if(!pv) { + cookies->cookies[i] = co->next; + } + else { + pv->next = co->next; + } + cookies->numcookies--; + freecookie(co); + } + else { + /* + * If this cookie has an expiration timestamp earlier than what we have + * seen so far then record it for the next round of expirations. + */ + if(co->expires && co->expires < cookies->next_expiration) + cookies->next_expiration = co->expires; + pv = co; + } + co = nx; + } + } +} + +#ifndef USE_LIBPSL +/* Make sure domain contains a dot or is localhost. */ +static bool bad_domain(const char *domain, size_t len) +{ + if((len == 9) && strncasecompare(domain, "localhost", 9)) + return FALSE; + else { + /* there must be a dot present, but that dot must not be a trailing dot */ + char *dot = memchr(domain, '.', len); + if(dot) { + size_t i = dot - domain; + if((len - i) > 1) + /* the dot is not the last byte */ + return FALSE; + } + } + return TRUE; +} +#endif + +/* + RFC 6265 section 4.1.1 says a server should accept this range: + + cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E + + But Firefox and Chrome as of June 2022 accept space, comma and double-quotes + fine. The prime reason for filtering out control bytes is that some HTTP + servers return 400 for requests that contain such. +*/ +static int invalid_octets(const char *p) +{ + /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */ + static const char badoctets[] = { + "\x01\x02\x03\x04\x05\x06\x07\x08\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f" + }; + size_t len; + /* scan for all the octets that are *not* in cookie-octet */ + len = strcspn(p, badoctets); + return (p[len] != '\0'); +} + +/* + * Curl_cookie_add + * + * Add a single cookie line to the cookie keeping object. Be aware that + * sometimes we get an IP-only hostname, and that might also be a numerical + * IPv6 address. + * + * Returns NULL on out of memory or invalid cookie. This is suboptimal, + * as they should be treated separately. + */ +struct Cookie * +Curl_cookie_add(struct Curl_easy *data, + struct CookieInfo *c, + bool httpheader, /* TRUE if HTTP header-style line */ + bool noexpire, /* if TRUE, skip remove_expired() */ + const char *lineptr, /* first character of the line */ + const char *domain, /* default domain */ + const char *path, /* full path used when this cookie is set, + used to get default path for the cookie + unless set */ + bool secure) /* TRUE if connection is over secure origin */ +{ + struct Cookie *clist; + struct Cookie *co; + struct Cookie *lastc = NULL; + struct Cookie *replace_co = NULL; + struct Cookie *replace_clist = NULL; + time_t now = time(NULL); + bool replace_old = FALSE; + bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */ + size_t myhash; + + DEBUGASSERT(data); + DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */ + if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT) + return NULL; + + /* First, alloc and init a new struct for it */ + co = calloc(1, sizeof(struct Cookie)); + if(!co) + return NULL; /* bail out if we are this low on memory */ + + if(httpheader) { + /* This line was read off an HTTP-header */ + const char *ptr; + + size_t linelength = strlen(lineptr); + if(linelength > MAX_COOKIE_LINE) { + /* discard overly long lines at once */ + free(co); + return NULL; + } + + ptr = lineptr; + do { + size_t vlen; + size_t nlen; + + while(*ptr && ISBLANK(*ptr)) + ptr++; + + /* we have a = pair or a stand-alone word here */ + nlen = strcspn(ptr, ";\t\r\n="); + if(nlen) { + bool done = FALSE; + bool sep = FALSE; + const char *namep = ptr; + const char *valuep; + + ptr += nlen; + + /* trim trailing spaces and tabs after name */ + while(nlen && ISBLANK(namep[nlen - 1])) + nlen--; + + if(*ptr == '=') { + vlen = strcspn(++ptr, ";\r\n"); + valuep = ptr; + sep = TRUE; + ptr = &valuep[vlen]; + + /* Strip off trailing whitespace from the value */ + while(vlen && ISBLANK(valuep[vlen-1])) + vlen--; + + /* Skip leading whitespace from the value */ + while(vlen && ISBLANK(*valuep)) { + valuep++; + vlen--; + } + + /* Reject cookies with a TAB inside the value */ + if(memchr(valuep, '\t', vlen)) { + freecookie(co); + infof(data, "cookie contains TAB, dropping"); + return NULL; + } + } + else { + valuep = NULL; + vlen = 0; + } + + /* + * Check for too long individual name or contents, or too long + * combination of name + contents. Chrome and Firefox support 4095 or + * 4096 bytes combo + */ + if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) || + ((nlen + vlen) > MAX_NAME)) { + freecookie(co); + infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", + nlen, vlen); + return NULL; + } + + /* + * Check if we have a reserved prefix set before anything else, as we + * otherwise have to test for the prefix in both the cookie name and + * "the rest". Prefixes must start with '__' and end with a '-', so + * only test for names where that can possibly be true. + */ + if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') { + if(strncasecompare("__Secure-", namep, 9)) + co->prefix |= COOKIE_PREFIX__SECURE; + else if(strncasecompare("__Host-", namep, 7)) + co->prefix |= COOKIE_PREFIX__HOST; + } + + /* + * Use strstore() below to properly deal with received cookie + * headers that have the same string property set more than once, + * and then we use the last one. + */ + + if(!co->name) { + /* The very first name/value pair is the actual cookie name */ + if(!sep) { + /* Bad name/value pair. */ + badcookie = TRUE; + break; + } + strstore(&co->name, namep, nlen); + strstore(&co->value, valuep, vlen); + done = TRUE; + if(!co->name || !co->value) { + badcookie = TRUE; + break; + } + if(invalid_octets(co->value) || invalid_octets(co->name)) { + infof(data, "invalid octets in name/value, cookie dropped"); + badcookie = TRUE; + break; + } + } + else if(!vlen) { + /* + * this was a "=" with no content, and we must allow + * 'secure' and 'httponly' specified this weirdly + */ + done = TRUE; + /* + * secure cookies are only allowed to be set when the connection is + * using a secure protocol, or when the cookie is being set by + * reading from file + */ + if((nlen == 6) && strncasecompare("secure", namep, 6)) { + if(secure || !c->running) { + co->secure = TRUE; + } + else { + badcookie = TRUE; + break; + } + } + else if((nlen == 8) && strncasecompare("httponly", namep, 8)) + co->httponly = TRUE; + else if(sep) + /* there was a '=' so we are not done parsing this field */ + done = FALSE; + } + if(done) + ; + else if((nlen == 4) && strncasecompare("path", namep, 4)) { + strstore(&co->path, valuep, vlen); + if(!co->path) { + badcookie = TRUE; /* out of memory bad */ + break; + } + free(co->spath); /* if this is set again */ + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) { + badcookie = TRUE; /* out of memory bad */ + break; + } + } + else if((nlen == 6) && + strncasecompare("domain", namep, 6) && vlen) { + bool is_ip; + + /* + * Now, we make sure that our host is within the given domain, or + * the given domain is not valid and thus cannot be set. + */ + + if('.' == valuep[0]) { + valuep++; /* ignore preceding dot */ + vlen--; + } + +#ifndef USE_LIBPSL + /* + * Without PSL we do not know when the incoming cookie is set on a + * TLD or otherwise "protected" suffix. To reduce risk, we require a + * dot OR the exact hostname being "localhost". + */ + if(bad_domain(valuep, vlen)) + domain = ":"; +#endif + + is_ip = Curl_host_is_ipnum(domain ? domain : valuep); + + if(!domain + || (is_ip && !strncmp(valuep, domain, vlen) && + (vlen == strlen(domain))) + || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) { + strstore(&co->domain, valuep, vlen); + if(!co->domain) { + badcookie = TRUE; + break; + } + if(!is_ip) + co->tailmatch = TRUE; /* we always do that if the domain name was + given */ + } + else { + /* + * We did not get a tailmatch and then the attempted set domain is + * not a domain to which the current host belongs. Mark as bad. + */ + badcookie = TRUE; + infof(data, "skipped cookie with bad tailmatch domain: %s", + valuep); + } + } + else if((nlen == 7) && strncasecompare("version", namep, 7)) { + /* just ignore */ + } + else if((nlen == 7) && strncasecompare("max-age", namep, 7)) { + /* + * Defined in RFC2109: + * + * Optional. The Max-Age attribute defines the lifetime of the + * cookie, in seconds. The delta-seconds value is a decimal non- + * negative integer. After delta-seconds seconds elapse, the + * client should discard the cookie. A value of zero means the + * cookie should be discarded immediately. + */ + CURLofft offt; + const char *maxage = valuep; + offt = curlx_strtoofft((*maxage == '\"')? + &maxage[1]:&maxage[0], NULL, 10, + &co->expires); + switch(offt) { + case CURL_OFFT_FLOW: + /* overflow, used max value */ + co->expires = CURL_OFF_T_MAX; + break; + case CURL_OFFT_INVAL: + /* negative or otherwise bad, expire */ + co->expires = 1; + break; + case CURL_OFFT_OK: + if(!co->expires) + /* already expired */ + co->expires = 1; + else if(CURL_OFF_T_MAX - now < co->expires) + /* would overflow */ + co->expires = CURL_OFF_T_MAX; + else + co->expires += now; + break; + } + } + else if((nlen == 7) && strncasecompare("expires", namep, 7)) { + char date[128]; + if(!co->expires && (vlen < sizeof(date))) { + /* copy the date so that it can be null terminated */ + memcpy(date, valuep, vlen); + date[vlen] = 0; + /* + * Let max-age have priority. + * + * If the date cannot get parsed for whatever reason, the cookie + * will be treated as a session cookie + */ + co->expires = Curl_getdate_capped(date); + + /* + * Session cookies have expires set to 0 so if we get that back + * from the date parser let's add a second to make it a + * non-session cookie + */ + if(co->expires == 0) + co->expires = 1; + else if(co->expires < 0) + co->expires = 0; + } + } + + /* + * Else, this is the second (or more) name we do not know about! + */ + } + else { + /* this is an "illegal" = pair */ + } + + while(*ptr && ISBLANK(*ptr)) + ptr++; + if(*ptr == ';') + ptr++; + else + break; + } while(1); + + if(!badcookie && !co->domain) { + if(domain) { + /* no domain was given in the header line, set the default */ + co->domain = strdup(domain); + if(!co->domain) + badcookie = TRUE; + } + } + + if(!badcookie && !co->path && path) { + /* + * No path was given in the header line, set the default. Note that the + * passed-in path to this function MAY have a '?' and following part that + * MUST NOT be stored as part of the path. + */ + char *queryp = strchr(path, '?'); + + /* + * queryp is where the interesting part of the path ends, so now we + * want to the find the last + */ + char *endslash; + if(!queryp) + endslash = strrchr(path, '/'); + else + endslash = memrchr(path, '/', (queryp - path)); + if(endslash) { + size_t pathlen = (endslash-path + 1); /* include end slash */ + co->path = Curl_memdup0(path, pathlen); + if(co->path) { + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) + badcookie = TRUE; /* out of memory bad */ + } + else + badcookie = TRUE; + } + } + + /* + * If we did not get a cookie name, or a bad one, the this is an illegal + * line so bail out. + */ + if(badcookie || !co->name) { + freecookie(co); + return NULL; + } + data->req.setcookies++; + } + else { + /* + * This line is NOT an HTTP header style line, we do offer support for + * reading the odd netscape cookies-file format here + */ + char *ptr; + char *firstptr; + char *tok_buf = NULL; + int fields; + + /* + * IE introduced HTTP-only cookies to prevent XSS attacks. Cookies marked + * with httpOnly after the domain name are not accessible from javascripts, + * but since curl does not operate at javascript level, we include them + * anyway. In Firefox's cookie files, these lines are preceded with + * #HttpOnly_ and then everything is as usual, so we skip 10 characters of + * the line.. + */ + if(strncmp(lineptr, "#HttpOnly_", 10) == 0) { + lineptr += 10; + co->httponly = TRUE; + } + + if(lineptr[0]=='#') { + /* do not even try the comments */ + free(co); + return NULL; + } + /* strip off the possible end-of-line characters */ + ptr = strchr(lineptr, '\r'); + if(ptr) + *ptr = 0; /* clear it */ + ptr = strchr(lineptr, '\n'); + if(ptr) + *ptr = 0; /* clear it */ + + firstptr = strtok_r((char *)lineptr, "\t", &tok_buf); /* tokenize on TAB */ + + /* + * Now loop through the fields and init the struct we already have + * allocated + */ + fields = 0; + for(ptr = firstptr; ptr && !badcookie; + ptr = strtok_r(NULL, "\t", &tok_buf), fields++) { + switch(fields) { + case 0: + if(ptr[0]=='.') /* skip preceding dots */ + ptr++; + co->domain = strdup(ptr); + if(!co->domain) + badcookie = TRUE; + break; + case 1: + /* + * flag: A TRUE/FALSE value indicating if all machines within a given + * domain can access the variable. Set TRUE when the cookie says + * .domain.com and to false when the domain is complete www.domain.com + */ + co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE; + break; + case 2: + /* The file format allows the path field to remain not filled in */ + if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { + /* only if the path does not look like a boolean option! */ + co->path = strdup(ptr); + if(!co->path) + badcookie = TRUE; + else { + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) { + badcookie = TRUE; /* out of memory bad */ + } + } + break; + } + /* this does not look like a path, make one up! */ + co->path = strdup("/"); + if(!co->path) + badcookie = TRUE; + co->spath = strdup("/"); + if(!co->spath) + badcookie = TRUE; + fields++; /* add a field and fall down to secure */ + FALLTHROUGH(); + case 3: + co->secure = FALSE; + if(strcasecompare(ptr, "TRUE")) { + if(secure || c->running) + co->secure = TRUE; + else + badcookie = TRUE; + } + break; + case 4: + if(curlx_strtoofft(ptr, NULL, 10, &co->expires)) + badcookie = TRUE; + break; + case 5: + co->name = strdup(ptr); + if(!co->name) + badcookie = TRUE; + else { + /* For Netscape file format cookies we check prefix on the name */ + if(strncasecompare("__Secure-", co->name, 9)) + co->prefix |= COOKIE_PREFIX__SECURE; + else if(strncasecompare("__Host-", co->name, 7)) + co->prefix |= COOKIE_PREFIX__HOST; + } + break; + case 6: + co->value = strdup(ptr); + if(!co->value) + badcookie = TRUE; + break; + } + } + if(6 == fields) { + /* we got a cookie with blank contents, fix it */ + co->value = strdup(""); + if(!co->value) + badcookie = TRUE; + else + fields++; + } + + if(!badcookie && (7 != fields)) + /* we did not find the sufficient number of fields */ + badcookie = TRUE; + + if(badcookie) { + freecookie(co); + return NULL; + } + + } + + if(co->prefix & COOKIE_PREFIX__SECURE) { + /* The __Secure- prefix only requires that the cookie be set secure */ + if(!co->secure) { + freecookie(co); + return NULL; + } + } + if(co->prefix & COOKIE_PREFIX__HOST) { + /* + * The __Host- prefix requires the cookie to be secure, have a "/" path + * and not have a domain set. + */ + if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch) + ; + else { + freecookie(co); + return NULL; + } + } + + if(!c->running && /* read from a file */ + c->newsession && /* clean session cookies */ + !co->expires) { /* this is a session cookie since it does not expire! */ + freecookie(co); + return NULL; + } + + co->livecookie = c->running; + co->creationtime = ++c->lastct; + + /* + * Now we have parsed the incoming line, we must now check if this supersedes + * an already existing cookie, which it may if the previous have the same + * domain and path as this. + */ + + /* at first, remove expired cookies */ + if(!noexpire) + remove_expired(c); + +#ifdef USE_LIBPSL + /* + * Check if the domain is a Public Suffix and if yes, ignore the cookie. We + * must also check that the data handle is not NULL since the psl code will + * dereference it. + */ + if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { + bool acceptable = FALSE; + char lcase[256]; + char lcookie[256]; + size_t dlen = strlen(domain); + size_t clen = strlen(co->domain); + if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) { + const psl_ctx_t *psl = Curl_psl_use(data); + if(psl) { + /* the PSL check requires lowercase domain name and pattern */ + Curl_strntolower(lcase, domain, dlen + 1); + Curl_strntolower(lcookie, co->domain, clen + 1); + acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); + Curl_psl_release(data); + } + else + infof(data, "libpsl problem, rejecting cookie for satety"); + } + + if(!acceptable) { + infof(data, "cookie '%s' dropped, domain '%s' must not " + "set cookies for '%s'", co->name, domain, co->domain); + freecookie(co); + return NULL; + } + } +#endif + + /* A non-secure cookie may not overlay an existing secure cookie. */ + myhash = cookiehash(co->domain); + clist = c->cookies[myhash]; + while(clist) { + if(strcasecompare(clist->name, co->name)) { + /* the names are identical */ + bool matching_domains = FALSE; + + if(clist->domain && co->domain) { + if(strcasecompare(clist->domain, co->domain)) + /* The domains are identical */ + matching_domains = TRUE; + } + else if(!clist->domain && !co->domain) + matching_domains = TRUE; + + if(matching_domains && /* the domains were identical */ + clist->spath && co->spath && /* both have paths */ + clist->secure && !co->secure && !secure) { + size_t cllen; + const char *sep; + + /* + * A non-secure cookie may not overlay an existing secure cookie. + * For an existing cookie "a" with path "/login", refuse a new + * cookie "a" with for example path "/login/en", while the path + * "/loginhelper" is ok. + */ + + sep = strchr(clist->spath + 1, '/'); + + if(sep) + cllen = sep - clist->spath; + else + cllen = strlen(clist->spath); + + if(strncasecompare(clist->spath, co->spath, cllen)) { + infof(data, "cookie '%s' for domain '%s' dropped, would " + "overlay an existing cookie", co->name, co->domain); + freecookie(co); + return NULL; + } + } + } + + if(!replace_co && strcasecompare(clist->name, co->name)) { + /* the names are identical */ + + if(clist->domain && co->domain) { + if(strcasecompare(clist->domain, co->domain) && + (clist->tailmatch == co->tailmatch)) + /* The domains are identical */ + replace_old = TRUE; + } + else if(!clist->domain && !co->domain) + replace_old = TRUE; + + if(replace_old) { + /* the domains were identical */ + + if(clist->spath && co->spath && + !strcasecompare(clist->spath, co->spath)) + replace_old = FALSE; + else if(!clist->spath != !co->spath) + replace_old = FALSE; + } + + if(replace_old && !co->livecookie && clist->livecookie) { + /* + * Both cookies matched fine, except that the already present cookie + * is "live", which means it was set from a header, while the new one + * was read from a file and thus is not "live". "live" cookies are + * preferred so the new cookie is freed. + */ + freecookie(co); + return NULL; + } + if(replace_old) { + replace_co = co; + replace_clist = clist; + } + } + lastc = clist; + clist = clist->next; + } + if(replace_co) { + co = replace_co; + clist = replace_clist; + co->next = clist->next; /* get the next-pointer first */ + + /* when replacing, creationtime is kept from old */ + co->creationtime = clist->creationtime; + + /* then free all the old pointers */ + free(clist->name); + free(clist->value); + free(clist->domain); + free(clist->path); + free(clist->spath); + + *clist = *co; /* then store all the new data */ + + free(co); /* free the newly allocated memory */ + co = clist; + } + + if(c->running) + /* Only show this when NOT reading the cookies from a file */ + infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, " + "expire %" CURL_FORMAT_CURL_OFF_T, + replace_old?"Replaced":"Added", co->name, co->value, + co->domain, co->path, co->expires); + + if(!replace_old) { + /* then make the last item point on this new one */ + if(lastc) + lastc->next = co; + else + c->cookies[myhash] = co; + c->numcookies++; /* one more cookie in the jar */ + } + + /* + * Now that we have added a new cookie to the jar, update the expiration + * tracker in case it is the next one to expire. + */ + if(co->expires && (co->expires < c->next_expiration)) + c->next_expiration = co->expires; + + return co; +} + + +/* + * Curl_cookie_init() + * + * Inits a cookie struct to read data from a local file. This is always + * called before any cookies are set. File may be NULL in which case only the + * struct is initialized. Is file is "-" then STDIN is read. + * + * If 'newsession' is TRUE, discard all "session cookies" on read from file. + * + * Note that 'data' might be called as NULL pointer. If data is NULL, 'file' + * will be ignored. + * + * Returns NULL on out of memory. Invalid cookies are ignored. + */ +struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, + const char *file, + struct CookieInfo *inc, + bool newsession) +{ + struct CookieInfo *c; + FILE *handle = NULL; + + if(!inc) { + /* we did not get a struct, create one */ + c = calloc(1, sizeof(struct CookieInfo)); + if(!c) + return NULL; /* failed to get memory */ + /* + * Initialize the next_expiration time to signal that we do not have enough + * information yet. + */ + c->next_expiration = CURL_OFF_T_MAX; + } + else { + /* we got an already existing one, use that */ + c = inc; + } + c->newsession = newsession; /* new session? */ + + if(data) { + FILE *fp = NULL; + if(file && *file) { + if(!strcmp(file, "-")) + fp = stdin; + else { + fp = fopen(file, "rb"); + if(!fp) + infof(data, "WARNING: failed to open cookie file \"%s\"", file); + else + handle = fp; + } + } + + c->running = FALSE; /* this is not running, this is init */ + if(fp) { + struct dynbuf buf; + Curl_dyn_init(&buf, MAX_COOKIE_LINE); + while(Curl_get_line(&buf, fp)) { + char *lineptr = Curl_dyn_ptr(&buf); + bool headerline = FALSE; + if(checkprefix("Set-Cookie:", lineptr)) { + /* This is a cookie line, get it! */ + lineptr += 11; + headerline = TRUE; + while(*lineptr && ISBLANK(*lineptr)) + lineptr++; + } + + Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); + } + Curl_dyn_free(&buf); /* free the line buffer */ + + /* + * Remove expired cookies from the hash. We must make sure to run this + * after reading the file, and not on every cookie. + */ + remove_expired(c); + + if(handle) + fclose(handle); + } + data->state.cookie_engine = TRUE; + } + c->running = TRUE; /* now, we are running */ + + return c; +} + +/* + * cookie_sort + * + * Helper function to sort cookies such that the longest path gets before the + * shorter path. Path, domain and name lengths are considered in that order, + * with the creationtime as the tiebreaker. The creationtime is guaranteed to + * be unique per cookie, so we know we will get an ordering at that point. + */ +static int cookie_sort(const void *p1, const void *p2) +{ + struct Cookie *c1 = *(struct Cookie **)p1; + struct Cookie *c2 = *(struct Cookie **)p2; + size_t l1, l2; + + /* 1 - compare cookie path lengths */ + l1 = c1->path ? strlen(c1->path) : 0; + l2 = c2->path ? strlen(c2->path) : 0; + + if(l1 != l2) + return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ + + /* 2 - compare cookie domain lengths */ + l1 = c1->domain ? strlen(c1->domain) : 0; + l2 = c2->domain ? strlen(c2->domain) : 0; + + if(l1 != l2) + return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ + + /* 3 - compare cookie name lengths */ + l1 = c1->name ? strlen(c1->name) : 0; + l2 = c2->name ? strlen(c2->name) : 0; + + if(l1 != l2) + return (l2 > l1) ? 1 : -1; + + /* 4 - compare cookie creation time */ + return (c2->creationtime > c1->creationtime) ? 1 : -1; +} + +/* + * cookie_sort_ct + * + * Helper function to sort cookies according to creation time. + */ +static int cookie_sort_ct(const void *p1, const void *p2) +{ + struct Cookie *c1 = *(struct Cookie **)p1; + struct Cookie *c2 = *(struct Cookie **)p2; + + return (c2->creationtime > c1->creationtime) ? 1 : -1; +} + +#define CLONE(field) \ + do { \ + if(src->field) { \ + d->field = strdup(src->field); \ + if(!d->field) \ + goto fail; \ + } \ + } while(0) + +static struct Cookie *dup_cookie(struct Cookie *src) +{ + struct Cookie *d = calloc(1, sizeof(struct Cookie)); + if(d) { + CLONE(domain); + CLONE(path); + CLONE(spath); + CLONE(name); + CLONE(value); + d->expires = src->expires; + d->tailmatch = src->tailmatch; + d->secure = src->secure; + d->livecookie = src->livecookie; + d->httponly = src->httponly; + d->creationtime = src->creationtime; + } + return d; + +fail: + freecookie(d); + return NULL; +} + +/* + * Curl_cookie_getlist + * + * For a given host and path, return a linked list of cookies that the client + * should send to the server if used now. The secure boolean informs the cookie + * if a secure connection is achieved or not. + * + * It shall only return cookies that have not expired. + */ +struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, + struct CookieInfo *c, + const char *host, const char *path, + bool secure) +{ + struct Cookie *newco; + struct Cookie *co; + struct Cookie *mainco = NULL; + size_t matches = 0; + bool is_ip; + const size_t myhash = cookiehash(host); + + if(!c || !c->cookies[myhash]) + return NULL; /* no cookie struct or no cookies in the struct */ + + /* at first, remove expired cookies */ + remove_expired(c); + + /* check if host is an IP(v4|v6) address */ + is_ip = Curl_host_is_ipnum(host); + + co = c->cookies[myhash]; + + while(co) { + /* if the cookie requires we are secure we must only continue if we are! */ + if(co->secure?secure:TRUE) { + + /* now check if the domain is correct */ + if(!co->domain || + (co->tailmatch && !is_ip && + cookie_tailmatch(co->domain, strlen(co->domain), host)) || + ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) { + /* + * the right part of the host matches the domain stuff in the + * cookie data + */ + + /* + * now check the left part of the path with the cookies path + * requirement + */ + if(!co->spath || pathmatch(co->spath, path) ) { + + /* + * and now, we know this is a match and we should create an + * entry for the return-linked-list + */ + + newco = dup_cookie(co); + if(newco) { + /* then modify our next */ + newco->next = mainco; + + /* point the main to us */ + mainco = newco; + + matches++; + if(matches >= MAX_COOKIE_SEND_AMOUNT) { + infof(data, "Included max number of cookies (%zu) in request!", + matches); + break; + } + } + else + goto fail; + } + } + } + co = co->next; + } + + if(matches) { + /* + * Now we need to make sure that if there is a name appearing more than + * once, the longest specified path version comes first. To make this + * the swiftest way, we just sort them all based on path length. + */ + struct Cookie **array; + size_t i; + + /* alloc an array and store all cookie pointers */ + array = malloc(sizeof(struct Cookie *) * matches); + if(!array) + goto fail; + + co = mainco; + + for(i = 0; co; co = co->next) + array[i++] = co; + + /* now sort the cookie pointers in path length order */ + qsort(array, matches, sizeof(struct Cookie *), cookie_sort); + + /* remake the linked list order according to the new order */ + + mainco = array[0]; /* start here */ + for(i = 0; inext = array[i + 1]; + array[matches-1]->next = NULL; /* terminate the list */ + + free(array); /* remove the temporary data again */ + } + + return mainco; /* return the new list */ + +fail: + /* failure, clear up the allocated chain and return NULL */ + Curl_cookie_freelist(mainco); + return NULL; +} + +/* + * Curl_cookie_clearall + * + * Clear all existing cookies and reset the counter. + */ +void Curl_cookie_clearall(struct CookieInfo *cookies) +{ + if(cookies) { + unsigned int i; + for(i = 0; i < COOKIE_HASH_SIZE; i++) { + Curl_cookie_freelist(cookies->cookies[i]); + cookies->cookies[i] = NULL; + } + cookies->numcookies = 0; + } +} + +/* + * Curl_cookie_freelist + * + * Free a list of cookies previously returned by Curl_cookie_getlist(); + */ +void Curl_cookie_freelist(struct Cookie *co) +{ + struct Cookie *next; + while(co) { + next = co->next; + freecookie(co); + co = next; + } +} + +/* + * Curl_cookie_clearsess + * + * Free all session cookies in the cookies list. + */ +void Curl_cookie_clearsess(struct CookieInfo *cookies) +{ + struct Cookie *first, *curr, *next, *prev = NULL; + unsigned int i; + + if(!cookies) + return; + + for(i = 0; i < COOKIE_HASH_SIZE; i++) { + if(!cookies->cookies[i]) + continue; + + first = curr = prev = cookies->cookies[i]; + + for(; curr; curr = next) { + next = curr->next; + if(!curr->expires) { + if(first == curr) + first = next; + + if(prev == curr) + prev = next; + else + prev->next = next; + + freecookie(curr); + cookies->numcookies--; + } + else + prev = curr; + } + + cookies->cookies[i] = first; + } +} + +/* + * Curl_cookie_cleanup() + * + * Free a "cookie object" previous created with Curl_cookie_init(). + */ +void Curl_cookie_cleanup(struct CookieInfo *c) +{ + if(c) { + unsigned int i; + for(i = 0; i < COOKIE_HASH_SIZE; i++) + Curl_cookie_freelist(c->cookies[i]); + free(c); /* free the base struct as well */ + } +} + +/* + * get_netscape_format() + * + * Formats a string for Netscape output file, w/o a newline at the end. + * Function returns a char * to a formatted line. The caller is responsible + * for freeing the returned pointer. + */ +static char *get_netscape_format(const struct Cookie *co) +{ + return aprintf( + "%s" /* httponly preamble */ + "%s%s\t" /* domain */ + "%s\t" /* tailmatch */ + "%s\t" /* path */ + "%s\t" /* secure */ + "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */ + "%s\t" /* name */ + "%s", /* value */ + co->httponly?"#HttpOnly_":"", + /* + * Make sure all domains are prefixed with a dot if they allow + * tailmatching. This is Mozilla-style. + */ + (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"", + co->domain?co->domain:"unknown", + co->tailmatch?"TRUE":"FALSE", + co->path?co->path:"/", + co->secure?"TRUE":"FALSE", + co->expires, + co->name, + co->value?co->value:""); +} + +/* + * cookie_output() + * + * Writes all internally known cookies to the specified file. Specify + * "-" as filename to write to stdout. + * + * The function returns non-zero on write failure. + */ +static CURLcode cookie_output(struct Curl_easy *data, + struct CookieInfo *c, const char *filename) +{ + struct Cookie *co; + FILE *out = NULL; + bool use_stdout = FALSE; + char *tempstore = NULL; + CURLcode error = CURLE_OK; + + if(!c) + /* no cookie engine alive */ + return CURLE_OK; + + /* at first, remove expired cookies */ + remove_expired(c); + + if(!strcmp("-", filename)) { + /* use stdout */ + out = stdout; + use_stdout = TRUE; + } + else { + error = Curl_fopen(data, filename, &out, &tempstore); + if(error) + goto error; + } + + fputs("# Netscape HTTP Cookie File\n" + "# https://curl.se/docs/http-cookies.html\n" + "# This file was generated by libcurl! Edit at your own risk.\n\n", + out); + + if(c->numcookies) { + unsigned int i; + size_t nvalid = 0; + struct Cookie **array; + + array = calloc(1, sizeof(struct Cookie *) * c->numcookies); + if(!array) { + error = CURLE_OUT_OF_MEMORY; + goto error; + } + + /* only sort the cookies with a domain property */ + for(i = 0; i < COOKIE_HASH_SIZE; i++) { + for(co = c->cookies[i]; co; co = co->next) { + if(!co->domain) + continue; + array[nvalid++] = co; + } + } + + qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct); + + for(i = 0; i < nvalid; i++) { + char *format_ptr = get_netscape_format(array[i]); + if(!format_ptr) { + free(array); + error = CURLE_OUT_OF_MEMORY; + goto error; + } + fprintf(out, "%s\n", format_ptr); + free(format_ptr); + } + + free(array); + } + + if(!use_stdout) { + fclose(out); + out = NULL; + if(tempstore && Curl_rename(tempstore, filename)) { + unlink(tempstore); + error = CURLE_WRITE_ERROR; + goto error; + } + } + + /* + * If we reach here we have successfully written a cookie file so there is + * no need to inspect the error, any error case should have jumped into the + * error block below. + */ + free(tempstore); + return CURLE_OK; + +error: + if(out && !use_stdout) + fclose(out); + free(tempstore); + return error; +} + +static struct curl_slist *cookie_list(struct Curl_easy *data) +{ + struct curl_slist *list = NULL; + struct curl_slist *beg; + struct Cookie *c; + char *line; + unsigned int i; + + if(!data->cookies || (data->cookies->numcookies == 0)) + return NULL; + + for(i = 0; i < COOKIE_HASH_SIZE; i++) { + for(c = data->cookies->cookies[i]; c; c = c->next) { + if(!c->domain) + continue; + line = get_netscape_format(c); + if(!line) { + curl_slist_free_all(list); + return NULL; + } + beg = Curl_slist_append_nodup(list, line); + if(!beg) { + free(line); + curl_slist_free_all(list); + return NULL; + } + list = beg; + } + } + + return list; +} + +struct curl_slist *Curl_cookie_list(struct Curl_easy *data) +{ + struct curl_slist *list; + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + list = cookie_list(data); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + return list; +} + +void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) +{ + CURLcode res; + + if(data->set.str[STRING_COOKIEJAR]) { + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + + /* if we have a destination file for all the cookies to get dumped to */ + res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]); + if(res) + infof(data, "WARNING: failed to save cookies in %s: %s", + data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res)); + } + else { + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + } + + if(cleanup && (!data->share || (data->cookies != data->share->cookies))) { + Curl_cookie_cleanup(data->cookies); + data->cookies = NULL; + } + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); +} + +#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ diff --git a/third_party/curl/lib/cookie.h b/third_party/curl/lib/cookie.h new file mode 100644 index 0000000000..838d74d82f --- /dev/null +++ b/third_party/curl/lib/cookie.h @@ -0,0 +1,138 @@ +#ifndef HEADER_CURL_COOKIE_H +#define HEADER_CURL_COOKIE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include + +struct Cookie { + struct Cookie *next; /* next in the chain */ + char *name; /* = value */ + char *value; /* name = */ + char *path; /* path = which is in Set-Cookie: */ + char *spath; /* sanitized cookie path */ + char *domain; /* domain = */ + curl_off_t expires; /* expires = */ + bool tailmatch; /* whether we do tail-matching of the domain name */ + bool secure; /* whether the 'secure' keyword was used */ + bool livecookie; /* updated from a server, not a stored file */ + bool httponly; /* true if the httponly directive is present */ + int creationtime; /* time when the cookie was written */ + unsigned char prefix; /* bitmap fields indicating which prefix are set */ +}; + +/* + * Available cookie prefixes, as defined in + * draft-ietf-httpbis-rfc6265bis-02 + */ +#define COOKIE_PREFIX__SECURE (1<<0) +#define COOKIE_PREFIX__HOST (1<<1) + +#define COOKIE_HASH_SIZE 63 + +struct CookieInfo { + /* linked list of cookies we know of */ + struct Cookie *cookies[COOKIE_HASH_SIZE]; + curl_off_t next_expiration; /* the next time at which expiration happens */ + int numcookies; /* number of cookies in the "jar" */ + int lastct; /* last creation-time used in the jar */ + bool running; /* state info, for cookie adding information */ + bool newsession; /* new session, discard session cookies on load */ +}; + +/* The maximum sizes we accept for cookies. RFC 6265 section 6.1 says + "general-use user agents SHOULD provide each of the following minimum + capabilities": + + - At least 4096 bytes per cookie (as measured by the sum of the length of + the cookie's name, value, and attributes). + In the 6265bis draft document section 5.4 it is phrased even stronger: "If + the sum of the lengths of the name string and the value string is more than + 4096 octets, abort these steps and ignore the set-cookie-string entirely." +*/ + +/** Limits for INCOMING cookies **/ + +/* The longest we allow a line to be when reading a cookie from an HTTP header + or from a cookie jar */ +#define MAX_COOKIE_LINE 5000 + +/* Maximum length of an incoming cookie name or content we deal with. Longer + cookies are ignored. */ +#define MAX_NAME 4096 + +/* Maximum number of Set-Cookie: lines accepted in a single response. If more + such header lines are received, they are ignored. This value must be less + than 256 since an unsigned char is used to count. */ +#define MAX_SET_COOKIE_AMOUNT 50 + +/** Limits for OUTGOING cookies **/ + +/* Maximum size for an outgoing cookie line libcurl will use in an http + request. This is the default maximum length used in some versions of Apache + httpd. */ +#define MAX_COOKIE_HEADER_LEN 8190 + +/* Maximum number of cookies libcurl will send in a single request, even if + there might be more cookies that match. One reason to cap the number is to + keep the maximum HTTP request within the maximum allowed size. */ +#define MAX_COOKIE_SEND_AMOUNT 150 + +struct Curl_easy; +/* + * Add a cookie to the internal list of cookies. The domain and path arguments + * are only used if the header boolean is TRUE. + */ + +struct Cookie *Curl_cookie_add(struct Curl_easy *data, + struct CookieInfo *c, bool header, + bool noexpiry, const char *lineptr, + const char *domain, const char *path, + bool secure); + +struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, + struct CookieInfo *c, const char *host, + const char *path, bool secure); +void Curl_cookie_freelist(struct Cookie *cookies); +void Curl_cookie_clearall(struct CookieInfo *cookies); +void Curl_cookie_clearsess(struct CookieInfo *cookies); + +#if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_COOKIES) +#define Curl_cookie_list(x) NULL +#define Curl_cookie_loadfiles(x) Curl_nop_stmt +#define Curl_cookie_init(x,y,z,w) NULL +#define Curl_cookie_cleanup(x) Curl_nop_stmt +#define Curl_flush_cookies(x,y) Curl_nop_stmt +#else +void Curl_flush_cookies(struct Curl_easy *data, bool cleanup); +void Curl_cookie_cleanup(struct CookieInfo *c); +struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, + const char *file, struct CookieInfo *inc, + bool newsession); +struct curl_slist *Curl_cookie_list(struct Curl_easy *data); +void Curl_cookie_loadfiles(struct Curl_easy *data); +#endif + +#endif /* HEADER_CURL_COOKIE_H */ diff --git a/third_party/curl/lib/curl_addrinfo.c b/third_party/curl/lib/curl_addrinfo.c new file mode 100644 index 0000000000..d290d7da10 --- /dev/null +++ b/third_party/curl/lib/curl_addrinfo.c @@ -0,0 +1,600 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETINET_IN6_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_SYS_UN_H +# include +#endif + +#ifdef __VMS +# include +# include +#endif + +#include + +#include "curl_addrinfo.h" +#include "inet_pton.h" +#include "warnless.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_freeaddrinfo() + * + * This is used to free a linked list of Curl_addrinfo structs along + * with all its associated allocated storage. This function should be + * called once for each successful call to Curl_getaddrinfo_ex() or to + * any function call which actually allocates a Curl_addrinfo struct. + */ + +#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ + defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) + /* workaround icc 9.1 optimizer issue */ +# define vqualifier volatile +#else +# define vqualifier +#endif + +void +Curl_freeaddrinfo(struct Curl_addrinfo *cahead) +{ + struct Curl_addrinfo *vqualifier canext; + struct Curl_addrinfo *ca; + + for(ca = cahead; ca; ca = canext) { + canext = ca->ai_next; + free(ca); + } +} + + +#ifdef HAVE_GETADDRINFO +/* + * Curl_getaddrinfo_ex() + * + * This is a wrapper function around system's getaddrinfo(), with + * the only difference that instead of returning a linked list of + * addrinfo structs this one returns a linked list of Curl_addrinfo + * ones. The memory allocated by this function *MUST* be free'd with + * Curl_freeaddrinfo(). For each successful call to this function + * there must be an associated call later to Curl_freeaddrinfo(). + * + * There should be no single call to system's getaddrinfo() in the + * whole library, any such call should be 'routed' through this one. + */ + +int +Curl_getaddrinfo_ex(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct Curl_addrinfo **result) +{ + const struct addrinfo *ai; + struct addrinfo *aihead; + struct Curl_addrinfo *cafirst = NULL; + struct Curl_addrinfo *calast = NULL; + struct Curl_addrinfo *ca; + size_t ss_size; + int error; + + *result = NULL; /* assume failure */ + + error = getaddrinfo(nodename, servname, hints, &aihead); + if(error) + return error; + + /* traverse the addrinfo list */ + + for(ai = aihead; ai != NULL; ai = ai->ai_next) { + size_t namelen = ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0; + /* ignore elements with unsupported address family, */ + /* settle family-specific sockaddr structure size. */ + if(ai->ai_family == AF_INET) + ss_size = sizeof(struct sockaddr_in); +#ifdef USE_IPV6 + else if(ai->ai_family == AF_INET6) + ss_size = sizeof(struct sockaddr_in6); +#endif + else + continue; + + /* ignore elements without required address info */ + if(!ai->ai_addr || !(ai->ai_addrlen > 0)) + continue; + + /* ignore elements with bogus address size */ + if((size_t)ai->ai_addrlen < ss_size) + continue; + + ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen); + if(!ca) { + error = EAI_MEMORY; + break; + } + + /* copy each structure member individually, member ordering, */ + /* size, or padding might be different for each platform. */ + + ca->ai_flags = ai->ai_flags; + ca->ai_family = ai->ai_family; + ca->ai_socktype = ai->ai_socktype; + ca->ai_protocol = ai->ai_protocol; + ca->ai_addrlen = (curl_socklen_t)ss_size; + ca->ai_addr = NULL; + ca->ai_canonname = NULL; + ca->ai_next = NULL; + + ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); + memcpy(ca->ai_addr, ai->ai_addr, ss_size); + + if(namelen) { + ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size); + memcpy(ca->ai_canonname, ai->ai_canonname, namelen); + } + + /* if the return list is empty, this becomes the first element */ + if(!cafirst) + cafirst = ca; + + /* add this element last in the return list */ + if(calast) + calast->ai_next = ca; + calast = ca; + + } + + /* destroy the addrinfo list */ + if(aihead) + freeaddrinfo(aihead); + + /* if we failed, also destroy the Curl_addrinfo list */ + if(error) { + Curl_freeaddrinfo(cafirst); + cafirst = NULL; + } + else if(!cafirst) { +#ifdef EAI_NONAME + /* rfc3493 conformant */ + error = EAI_NONAME; +#else + /* rfc3493 obsoleted */ + error = EAI_NODATA; +#endif +#ifdef USE_WINSOCK + SET_SOCKERRNO(error); +#endif + } + + *result = cafirst; + + /* This is not a CURLcode */ + return error; +} +#endif /* HAVE_GETADDRINFO */ + + +/* + * Curl_he2ai() + * + * This function returns a pointer to the first element of a newly allocated + * Curl_addrinfo struct linked list filled with the data of a given hostent. + * Curl_addrinfo is meant to work like the addrinfo struct does for a IPv6 + * stack, but usable also for IPv4, all hosts and environments. + * + * The memory allocated by this function *MUST* be free'd later on calling + * Curl_freeaddrinfo(). For each successful call to this function there + * must be an associated call later to Curl_freeaddrinfo(). + * + * Curl_addrinfo defined in "lib/curl_addrinfo.h" + * + * struct Curl_addrinfo { + * int ai_flags; + * int ai_family; + * int ai_socktype; + * int ai_protocol; + * curl_socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo * + * char *ai_canonname; + * struct sockaddr *ai_addr; + * struct Curl_addrinfo *ai_next; + * }; + * + * hostent defined in + * + * struct hostent { + * char *h_name; + * char **h_aliases; + * int h_addrtype; + * int h_length; + * char **h_addr_list; + * }; + * + * for backward compatibility: + * + * #define h_addr h_addr_list[0] + */ + +struct Curl_addrinfo * +Curl_he2ai(const struct hostent *he, int port) +{ + struct Curl_addrinfo *ai; + struct Curl_addrinfo *prevai = NULL; + struct Curl_addrinfo *firstai = NULL; + struct sockaddr_in *addr; +#ifdef USE_IPV6 + struct sockaddr_in6 *addr6; +#endif + CURLcode result = CURLE_OK; + int i; + char *curr; + + if(!he) + /* no input == no output! */ + return NULL; + + DEBUGASSERT((he->h_name != NULL) && (he->h_addr_list != NULL)); + + for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) { + size_t ss_size; + size_t namelen = strlen(he->h_name) + 1; /* include null-terminator */ +#ifdef USE_IPV6 + if(he->h_addrtype == AF_INET6) + ss_size = sizeof(struct sockaddr_in6); + else +#endif + ss_size = sizeof(struct sockaddr_in); + + /* allocate memory to hold the struct, the address and the name */ + ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + namelen); + if(!ai) { + result = CURLE_OUT_OF_MEMORY; + break; + } + /* put the address after the struct */ + ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); + /* then put the name after the address */ + ai->ai_canonname = (char *)ai->ai_addr + ss_size; + memcpy(ai->ai_canonname, he->h_name, namelen); + + if(!firstai) + /* store the pointer we want to return from this function */ + firstai = ai; + + if(prevai) + /* make the previous entry point to this */ + prevai->ai_next = ai; + + ai->ai_family = he->h_addrtype; + + /* we return all names as STREAM, so when using this address for TFTP + the type must be ignored and conn->socktype be used instead! */ + ai->ai_socktype = SOCK_STREAM; + + ai->ai_addrlen = (curl_socklen_t)ss_size; + + /* leave the rest of the struct filled with zero */ + + switch(ai->ai_family) { + case AF_INET: + addr = (void *)ai->ai_addr; /* storage area for this info */ + + memcpy(&addr->sin_addr, curr, sizeof(struct in_addr)); +#ifdef __MINGW32__ + addr->sin_family = (short)(he->h_addrtype); +#else + addr->sin_family = (CURL_SA_FAMILY_T)(he->h_addrtype); +#endif + addr->sin_port = htons((unsigned short)port); + break; + +#ifdef USE_IPV6 + case AF_INET6: + addr6 = (void *)ai->ai_addr; /* storage area for this info */ + + memcpy(&addr6->sin6_addr, curr, sizeof(struct in6_addr)); +#ifdef __MINGW32__ + addr6->sin6_family = (short)(he->h_addrtype); +#else + addr6->sin6_family = (CURL_SA_FAMILY_T)(he->h_addrtype); +#endif + addr6->sin6_port = htons((unsigned short)port); + break; +#endif + } + + prevai = ai; + } + + if(result) { + Curl_freeaddrinfo(firstai); + firstai = NULL; + } + + return firstai; +} + + +struct namebuff { + struct hostent hostentry; + union { + struct in_addr ina4; +#ifdef USE_IPV6 + struct in6_addr ina6; +#endif + } addrentry; + char *h_addr_list[2]; +}; + + +/* + * Curl_ip2addr() + * + * This function takes an Internet address, in binary form, as input parameter + * along with its address family and the string version of the address, and it + * returns a Curl_addrinfo chain filled in correctly with information for the + * given address/host + */ + +struct Curl_addrinfo * +Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port) +{ + struct Curl_addrinfo *ai; + +#if defined(__VMS) && \ + defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64) +#pragma pointer_size save +#pragma pointer_size short +#pragma message disable PTRMISMATCH +#endif + + struct hostent *h; + struct namebuff *buf; + char *addrentry; + char *hoststr; + size_t addrsize; + + DEBUGASSERT(inaddr && hostname); + + buf = malloc(sizeof(struct namebuff)); + if(!buf) + return NULL; + + hoststr = strdup(hostname); + if(!hoststr) { + free(buf); + return NULL; + } + + switch(af) { + case AF_INET: + addrsize = sizeof(struct in_addr); + addrentry = (void *)&buf->addrentry.ina4; + memcpy(addrentry, inaddr, sizeof(struct in_addr)); + break; +#ifdef USE_IPV6 + case AF_INET6: + addrsize = sizeof(struct in6_addr); + addrentry = (void *)&buf->addrentry.ina6; + memcpy(addrentry, inaddr, sizeof(struct in6_addr)); + break; +#endif + default: + free(hoststr); + free(buf); + return NULL; + } + + h = &buf->hostentry; + h->h_name = hoststr; + h->h_aliases = NULL; + h->h_addrtype = (short)af; + h->h_length = (short)addrsize; + h->h_addr_list = &buf->h_addr_list[0]; + h->h_addr_list[0] = addrentry; + h->h_addr_list[1] = NULL; /* terminate list of entries */ + +#if defined(__VMS) && \ + defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64) +#pragma pointer_size restore +#pragma message enable PTRMISMATCH +#endif + + ai = Curl_he2ai(h, port); + + free(hoststr); + free(buf); + + return ai; +} + +/* + * Given an IPv4 or IPv6 dotted string address, this converts it to a proper + * allocated Curl_addrinfo struct and returns it. + */ +struct Curl_addrinfo *Curl_str2addr(char *address, int port) +{ + struct in_addr in; + if(Curl_inet_pton(AF_INET, address, &in) > 0) + /* This is a dotted IP address 123.123.123.123-style */ + return Curl_ip2addr(AF_INET, &in, address, port); +#ifdef USE_IPV6 + { + struct in6_addr in6; + if(Curl_inet_pton(AF_INET6, address, &in6) > 0) + /* This is a dotted IPv6 address ::1-style */ + return Curl_ip2addr(AF_INET6, &in6, address, port); + } +#endif + return NULL; /* bad input format */ +} + +#ifdef USE_UNIX_SOCKETS +/** + * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo + * struct initialized with this path. + * Set '*longpath' to TRUE if the error is a too long path. + */ +struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, + bool abstract) +{ + struct Curl_addrinfo *ai; + struct sockaddr_un *sa_un; + size_t path_len; + + *longpath = FALSE; + + ai = calloc(1, sizeof(struct Curl_addrinfo) + sizeof(struct sockaddr_un)); + if(!ai) + return NULL; + ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); + + sa_un = (void *) ai->ai_addr; + sa_un->sun_family = AF_UNIX; + + /* sun_path must be able to store the NUL-terminated path */ + path_len = strlen(path) + 1; + if(path_len > sizeof(sa_un->sun_path)) { + free(ai); + *longpath = TRUE; + return NULL; + } + + ai->ai_family = AF_UNIX; + ai->ai_socktype = SOCK_STREAM; /* assume reliable transport for HTTP */ + ai->ai_addrlen = (curl_socklen_t) + ((offsetof(struct sockaddr_un, sun_path) + path_len) & 0x7FFFFFFF); + + /* Abstract Unix domain socket have NULL prefix instead of suffix */ + if(abstract) + memcpy(sa_un->sun_path + 1, path, path_len - 1); + else + memcpy(sa_un->sun_path, path, path_len); /* copy NUL byte */ + + return ai; +} +#endif + +#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \ + defined(HAVE_FREEADDRINFO) +/* + * curl_dbg_freeaddrinfo() + * + * This is strictly for memory tracing and are using the same style as the + * family otherwise present in memdebug.c. I put these ones here since they + * require a bunch of structs I did not want to include in memdebug.c + */ + +void +curl_dbg_freeaddrinfo(struct addrinfo *freethis, + int line, const char *source) +{ + curl_dbg_log("ADDR %s:%d freeaddrinfo(%p)\n", + source, line, (void *)freethis); +#ifdef USE_LWIPSOCK + lwip_freeaddrinfo(freethis); +#else + (freeaddrinfo)(freethis); +#endif +} +#endif /* defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO) */ + + +#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) +/* + * curl_dbg_getaddrinfo() + * + * This is strictly for memory tracing and are using the same style as the + * family otherwise present in memdebug.c. I put these ones here since they + * require a bunch of structs I did not want to include in memdebug.c + */ + +int +curl_dbg_getaddrinfo(const char *hostname, + const char *service, + const struct addrinfo *hints, + struct addrinfo **result, + int line, const char *source) +{ +#ifdef USE_LWIPSOCK + int res = lwip_getaddrinfo(hostname, service, hints, result); +#else + int res = (getaddrinfo)(hostname, service, hints, result); +#endif + if(0 == res) + /* success */ + curl_dbg_log("ADDR %s:%d getaddrinfo() = %p\n", + source, line, (void *)*result); + else + curl_dbg_log("ADDR %s:%d getaddrinfo() failed\n", + source, line); + return res; +} +#endif /* defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) */ + +#if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS) +/* + * Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and Mac OS X + * 10.11.5. + */ +void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) +{ + struct Curl_addrinfo *ca; + struct sockaddr_in *addr; +#ifdef USE_IPV6 + struct sockaddr_in6 *addr6; +#endif + for(ca = addrinfo; ca != NULL; ca = ca->ai_next) { + switch(ca->ai_family) { + case AF_INET: + addr = (void *)ca->ai_addr; /* storage area for this info */ + addr->sin_port = htons((unsigned short)port); + break; + +#ifdef USE_IPV6 + case AF_INET6: + addr6 = (void *)ca->ai_addr; /* storage area for this info */ + addr6->sin6_port = htons((unsigned short)port); + break; +#endif + } + } +} +#endif diff --git a/third_party/curl/lib/curl_addrinfo.h b/third_party/curl/lib/curl_addrinfo.h new file mode 100644 index 0000000000..9ceac997da --- /dev/null +++ b/third_party/curl/lib/curl_addrinfo.h @@ -0,0 +1,108 @@ +#ifndef HEADER_CURL_ADDRINFO_H +#define HEADER_CURL_ADDRINFO_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#ifdef __VMS +# include +# include +# include +#endif + +/* + * Curl_addrinfo is our internal struct definition that we use to allow + * consistent internal handling of this data. We use this even when the system + * provides an addrinfo structure definition. We use this for all sorts of + * IPv4 and IPV6 builds. + */ + +struct Curl_addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + curl_socklen_t ai_addrlen; /* Follow rfc3493 struct addrinfo */ + char *ai_canonname; + struct sockaddr *ai_addr; + struct Curl_addrinfo *ai_next; +}; + +void +Curl_freeaddrinfo(struct Curl_addrinfo *cahead); + +#ifdef HAVE_GETADDRINFO +int +Curl_getaddrinfo_ex(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct Curl_addrinfo **result); +#endif + +struct Curl_addrinfo * +Curl_he2ai(const struct hostent *he, int port); + +struct Curl_addrinfo * +Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port); + +struct Curl_addrinfo *Curl_str2addr(char *dotted, int port); + +#ifdef USE_UNIX_SOCKETS +struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, + bool abstract); +#endif + +#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \ + defined(HAVE_FREEADDRINFO) +void +curl_dbg_freeaddrinfo(struct addrinfo *freethis, int line, const char *source); +#endif + +#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) +int +curl_dbg_getaddrinfo(const char *hostname, const char *service, + const struct addrinfo *hints, struct addrinfo **result, + int line, const char *source); +#endif + +#ifdef HAVE_GETADDRINFO +#ifdef USE_RESOLVE_ON_IPS +void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port); +#else +#define Curl_addrinfo_set_port(x,y) +#endif +#endif + +#endif /* HEADER_CURL_ADDRINFO_H */ diff --git a/third_party/curl/lib/curl_base64.h b/third_party/curl/lib/curl_base64.h new file mode 100644 index 0000000000..7f7cd1d98a --- /dev/null +++ b/third_party/curl/lib/curl_base64.h @@ -0,0 +1,41 @@ +#ifndef HEADER_CURL_BASE64_H +#define HEADER_CURL_BASE64_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#ifndef BUILDING_LIBCURL +/* this renames functions so that the tool code can use the same code + without getting symbol collisions */ +#define Curl_base64_encode(a,b,c,d) curlx_base64_encode(a,b,c,d) +#define Curl_base64url_encode(a,b,c,d) curlx_base64url_encode(a,b,c,d) +#define Curl_base64_decode(a,b,c) curlx_base64_decode(a,b,c) +#endif + +CURLcode Curl_base64_encode(const char *inputbuff, size_t insize, + char **outptr, size_t *outlen); +CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize, + char **outptr, size_t *outlen); +CURLcode Curl_base64_decode(const char *src, + unsigned char **outptr, size_t *outlen); +#endif /* HEADER_CURL_BASE64_H */ diff --git a/third_party/curl/lib/curl_config.h.cmake b/third_party/curl/lib/curl_config.h.cmake new file mode 100644 index 0000000000..631ee27b33 --- /dev/null +++ b/third_party/curl/lib/curl_config.h.cmake @@ -0,0 +1,819 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* lib/curl_config.h.in. Generated somehow by cmake. */ + +/* Location of default ca bundle */ +#cmakedefine CURL_CA_BUNDLE "${CURL_CA_BUNDLE}" + +/* define "1" to use built-in ca store of TLS backend */ +#cmakedefine CURL_CA_FALLBACK 1 + +/* Location of default ca path */ +#cmakedefine CURL_CA_PATH "${CURL_CA_PATH}" + +/* Default SSL backend */ +#cmakedefine CURL_DEFAULT_SSL_BACKEND "${CURL_DEFAULT_SSL_BACKEND}" + +/* disables alt-svc */ +#cmakedefine CURL_DISABLE_ALTSVC 1 + +/* disables cookies support */ +#cmakedefine CURL_DISABLE_COOKIES 1 + +/* disables Basic authentication */ +#cmakedefine CURL_DISABLE_BASIC_AUTH 1 + +/* disables Bearer authentication */ +#cmakedefine CURL_DISABLE_BEARER_AUTH 1 + +/* disables Digest authentication */ +#cmakedefine CURL_DISABLE_DIGEST_AUTH 1 + +/* disables Kerberos authentication */ +#cmakedefine CURL_DISABLE_KERBEROS_AUTH 1 + +/* disables negotiate authentication */ +#cmakedefine CURL_DISABLE_NEGOTIATE_AUTH 1 + +/* disables AWS-SIG4 */ +#cmakedefine CURL_DISABLE_AWS 1 + +/* disables DICT */ +#cmakedefine CURL_DISABLE_DICT 1 + +/* disables DNS-over-HTTPS */ +#cmakedefine CURL_DISABLE_DOH 1 + +/* disables FILE */ +#cmakedefine CURL_DISABLE_FILE 1 + +/* disables form api */ +#cmakedefine CURL_DISABLE_FORM_API 1 + +/* disables FTP */ +#cmakedefine CURL_DISABLE_FTP 1 + +/* disables curl_easy_options API for existing options to curl_easy_setopt */ +#cmakedefine CURL_DISABLE_GETOPTIONS 1 + +/* disables GOPHER */ +#cmakedefine CURL_DISABLE_GOPHER 1 + +/* disables headers-api support */ +#cmakedefine CURL_DISABLE_HEADERS_API 1 + +/* disables HSTS support */ +#cmakedefine CURL_DISABLE_HSTS 1 + +/* disables HTTP */ +#cmakedefine CURL_DISABLE_HTTP 1 + +/* disables IMAP */ +#cmakedefine CURL_DISABLE_IMAP 1 + +/* disables LDAP */ +#cmakedefine CURL_DISABLE_LDAP 1 + +/* disables LDAPS */ +#cmakedefine CURL_DISABLE_LDAPS 1 + +/* disables --libcurl option from the curl tool */ +#cmakedefine CURL_DISABLE_LIBCURL_OPTION 1 + +/* disables MIME support */ +#cmakedefine CURL_DISABLE_MIME 1 + +/* disables local binding support */ +#cmakedefine CURL_DISABLE_BINDLOCAL 1 + +/* disables MQTT */ +#cmakedefine CURL_DISABLE_MQTT 1 + +/* disables netrc parser */ +#cmakedefine CURL_DISABLE_NETRC 1 + +/* disables NTLM support */ +#cmakedefine CURL_DISABLE_NTLM 1 + +/* disables date parsing */ +#cmakedefine CURL_DISABLE_PARSEDATE 1 + +/* disables POP3 */ +#cmakedefine CURL_DISABLE_POP3 1 + +/* disables built-in progress meter */ +#cmakedefine CURL_DISABLE_PROGRESS_METER 1 + +/* disables proxies */ +#cmakedefine CURL_DISABLE_PROXY 1 + +/* disables RTSP */ +#cmakedefine CURL_DISABLE_RTSP 1 + +/* disables SMB */ +#cmakedefine CURL_DISABLE_SMB 1 + +/* disables SMTP */ +#cmakedefine CURL_DISABLE_SMTP 1 + +/* disables use of socketpair for curl_multi_poll */ +#cmakedefine CURL_DISABLE_SOCKETPAIR 1 + +/* disables TELNET */ +#cmakedefine CURL_DISABLE_TELNET 1 + +/* disables TFTP */ +#cmakedefine CURL_DISABLE_TFTP 1 + +/* disables verbose strings */ +#cmakedefine CURL_DISABLE_VERBOSE_STRINGS 1 + +/* to make a symbol visible */ +#cmakedefine CURL_EXTERN_SYMBOL ${CURL_EXTERN_SYMBOL} +/* Ensure using CURL_EXTERN_SYMBOL is possible */ +#ifndef CURL_EXTERN_SYMBOL +#define CURL_EXTERN_SYMBOL +#endif + +/* Allow SMB to work on Windows */ +#cmakedefine USE_WIN32_CRYPTO 1 + +/* Use Windows LDAP implementation */ +#cmakedefine USE_WIN32_LDAP 1 + +/* Define if you want to enable IPv6 support */ +#cmakedefine USE_IPV6 1 + +/* Define to 1 if you have the alarm function. */ +#cmakedefine HAVE_ALARM 1 + +/* Define to 1 if you have the arc4random function. */ +#cmakedefine HAVE_ARC4RANDOM 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have _Atomic support. */ +#cmakedefine HAVE_ATOMIC 1 + +/* Define to 1 if you have the `fnmatch' function. */ +#cmakedefine HAVE_FNMATCH 1 + +/* Define to 1 if you have the `basename' function. */ +#cmakedefine HAVE_BASENAME 1 + +/* Define to 1 if bool is an available type. */ +#cmakedefine HAVE_BOOL_T 1 + +/* Define to 1 if you have the __builtin_available function. */ +#cmakedefine HAVE_BUILTIN_AVAILABLE 1 + +/* Define to 1 if you have the clock_gettime function and monotonic timer. */ +#cmakedefine HAVE_CLOCK_GETTIME_MONOTONIC 1 + +/* Define to 1 if you have the clock_gettime function and raw monotonic timer. + */ +#cmakedefine HAVE_CLOCK_GETTIME_MONOTONIC_RAW 1 + +/* Define to 1 if you have the `closesocket' function. */ +#cmakedefine HAVE_CLOSESOCKET 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DIRENT_H 1 + +/* Define to 1 if you have the `opendir' function. */ +#cmakedefine HAVE_OPENDIR 1 + +/* Define to 1 if you have the fcntl function. */ +#cmakedefine HAVE_FCNTL 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_FCNTL_H 1 + +/* Define to 1 if you have a working fcntl O_NONBLOCK function. */ +#cmakedefine HAVE_FCNTL_O_NONBLOCK 1 + +/* Define to 1 if you have the freeaddrinfo function. */ +#cmakedefine HAVE_FREEADDRINFO 1 + +/* Define to 1 if you have the fseeko function. */ +#cmakedefine HAVE_FSEEKO 1 + +/* Define to 1 if you have the fseeko declaration. */ +#cmakedefine HAVE_DECL_FSEEKO 1 + +/* Define to 1 if you have the _fseeki64 function. */ +#cmakedefine HAVE__FSEEKI64 1 + +/* Define to 1 if you have the ftruncate function. */ +#cmakedefine HAVE_FTRUNCATE 1 + +/* Define to 1 if you have a working getaddrinfo function. */ +#cmakedefine HAVE_GETADDRINFO 1 + +/* Define to 1 if the getaddrinfo function is threadsafe. */ +#cmakedefine HAVE_GETADDRINFO_THREADSAFE 1 + +/* Define to 1 if you have the `geteuid' function. */ +#cmakedefine HAVE_GETEUID 1 + +/* Define to 1 if you have the `getppid' function. */ +#cmakedefine HAVE_GETPPID 1 + +/* Define to 1 if you have the gethostbyname_r function. */ +#cmakedefine HAVE_GETHOSTBYNAME_R 1 + +/* gethostbyname_r() takes 3 args */ +#cmakedefine HAVE_GETHOSTBYNAME_R_3 1 + +/* gethostbyname_r() takes 5 args */ +#cmakedefine HAVE_GETHOSTBYNAME_R_5 1 + +/* gethostbyname_r() takes 6 args */ +#cmakedefine HAVE_GETHOSTBYNAME_R_6 1 + +/* Define to 1 if you have the gethostname function. */ +#cmakedefine HAVE_GETHOSTNAME 1 + +/* Define to 1 if you have a working getifaddrs function. */ +#cmakedefine HAVE_GETIFADDRS 1 + +/* Define to 1 if you have the `getpass_r' function. */ +#cmakedefine HAVE_GETPASS_R 1 + +/* Define to 1 if you have the `getpeername' function. */ +#cmakedefine HAVE_GETPEERNAME 1 + +/* Define to 1 if you have the `getsockname' function. */ +#cmakedefine HAVE_GETSOCKNAME 1 + +/* Define to 1 if you have the `if_nametoindex' function. */ +#cmakedefine HAVE_IF_NAMETOINDEX 1 + +/* Define to 1 if you have the `getpwuid' function. */ +#cmakedefine HAVE_GETPWUID 1 + +/* Define to 1 if you have the `getpwuid_r' function. */ +#cmakedefine HAVE_GETPWUID_R 1 + +/* Define to 1 if you have the `getrlimit' function. */ +#cmakedefine HAVE_GETRLIMIT 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have a working glibc-style strerror_r function. */ +#cmakedefine HAVE_GLIBC_STRERROR_R 1 + +/* Define to 1 if you have a working gmtime_r function. */ +#cmakedefine HAVE_GMTIME_R 1 + +/* if you have the gssapi libraries */ +#cmakedefine HAVE_GSSAPI 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_GSSAPI_GSSAPI_GENERIC_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_GSSAPI_GSSAPI_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_GSSAPI_GSSAPI_KRB5_H 1 + +/* if you have the GNU gssapi libraries */ +#cmakedefine HAVE_GSSGNU 1 + +/* Define to 1 if you have the `idna_strerror' function. */ +#cmakedefine HAVE_IDNA_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_IFADDRS_H 1 + +/* Define to 1 if you have a IPv6 capable working inet_ntop function. */ +#cmakedefine HAVE_INET_NTOP 1 + +/* Define to 1 if you have a IPv6 capable working inet_pton function. */ +#cmakedefine HAVE_INET_PTON 1 + +/* Define to 1 if symbol `sa_family_t' exists */ +#cmakedefine HAVE_SA_FAMILY_T 1 + +/* Define to 1 if symbol `ADDRESS_FAMILY' exists */ +#cmakedefine HAVE_ADDRESS_FAMILY 1 + +/* Define to 1 if you have the ioctlsocket function. */ +#cmakedefine HAVE_IOCTLSOCKET 1 + +/* Define to 1 if you have the IoctlSocket camel case function. */ +#cmakedefine HAVE_IOCTLSOCKET_CAMEL 1 + +/* Define to 1 if you have a working IoctlSocket camel case FIONBIO function. + */ +#cmakedefine HAVE_IOCTLSOCKET_CAMEL_FIONBIO 1 + +/* Define to 1 if you have a working ioctlsocket FIONBIO function. */ +#cmakedefine HAVE_IOCTLSOCKET_FIONBIO 1 + +/* Define to 1 if you have a working ioctl FIONBIO function. */ +#cmakedefine HAVE_IOCTL_FIONBIO 1 + +/* Define to 1 if you have a working ioctl SIOCGIFADDR function. */ +#cmakedefine HAVE_IOCTL_SIOCGIFADDR 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_IO_H 1 + +/* Define to 1 if you have the lber.h header file. */ +#cmakedefine HAVE_LBER_H 1 + +/* Define to 1 if you have the ldap.h header file. */ +#cmakedefine HAVE_LDAP_H 1 + +/* Use LDAPS implementation */ +#cmakedefine HAVE_LDAP_SSL 1 + +/* Define to 1 if you have the ldap_ssl.h header file. */ +#cmakedefine HAVE_LDAP_SSL_H 1 + +/* Define to 1 if you have the `ldap_url_parse' function. */ +#cmakedefine HAVE_LDAP_URL_PARSE 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LIBGEN_H 1 + +/* Define to 1 if you have the `idn2' library (-lidn2). */ +#cmakedefine HAVE_LIBIDN2 1 + +/* Define to 1 if you have the idn2.h header file. */ +#cmakedefine HAVE_IDN2_H 1 + +/* if zlib is available */ +#cmakedefine HAVE_LIBZ 1 + +/* if brotli is available */ +#cmakedefine HAVE_BROTLI 1 + +/* if zstd is available */ +#cmakedefine HAVE_ZSTD 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LOCALE_H 1 + +/* Define to 1 if the compiler supports the 'long long' data type. */ +#cmakedefine HAVE_LONGLONG 1 + +/* Define to 1 if you have the 'suseconds_t' data type. */ +#cmakedefine HAVE_SUSECONDS_T 1 + +/* Define to 1 if you have the MSG_NOSIGNAL flag. */ +#cmakedefine HAVE_MSG_NOSIGNAL 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETINET_TCP_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETINET_UDP_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LINUX_TCP_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NET_IF_H 1 + +/* if you have an old MIT gssapi library, lacking GSS_C_NT_HOSTBASED_SERVICE */ +#cmakedefine HAVE_OLD_GSSMIT 1 + +/* Define to 1 if you have the `pipe' function. */ +#cmakedefine HAVE_PIPE 1 + +/* Define to 1 if you have the `eventfd' function. */ +#cmakedefine HAVE_EVENTFD 1 + +/* If you have a fine poll */ +#cmakedefine HAVE_POLL_FINE 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_POLL_H 1 + +/* Define to 1 if you have a working POSIX-style strerror_r function. */ +#cmakedefine HAVE_POSIX_STRERROR_R 1 + +/* Define to 1 if you have the header file */ +#cmakedefine HAVE_PTHREAD_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_PWD_H 1 + +/* Define to 1 if OpenSSL has the `SSL_set0_wbio` function. */ +#cmakedefine HAVE_SSL_SET0_WBIO 1 + +/* Define to 1 if you have the recv function. */ +#cmakedefine HAVE_RECV 1 + +/* Define to 1 if you have the select function. */ +#cmakedefine HAVE_SELECT 1 + +/* Define to 1 if you have the sched_yield function. */ +#cmakedefine HAVE_SCHED_YIELD 1 + +/* Define to 1 if you have the send function. */ +#cmakedefine HAVE_SEND 1 + +/* Define to 1 if you have the sendmsg function. */ +#cmakedefine HAVE_SENDMSG 1 + +/* Define to 1 if you have the 'fsetxattr' function. */ +#cmakedefine HAVE_FSETXATTR 1 + +/* fsetxattr() takes 5 args */ +#cmakedefine HAVE_FSETXATTR_5 1 + +/* fsetxattr() takes 6 args */ +#cmakedefine HAVE_FSETXATTR_6 1 + +/* Define to 1 if you have the `setlocale' function. */ +#cmakedefine HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `setmode' function. */ +#cmakedefine HAVE_SETMODE 1 + +/* Define to 1 if you have the `setrlimit' function. */ +#cmakedefine HAVE_SETRLIMIT 1 + +/* Define to 1 if you have a working setsockopt SO_NONBLOCK function. */ +#cmakedefine HAVE_SETSOCKOPT_SO_NONBLOCK 1 + +/* Define to 1 if you have the sigaction function. */ +#cmakedefine HAVE_SIGACTION 1 + +/* Define to 1 if you have the siginterrupt function. */ +#cmakedefine HAVE_SIGINTERRUPT 1 + +/* Define to 1 if you have the signal function. */ +#cmakedefine HAVE_SIGNAL 1 + +/* Define to 1 if you have the sigsetjmp function or macro. */ +#cmakedefine HAVE_SIGSETJMP 1 + +/* Define to 1 if you have the `snprintf' function. */ +#cmakedefine HAVE_SNPRINTF 1 + +/* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */ +#cmakedefine HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 + +/* Define to 1 if you have the `socket' function. */ +#cmakedefine HAVE_SOCKET 1 + +/* Define to 1 if you have the socketpair function. */ +#cmakedefine HAVE_SOCKETPAIR 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDATOMIC_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the strcasecmp function. */ +#cmakedefine HAVE_STRCASECMP 1 + +/* Define to 1 if you have the strcmpi function. */ +#cmakedefine HAVE_STRCMPI 1 + +/* Define to 1 if you have the strdup function. */ +#cmakedefine HAVE_STRDUP 1 + +/* Define to 1 if you have the strerror_r function. */ +#cmakedefine HAVE_STRERROR_R 1 + +/* Define to 1 if you have the stricmp function. */ +#cmakedefine HAVE_STRICMP 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STROPTS_H 1 + +/* Define to 1 if you have the strtok_r function. */ +#cmakedefine HAVE_STRTOK_R 1 + +/* Define to 1 if you have the strtoll function. */ +#cmakedefine HAVE_STRTOLL 1 + +/* Define to 1 if you have the memrchr function. */ +#cmakedefine HAVE_MEMRCHR 1 + +/* if struct sockaddr_storage is defined */ +#cmakedefine HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if you have the timeval struct. */ +#cmakedefine HAVE_STRUCT_TIMEVAL 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_EVENTFD_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_FILIO_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_POLL_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_SOCKIO_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_UTIME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_TERMIOS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_TERMIO_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `utime' function. */ +#cmakedefine HAVE_UTIME 1 + +/* Define to 1 if you have the `utimes' function. */ +#cmakedefine HAVE_UTIMES 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UTIME_H 1 + +/* Define this symbol if your OS supports changing the contents of argv */ +#cmakedefine HAVE_WRITABLE_ARGV 1 + +/* Define to 1 if you need the lber.h header file even with ldap.h */ +#cmakedefine NEED_LBER_H 1 + +/* Define to 1 if you need the malloc.h header file even with stdlib.h */ +#cmakedefine NEED_MALLOC_H 1 + +/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */ +#cmakedefine NEED_REENTRANT 1 + +/* cpu-machine-OS */ +#cmakedefine OS ${OS} + +/* Name of package */ +#cmakedefine PACKAGE ${PACKAGE} + +/* Define to the address where bug reports for this package should be sent. */ +#cmakedefine PACKAGE_BUGREPORT ${PACKAGE_BUGREPORT} + +/* Define to the full name of this package. */ +#cmakedefine PACKAGE_NAME ${PACKAGE_NAME} + +/* Define to the full name and version of this package. */ +#cmakedefine PACKAGE_STRING ${PACKAGE_STRING} + +/* Define to the one symbol short name of this package. */ +#cmakedefine PACKAGE_TARNAME ${PACKAGE_TARNAME} + +/* Define to the version of this package. */ +#cmakedefine PACKAGE_VERSION ${PACKAGE_VERSION} + +/* a suitable file to read random data from */ +#cmakedefine RANDOM_FILE "${RANDOM_FILE}" + +/* + Note: SIZEOF_* variables are fetched with CMake through check_type_size(). + As per CMake documentation on CheckTypeSize, C preprocessor code is + generated by CMake into SIZEOF_*_CODE. This is what we use in the + following statements. + + Reference: https://cmake.org/cmake/help/latest/module/CheckTypeSize.html +*/ + +/* The size of `int', as computed by sizeof. */ +${SIZEOF_INT_CODE} + +/* The size of `long', as computed by sizeof. */ +${SIZEOF_LONG_CODE} + +/* The size of `long long', as computed by sizeof. */ +${SIZEOF_LONG_LONG_CODE} + +/* The size of `off_t', as computed by sizeof. */ +${SIZEOF_OFF_T_CODE} + +/* The size of `curl_off_t', as computed by sizeof. */ +${SIZEOF_CURL_OFF_T_CODE} + +/* The size of `curl_socket_t', as computed by sizeof. */ +${SIZEOF_CURL_SOCKET_T_CODE} + +/* The size of `size_t', as computed by sizeof. */ +${SIZEOF_SIZE_T_CODE} + +/* The size of `time_t', as computed by sizeof. */ +${SIZEOF_TIME_T_CODE} + +/* Define to 1 if you have the ANSI C header files. */ +#cmakedefine STDC_HEADERS 1 + +/* Define if you want to enable c-ares support */ +#cmakedefine USE_ARES 1 + +/* Define if you want to enable POSIX threaded DNS lookup */ +#cmakedefine USE_THREADS_POSIX 1 + +/* Define if you want to enable WIN32 threaded DNS lookup */ +#cmakedefine USE_THREADS_WIN32 1 + +/* if GnuTLS is enabled */ +#cmakedefine USE_GNUTLS 1 + +/* if Secure Transport is enabled */ +#cmakedefine USE_SECTRANSP 1 + +/* if mbedTLS is enabled */ +#cmakedefine USE_MBEDTLS 1 + +/* if BearSSL is enabled */ +#cmakedefine USE_BEARSSL 1 + +/* if wolfSSL is enabled */ +#cmakedefine USE_WOLFSSL 1 + +/* if wolfSSL has the wolfSSL_DES_ecb_encrypt function. */ +#cmakedefine HAVE_WOLFSSL_DES_ECB_ENCRYPT 1 + +/* if wolfSSL has the wolfSSL_BIO_set_shutdown function. */ +#cmakedefine HAVE_WOLFSSL_FULL_BIO 1 + +/* if libssh is in use */ +#cmakedefine USE_LIBSSH 1 + +/* if libssh2 is in use */ +#cmakedefine USE_LIBSSH2 1 + +/* if libpsl is in use */ +#cmakedefine USE_LIBPSL 1 + +/* if you want to use OpenLDAP code instead of legacy ldap implementation */ +#cmakedefine USE_OPENLDAP 1 + +/* if OpenSSL is in use */ +#cmakedefine USE_OPENSSL 1 + +/* if librtmp/rtmpdump is in use */ +#cmakedefine USE_LIBRTMP 1 + +/* if GSASL is in use */ +#cmakedefine USE_GSASL 1 + +/* Define to 1 if you do not want the OpenSSL configuration to be loaded + automatically */ +#cmakedefine CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG 1 + +/* to enable NGHTTP2 */ +#cmakedefine USE_NGHTTP2 1 + +/* to enable NGTCP2 */ +#cmakedefine USE_NGTCP2 1 + +/* to enable NGHTTP3 */ +#cmakedefine USE_NGHTTP3 1 + +/* to enable quiche */ +#cmakedefine USE_QUICHE 1 + +/* to enable openssl + nghttp3 */ +#cmakedefine USE_OPENSSL_QUIC 1 + +/* Define to 1 if you have the quiche_conn_set_qlog_fd function. */ +#cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1 + +/* to enable msh3 */ +#cmakedefine USE_MSH3 1 + +/* if Unix domain sockets are enabled */ +#cmakedefine USE_UNIX_SOCKETS 1 + +/* Define to 1 if you are building a Windows target with large file support. */ +#cmakedefine USE_WIN32_LARGE_FILES 1 + +/* to enable SSPI support */ +#cmakedefine USE_WINDOWS_SSPI 1 + +/* to enable Windows SSL */ +#cmakedefine USE_SCHANNEL 1 + +/* enable multiple SSL backends */ +#cmakedefine CURL_WITH_MULTI_SSL 1 + +/* Version number of package */ +#cmakedefine VERSION ${VERSION} + +/* Define to 1 if OS is AIX. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS} + +/* Define for large files, on AIX-style hosts. */ +#cmakedefine _LARGE_FILES ${_LARGE_FILES} + +/* define this if you need it to compile thread-safe code */ +#cmakedefine _THREAD_SAFE ${_THREAD_SAFE} + +/* Define to empty if `const' does not conform to ANSI C. */ +#cmakedefine const ${const} + +/* Type to use in place of in_addr_t when system does not provide it. */ +#cmakedefine in_addr_t ${in_addr_t} + +/* Define to `unsigned int' if does not define. */ +#cmakedefine size_t ${size_t} + +/* the signed version of size_t */ +#cmakedefine ssize_t ${ssize_t} + +/* Define to 1 if you have the mach_absolute_time function. */ +#cmakedefine HAVE_MACH_ABSOLUTE_TIME 1 + +/* to enable Windows IDN */ +#cmakedefine USE_WIN32_IDN 1 + +/* to enable Apple IDN */ +#cmakedefine USE_APPLE_IDN 1 + +/* Define to 1 to enable websocket support. */ +#cmakedefine USE_WEBSOCKETS 1 + +/* Define to 1 if OpenSSL has the SSL_CTX_set_srp_username function. */ +#cmakedefine HAVE_OPENSSL_SRP 1 + +/* Define to 1 if GnuTLS has the gnutls_srp_verifier function. */ +#cmakedefine HAVE_GNUTLS_SRP 1 + +/* Define to 1 to enable TLS-SRP support. */ +#cmakedefine USE_TLS_SRP 1 + +/* Define to 1 to query for HTTPSRR when using DoH */ +#cmakedefine USE_HTTPSRR 1 + +/* if ECH support is available */ +#cmakedefine USE_ECH 1 diff --git a/third_party/curl/lib/curl_config.h.in b/third_party/curl/lib/curl_config.h.in new file mode 100644 index 0000000000..7871ac1470 --- /dev/null +++ b/third_party/curl/lib/curl_config.h.in @@ -0,0 +1,1007 @@ +/* lib/curl_config.h.in. Generated from configure.ac by autoheader. */ + +/* Ignore c-ares deprecation warnings */ +#undef CARES_NO_DEPRECATED + +/* to enable curl debug memory tracking */ +#undef CURLDEBUG + +/* Location of default ca bundle */ +#undef CURL_CA_BUNDLE + +/* define "1" to use built in CA store of SSL library */ +#undef CURL_CA_FALLBACK + +/* Location of default ca path */ +#undef CURL_CA_PATH + +/* Default SSL backend */ +#undef CURL_DEFAULT_SSL_BACKEND + +/* disable alt-svc */ +#undef CURL_DISABLE_ALTSVC + +/* to disable AWS sig support */ +#undef CURL_DISABLE_AWS + +/* to disable basic authentication */ +#undef CURL_DISABLE_BASIC_AUTH + +/* to disable bearer authentication */ +#undef CURL_DISABLE_BEARER_AUTH + +/* disable local binding support */ +#undef CURL_DISABLE_BINDLOCAL + +/* to disable cookies support */ +#undef CURL_DISABLE_COOKIES + +/* to disable DICT */ +#undef CURL_DISABLE_DICT + +/* to disable digest authentication */ +#undef CURL_DISABLE_DIGEST_AUTH + +/* disable DoH */ +#undef CURL_DISABLE_DOH + +/* to disable FILE */ +#undef CURL_DISABLE_FILE + +/* disable form API */ +#undef CURL_DISABLE_FORM_API + +/* to disable FTP */ +#undef CURL_DISABLE_FTP + +/* to disable curl_easy_options */ +#undef CURL_DISABLE_GETOPTIONS + +/* to disable Gopher */ +#undef CURL_DISABLE_GOPHER + +/* disable headers-api */ +#undef CURL_DISABLE_HEADERS_API + +/* disable alt-svc */ +#undef CURL_DISABLE_HSTS + +/* to disable HTTP */ +#undef CURL_DISABLE_HTTP + +/* disable HTTP authentication */ +#undef CURL_DISABLE_HTTP_AUTH + +/* to disable IMAP */ +#undef CURL_DISABLE_IMAP + +/* to disable kerberos authentication */ +#undef CURL_DISABLE_KERBEROS_AUTH + +/* to disable LDAP */ +#undef CURL_DISABLE_LDAP + +/* to disable LDAPS */ +#undef CURL_DISABLE_LDAPS + +/* to disable --libcurl C code generation option */ +#undef CURL_DISABLE_LIBCURL_OPTION + +/* disable mime API */ +#undef CURL_DISABLE_MIME + +/* to disable MQTT */ +#undef CURL_DISABLE_MQTT + +/* to disable negotiate authentication */ +#undef CURL_DISABLE_NEGOTIATE_AUTH + +/* disable netrc parsing */ +#undef CURL_DISABLE_NETRC + +/* to disable NTLM support */ +#undef CURL_DISABLE_NTLM + +/* if the OpenSSL configuration won't be loaded automatically */ +#undef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG + +/* disable date parsing */ +#undef CURL_DISABLE_PARSEDATE + +/* to disable POP3 */ +#undef CURL_DISABLE_POP3 + +/* disable progress-meter */ +#undef CURL_DISABLE_PROGRESS_METER + +/* to disable proxies */ +#undef CURL_DISABLE_PROXY + +/* to disable RTSP */ +#undef CURL_DISABLE_RTSP + +/* disable DNS shuffling */ +#undef CURL_DISABLE_SHUFFLE_DNS + +/* to disable SMB/CIFS */ +#undef CURL_DISABLE_SMB + +/* to disable SMTP */ +#undef CURL_DISABLE_SMTP + +/* to disable socketpair support */ +#undef CURL_DISABLE_SOCKETPAIR + +/* to disable TELNET */ +#undef CURL_DISABLE_TELNET + +/* to disable TFTP */ +#undef CURL_DISABLE_TFTP + +/* to disable verbose strings */ +#undef CURL_DISABLE_VERBOSE_STRINGS + +/* Definition to make a library symbol externally visible. */ +#undef CURL_EXTERN_SYMBOL + +/* IP address type in sockaddr */ +#undef CURL_SA_FAMILY_T + +/* built with multiple SSL backends */ +#undef CURL_WITH_MULTI_SSL + +/* enable debug build options */ +#undef DEBUGBUILD + +/* Define to the type of arg 2 for gethostname. */ +#undef GETHOSTNAME_TYPE_ARG2 + +/* Define to 1 if you have the alarm function. */ +#undef HAVE_ALARM + +/* Define to 1 if you have the `arc4random' function. */ +#undef HAVE_ARC4RANDOM + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you have _Atomic support. */ +#undef HAVE_ATOMIC + +/* Define to 1 if you have the basename function. */ +#undef HAVE_BASENAME + +/* Define to 1 if bool is an available type. */ +#undef HAVE_BOOL_T + +/* if BROTLI is in use */ +#undef HAVE_BROTLI + +/* Define to 1 if you have the header file. */ +#undef HAVE_BROTLI_DECODE_H + +/* Define to 1 if you have the __builtin_available function. */ +#undef HAVE_BUILTIN_AVAILABLE + +/* Define to 1 if you have the clock_gettime function and monotonic timer. */ +#undef HAVE_CLOCK_GETTIME_MONOTONIC + +/* Define to 1 if you have the clock_gettime function and raw monotonic timer. + */ +#undef HAVE_CLOCK_GETTIME_MONOTONIC_RAW + +/* Define to 1 if you have the closesocket function. */ +#undef HAVE_CLOSESOCKET + +/* Define to 1 if you have the CloseSocket camel case function. */ +#undef HAVE_CLOSESOCKET_CAMEL + +/* Define to 1 if you have the header file. */ +#undef HAVE_CRYPTO_H + +/* Define to 1 if you have the fseeko declaration */ +#undef HAVE_DECL_FSEEKO + +/* Define to 1 if you have the declaration of `getpwuid_r', and to 0 if you + don't. */ +#undef HAVE_DECL_GETPWUID_R + +/* "Set if getpwuid_r() declaration is missing" */ +#undef HAVE_DECL_GETPWUID_R_MISSING + +/* if you have */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ERR_H + +/* Define to 1 if you have the `eventfd' function. */ +#undef HAVE_EVENTFD + +/* Define to 1 if you have the fcntl function. */ +#undef HAVE_FCNTL + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have a working fcntl O_NONBLOCK function. */ +#undef HAVE_FCNTL_O_NONBLOCK + +/* Define to 1 if you have the `fnmatch' function. */ +#undef HAVE_FNMATCH + +/* Define to 1 if you have the freeaddrinfo function. */ +#undef HAVE_FREEADDRINFO + +/* Define to 1 if you have the `fseeko' function. */ +#undef HAVE_FSEEKO + +/* Define to 1 if you have the fsetxattr function. */ +#undef HAVE_FSETXATTR + +/* fsetxattr() takes 5 args */ +#undef HAVE_FSETXATTR_5 + +/* fsetxattr() takes 6 args */ +#undef HAVE_FSETXATTR_6 + +/* Define to 1 if you have the ftruncate function. */ +#undef HAVE_FTRUNCATE + +/* Define to 1 if you have a working getaddrinfo function. */ +#undef HAVE_GETADDRINFO + +/* Define to 1 if the getaddrinfo function is threadsafe. */ +#undef HAVE_GETADDRINFO_THREADSAFE + +/* Define to 1 if you have the `geteuid' function. */ +#undef HAVE_GETEUID + +/* Define to 1 if you have the gethostbyname function. */ +#undef HAVE_GETHOSTBYNAME + +/* Define to 1 if you have the gethostbyname_r function. */ +#undef HAVE_GETHOSTBYNAME_R + +/* gethostbyname_r() takes 3 args */ +#undef HAVE_GETHOSTBYNAME_R_3 + +/* gethostbyname_r() takes 5 args */ +#undef HAVE_GETHOSTBYNAME_R_5 + +/* gethostbyname_r() takes 6 args */ +#undef HAVE_GETHOSTBYNAME_R_6 + +/* Define to 1 if you have the gethostname function. */ +#undef HAVE_GETHOSTNAME + +/* Define to 1 if you have a working getifaddrs function. */ +#undef HAVE_GETIFADDRS + +/* Define to 1 if you have the `getpass_r' function. */ +#undef HAVE_GETPASS_R + +/* Define to 1 if you have the getpeername function. */ +#undef HAVE_GETPEERNAME + +/* Define to 1 if you have the `getppid' function. */ +#undef HAVE_GETPPID + +/* Define to 1 if you have the `getpwuid' function. */ +#undef HAVE_GETPWUID + +/* Define to 1 if you have the `getpwuid_r' function. */ +#undef HAVE_GETPWUID_R + +/* Define to 1 if you have the `getrlimit' function. */ +#undef HAVE_GETRLIMIT + +/* Define to 1 if you have the getsockname function. */ +#undef HAVE_GETSOCKNAME + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have a working glibc-style strerror_r function. */ +#undef HAVE_GLIBC_STRERROR_R + +/* Define to 1 if you have a working gmtime_r function. */ +#undef HAVE_GMTIME_R + +/* if you have the function gnutls_srp_verifier */ +#undef HAVE_GNUTLS_SRP + +/* if you have GSS-API libraries */ +#undef HAVE_GSSAPI + +/* Define to 1 if you have the header file. */ +#undef HAVE_GSSAPI_GSSAPI_GENERIC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_GSSAPI_GSSAPI_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_GSSAPI_GSSAPI_KRB5_H + +/* if you have GNU GSS */ +#undef HAVE_GSSGNU + +/* Define to 1 if you have the header file. */ +#undef HAVE_HYPER_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_IDN2_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_IFADDRS_H + +/* Define to 1 if you have the `if_nametoindex' function. */ +#undef HAVE_IF_NAMETOINDEX + +/* Define to 1 if you have a IPv6 capable working inet_ntop function. */ +#undef HAVE_INET_NTOP + +/* Define to 1 if you have a IPv6 capable working inet_pton function. */ +#undef HAVE_INET_PTON + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the ioctl function. */ +#undef HAVE_IOCTL + +/* Define to 1 if you have the ioctlsocket function. */ +#undef HAVE_IOCTLSOCKET + +/* Define to 1 if you have the IoctlSocket camel case function. */ +#undef HAVE_IOCTLSOCKET_CAMEL + +/* Define to 1 if you have a working IoctlSocket camel case FIONBIO function. + */ +#undef HAVE_IOCTLSOCKET_CAMEL_FIONBIO + +/* Define to 1 if you have a working ioctlsocket FIONBIO function. */ +#undef HAVE_IOCTLSOCKET_FIONBIO + +/* Define to 1 if you have a working ioctl FIONBIO function. */ +#undef HAVE_IOCTL_FIONBIO + +/* Define to 1 if you have a working ioctl SIOCGIFADDR function. */ +#undef HAVE_IOCTL_SIOCGIFADDR + +/* Define to 1 if you have the header file. */ +#undef HAVE_IO_H + +/* Define to 1 if you have the lber.h header file. */ +#undef HAVE_LBER_H + +/* Define to 1 if you have the ldap.h header file. */ +#undef HAVE_LDAP_H + +/* Define to 1 if you have the `ldap_init_fd' function. */ +#undef HAVE_LDAP_INIT_FD + +/* Use LDAPS implementation */ +#undef HAVE_LDAP_SSL + +/* Define to 1 if you have the ldap_ssl.h header file. */ +#undef HAVE_LDAP_SSL_H + +/* Define to 1 if you have the `ldap_url_parse' function. */ +#undef HAVE_LDAP_URL_PARSE + +/* Define to 1 if you have the `brotlidec' library (-lbrotlidec). */ +#undef HAVE_LIBBROTLIDEC + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBGEN_H + +/* Define to 1 if you have the `idn2' library (-lidn2). */ +#undef HAVE_LIBIDN2 + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBPSL_H + +/* Define to 1 if using LibreSSL. */ +#undef HAVE_LIBRESSL + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBRTMP_RTMP_H + +/* Define to 1 if you have the `ssh' library (-lssh). */ +#undef HAVE_LIBSSH + +/* Define to 1 if you have the `ssh2' library (-lssh2). */ +#undef HAVE_LIBSSH2 + +/* Define to 1 if you have the `ssl' library (-lssl). */ +#undef HAVE_LIBSSL + +/* Define to 1 if you have the `wolfssh' library (-lwolfssh). */ +#undef HAVE_LIBWOLFSSH + +/* if zlib is available */ +#undef HAVE_LIBZ + +/* Define to 1 if you have the `zstd' library (-lzstd). */ +#undef HAVE_LIBZSTD + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_TCP_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if the compiler supports the 'long long' data type. */ +#undef HAVE_LONGLONG + +/* Define to 1 if you have the `mach_absolute_time' function. */ +#undef HAVE_MACH_ABSOLUTE_TIME + +/* Define to 1 if you have the memrchr function or macro. */ +#undef HAVE_MEMRCHR + +/* Define to 1 if you have the MSG_NOSIGNAL flag. */ +#undef HAVE_MSG_NOSIGNAL + +/* Define to 1 if you have the header file. */ +#undef HAVE_MSH3_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN6_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_TCP_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_UDP_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NGHTTP2_NGHTTP2_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NGHTTP3_NGHTTP3_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NGTCP2_NGTCP2_CRYPTO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NGTCP2_NGTCP2_H + +/* if you have an old MIT Kerberos version, lacking GSS_C_NT_HOSTBASED_SERVICE + */ +#undef HAVE_OLD_GSSMIT + +/* if you have opendir */ +#undef HAVE_OPENDIR + +/* Define to 1 if using OpenSSL 3 or later. */ +#undef HAVE_OPENSSL3 + +/* Define to 1 if you have the header file. */ +#undef HAVE_OPENSSL_CRYPTO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_OPENSSL_ERR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_OPENSSL_PEM_H + +/* if you have the functions OSSL_QUIC_client_method */ +#undef HAVE_OPENSSL_QUIC + +/* Define to 1 if you have the header file. */ +#undef HAVE_OPENSSL_RSA_H + +/* if you have the functions SSL_CTX_set_srp_username and + SSL_CTX_set_srp_password */ +#undef HAVE_OPENSSL_SRP + +/* Define to 1 if you have the header file. */ +#undef HAVE_OPENSSL_SSL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_OPENSSL_X509_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PEM_H + +/* Define to 1 if you have the `pipe' function. */ +#undef HAVE_PIPE + +/* If you have a fine poll */ +#undef HAVE_POLL_FINE + +/* Define to 1 if you have the header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have a working POSIX-style strerror_r function. */ +#undef HAVE_POSIX_STRERROR_R + +/* Define to 1 if you have the header file. */ +#undef HAVE_PROTO_BSDSOCKET_H + +/* if you have */ +#undef HAVE_PTHREAD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* Define to 1 if you have the `quiche_conn_set_qlog_fd' function. */ +#undef HAVE_QUICHE_CONN_SET_QLOG_FD + +/* Define to 1 if you have the header file. */ +#undef HAVE_QUICHE_H + +/* Define to 1 if you have the recv function. */ +#undef HAVE_RECV + +/* Define to 1 if you have the header file. */ +#undef HAVE_RSA_H + +/* Define to 1 if you have the `sched_yield' function. */ +#undef HAVE_SCHED_YIELD + +/* Define to 1 if you have the select function. */ +#undef HAVE_SELECT + +/* Define to 1 if you have the send function. */ +#undef HAVE_SEND + +/* Define to 1 if you have the `sendmsg' function. */ +#undef HAVE_SENDMSG + +/* Define to 1 if you have the header file. */ +#undef HAVE_SETJMP_H + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `setmode' function. */ +#undef HAVE_SETMODE + +/* Define to 1 if you have the `setrlimit' function. */ +#undef HAVE_SETRLIMIT + +/* Define to 1 if you have the sigaction function. */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the siginterrupt function. */ +#undef HAVE_SIGINTERRUPT + +/* Define to 1 if you have the signal function. */ +#undef HAVE_SIGNAL + +/* Define to 1 if you have the sigsetjmp function or macro. */ +#undef HAVE_SIGSETJMP + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */ +#undef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + +/* Define to 1 if you have the socket function. */ +#undef HAVE_SOCKET + +/* Define to 1 if you have the socketpair function. */ +#undef HAVE_SOCKETPAIR + +/* Define to 1 if you have the header file. */ +#undef HAVE_SOCKET_H + +/* Define to 1 if you have the `SSL_ech_set1_echconfig' function. */ +#undef HAVE_SSL_ECH_SET1_ECHCONFIG + +/* Define to 1 if you have the header file. */ +#undef HAVE_SSL_H + +/* Define to 1 if you have the `SSL_set0_wbio' function. */ +#undef HAVE_SSL_SET0_WBIO + +/* Define to 1 if you have the `SSL_set1_ech_config_list' function. */ +#undef HAVE_SSL_SET1_ECH_CONFIG_LIST + +/* Define to 1 if you have the `SSL_set_quic_use_legacy_codepoint' function. + */ +#undef HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDATOMIC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the strcasecmp function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the strcmpi function. */ +#undef HAVE_STRCMPI + +/* Define to 1 if you have the strdup function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the strerror_r function. */ +#undef HAVE_STRERROR_R + +/* Define to 1 if you have the stricmp function. */ +#undef HAVE_STRICMP + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STROPTS_H + +/* Define to 1 if you have the strtok_r function. */ +#undef HAVE_STRTOK_R + +/* Define to 1 if you have the strtoll function. */ +#undef HAVE_STRTOLL + +/* if struct sockaddr_storage is defined */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE + +/* Define to 1 if you have the timeval struct. */ +#undef HAVE_STRUCT_TIMEVAL + +/* Define to 1 if suseconds_t is an available type. */ +#undef HAVE_SUSECONDS_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_EVENTFD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UTIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_XATTR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIO_H + +/* Define this if time_t is unsigned */ +#undef HAVE_TIME_T_UNSIGNED + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `utime' function. */ +#undef HAVE_UTIME + +/* Define to 1 if you have the `utimes' function. */ +#undef HAVE_UTIMES + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_WOLFSSH_SSH_H + +/* Define to 1 if you have the `wolfSSL_CTX_GenerateEchConfig' function. */ +#undef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG + +/* if you have wolfSSL_DES_ecb_encrypt */ +#undef HAVE_WOLFSSL_DES_ECB_ENCRYPT + +/* if you have wolfSSL_BIO_set_shutdown */ +#undef HAVE_WOLFSSL_FULL_BIO + +/* Define to 1 if you have the `wolfSSL_get_peer_certificate' function. */ +#undef HAVE_WOLFSSL_GET_PEER_CERTIFICATE + +/* Define to 1 if you have the `wolfSSL_UseALPN' function. */ +#undef HAVE_WOLFSSL_USEALPN + +/* Define this symbol if your OS supports changing the contents of argv */ +#undef HAVE_WRITABLE_ARGV + +/* Define to 1 if you have the header file. */ +#undef HAVE_X509_H + +/* if libzstd is in use */ +#undef HAVE_ZSTD + +/* Define to 1 if you have the header file. */ +#undef HAVE_ZSTD_H + +/* Define to 1 if you have the `_fseeki64' function. */ +#undef HAVE__FSEEKI64 + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Define to 1 if you need the lber.h header file even with ldap.h */ +#undef NEED_LBER_H + +/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */ +#undef NEED_REENTRANT + +/* Define to 1 if _THREAD_SAFE preprocessor symbol must be defined. */ +#undef NEED_THREAD_SAFE + +/* cpu-machine-OS */ +#undef OS + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* a suitable file to read random data from */ +#undef RANDOM_FILE + +/* Size of curl_off_t in number of bytes */ +#undef SIZEOF_CURL_OFF_T + +/* Size of curl_socket_t in number of bytes */ +#undef SIZEOF_CURL_SOCKET_T + +/* Size of int in number of bytes */ +#undef SIZEOF_INT + +/* Size of long in number of bytes */ +#undef SIZEOF_LONG + +/* Size of long long in number of bytes */ +#undef SIZEOF_LONG_LONG + +/* Size of off_t in number of bytes */ +#undef SIZEOF_OFF_T + +/* Size of size_t in number of bytes */ +#undef SIZEOF_SIZE_T + +/* Size of time_t in number of bytes */ +#undef SIZEOF_TIME_T + +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#undef STDC_HEADERS + +/* if AmiSSL is in use */ +#undef USE_AMISSL + +/* Define to enable c-ares support */ +#undef USE_ARES + +/* if BearSSL is enabled */ +#undef USE_BEARSSL + +/* if ECH support is available */ +#undef USE_ECH + +/* if GnuTLS is enabled */ +#undef USE_GNUTLS + +/* GSASL support enabled */ +#undef USE_GSASL + +/* force HTTPS RR support for ECH */ +#undef USE_HTTPSRR + +/* if hyper is in use */ +#undef USE_HYPER + +/* Define if you want to enable IPv6 support */ +#undef USE_IPV6 + +/* if libpsl is in use */ +#undef USE_LIBPSL + +/* if librtmp is in use */ +#undef USE_LIBRTMP + +/* if libssh is in use */ +#undef USE_LIBSSH + +/* if libssh2 is in use */ +#undef USE_LIBSSH2 + +/* If you want to build curl with the built-in manual */ +#undef USE_MANUAL + +/* if mbedTLS is enabled */ +#undef USE_MBEDTLS + +/* if msh3 is in use */ +#undef USE_MSH3 + +/* if nghttp2 is in use */ +#undef USE_NGHTTP2 + +/* if nghttp3 is in use */ +#undef USE_NGHTTP3 + +/* if ngtcp2 is in use */ +#undef USE_NGTCP2 + +/* if ngtcp2_crypto_boringssl is in use */ +#undef USE_NGTCP2_CRYPTO_BORINGSSL + +/* if ngtcp2_crypto_gnutls is in use */ +#undef USE_NGTCP2_CRYPTO_GNUTLS + +/* if ngtcp2_crypto_quictls is in use */ +#undef USE_NGTCP2_CRYPTO_QUICTLS + +/* if ngtcp2_crypto_wolfssl is in use */ +#undef USE_NGTCP2_CRYPTO_WOLFSSL + +/* if ngtcp2 + nghttp3 is in use */ +#undef USE_NGTCP2_H3 + +/* Use OpenLDAP-specific code */ +#undef USE_OPENLDAP + +/* if OpenSSL is in use */ +#undef USE_OPENSSL + +/* if openssl quic + nghttp3 is in use */ +#undef USE_OPENSSL_H3 + +/* if openssl QUIC is in use */ +#undef USE_OPENSSL_QUIC + +/* if quiche is in use */ +#undef USE_QUICHE + +/* if rustls is enabled */ +#undef USE_RUSTLS + +/* to enable Windows native SSL/TLS support */ +#undef USE_SCHANNEL + +/* enable Secure Transport */ +#undef USE_SECTRANSP + +/* if you want POSIX threaded DNS lookup */ +#undef USE_THREADS_POSIX + +/* if you want Win32 threaded DNS lookup */ +#undef USE_THREADS_WIN32 + +/* Use TLS-SRP authentication */ +#undef USE_TLS_SRP + +/* Use Unix domain sockets */ +#undef USE_UNIX_SOCKETS + +/* enable websockets support */ +#undef USE_WEBSOCKETS + +/* Define to 1 if you are building a Windows target with crypto API support. + */ +#undef USE_WIN32_CRYPTO + +/* Define to 1 if you have the `normaliz' (WinIDN) library (-lnormaliz). */ +#undef USE_WIN32_IDN + +/* Define to 1 if you are building a Windows target with large file support. + */ +#undef USE_WIN32_LARGE_FILES + +/* Use Windows LDAP implementation */ +#undef USE_WIN32_LDAP + +/* Define to 1 if you are building a Windows target without large file + support. */ +#undef USE_WIN32_SMALL_FILES + +/* to enable SSPI support */ +#undef USE_WINDOWS_SSPI + +/* if wolfSSH is in use */ +#undef USE_WOLFSSH + +/* if wolfSSL is enabled */ +#undef USE_WOLFSSL + +/* Version number of package */ +#undef VERSION + +/* Define to 1 if OS is AIX. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Type to use in place of in_addr_t when system does not provide it. */ +#undef in_addr_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* the signed version of size_t */ +#undef ssize_t diff --git a/third_party/curl/lib/curl_ctype.h b/third_party/curl/lib/curl_ctype.h new file mode 100644 index 0000000000..7f0d0cc291 --- /dev/null +++ b/third_party/curl/lib/curl_ctype.h @@ -0,0 +1,51 @@ +#ifndef HEADER_CURL_CTYPE_H +#define HEADER_CURL_CTYPE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f')) +#define ISUPHEXALHA(x) (((x) >= 'A') && ((x) <= 'F')) + +#define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f) +#define IS7F(x) ((x) == 0x7f) + +#define ISLOWPRINT(x) (((x) >= 9) && ((x) <= 0x0d)) + +#define ISPRINT(x) (ISLOWPRINT(x) || (((x) >= ' ') && ((x) <= 0x7e))) +#define ISGRAPH(x) (ISLOWPRINT(x) || (((x) > ' ') && ((x) <= 0x7e))) +#define ISCNTRL(x) (ISLOWCNTRL(x) || IS7F(x)) +#define ISALPHA(x) (ISLOWER(x) || ISUPPER(x)) +#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALHA(x) || ISUPHEXALHA(x)) +#define ISALNUM(x) (ISDIGIT(x) || ISLOWER(x) || ISUPPER(x)) +#define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z')) +#define ISLOWER(x) (((x) >= 'a') && ((x) <= 'z')) +#define ISDIGIT(x) (((x) >= '0') && ((x) <= '9')) +#define ISBLANK(x) (((x) == ' ') || ((x) == '\t')) +#define ISSPACE(x) (ISBLANK(x) || (((x) >= 0xa) && ((x) <= 0x0d))) +#define ISURLPUNTCS(x) (((x) == '-') || ((x) == '.') || ((x) == '_') || \ + ((x) == '~')) +#define ISUNRESERVED(x) (ISALNUM(x) || ISURLPUNTCS(x)) + + +#endif /* HEADER_CURL_CTYPE_H */ diff --git a/third_party/curl/lib/curl_des.c b/third_party/curl/lib/curl_des.c new file mode 100644 index 0000000000..9662ba39ab --- /dev/null +++ b/third_party/curl/lib/curl_des.c @@ -0,0 +1,69 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ + (defined(USE_GNUTLS) || \ + defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || \ + defined(USE_WIN32_CRYPTO)) + +#include "curl_des.h" + +/* + * Curl_des_set_odd_parity() + * + * This is used to apply odd parity to the given byte array. It is typically + * used by when a cryptography engine does not have its own version. + * + * The function is a port of the Java based oddParity() function over at: + * + * https://davenport.sourceforge.net/ntlm.html + * + * Parameters: + * + * bytes [in/out] - The data whose parity bits are to be adjusted for + * odd parity. + * len [out] - The length of the data. + */ +void Curl_des_set_odd_parity(unsigned char *bytes, size_t len) +{ + size_t i; + + for(i = 0; i < len; i++) { + unsigned char b = bytes[i]; + + bool needs_parity = (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ + (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ + (b >> 1)) & 0x01) == 0; + + if(needs_parity) + bytes[i] |= 0x01; + else + bytes[i] &= 0xfe; + } +} + +#endif diff --git a/third_party/curl/lib/curl_des.h b/third_party/curl/lib/curl_des.h new file mode 100644 index 0000000000..66525ab436 --- /dev/null +++ b/third_party/curl/lib/curl_des.h @@ -0,0 +1,40 @@ +#ifndef HEADER_CURL_DES_H +#define HEADER_CURL_DES_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ + (defined(USE_GNUTLS) || \ + defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || \ + defined(USE_WIN32_CRYPTO)) + +/* Applies odd parity to the given byte array */ +void Curl_des_set_odd_parity(unsigned char *bytes, size_t length); + +#endif + +#endif /* HEADER_CURL_DES_H */ diff --git a/third_party/curl/lib/curl_endian.c b/third_party/curl/lib/curl_endian.c new file mode 100644 index 0000000000..d982e31269 --- /dev/null +++ b/third_party/curl/lib/curl_endian.c @@ -0,0 +1,84 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "curl_endian.h" + +/* + * Curl_read16_le() + * + * This function converts a 16-bit integer from the little endian format, as + * used in the incoming package to whatever endian format we are using + * natively. + * + * Parameters: + * + * buf [in] - A pointer to a 2 byte buffer. + * + * Returns the integer. + */ +unsigned short Curl_read16_le(const unsigned char *buf) +{ + return (unsigned short)(((unsigned short)buf[0]) | + ((unsigned short)buf[1] << 8)); +} + +/* + * Curl_read32_le() + * + * This function converts a 32-bit integer from the little endian format, as + * used in the incoming package to whatever endian format we are using + * natively. + * + * Parameters: + * + * buf [in] - A pointer to a 4 byte buffer. + * + * Returns the integer. + */ +unsigned int Curl_read32_le(const unsigned char *buf) +{ + return ((unsigned int)buf[0]) | ((unsigned int)buf[1] << 8) | + ((unsigned int)buf[2] << 16) | ((unsigned int)buf[3] << 24); +} + +/* + * Curl_read16_be() + * + * This function converts a 16-bit integer from the big endian format, as + * used in the incoming package to whatever endian format we are using + * natively. + * + * Parameters: + * + * buf [in] - A pointer to a 2 byte buffer. + * + * Returns the integer. + */ +unsigned short Curl_read16_be(const unsigned char *buf) +{ + return (unsigned short)(((unsigned short)buf[0] << 8) | + ((unsigned short)buf[1])); +} diff --git a/third_party/curl/lib/curl_endian.h b/third_party/curl/lib/curl_endian.h new file mode 100644 index 0000000000..fa283214b1 --- /dev/null +++ b/third_party/curl/lib/curl_endian.h @@ -0,0 +1,36 @@ +#ifndef HEADER_CURL_ENDIAN_H +#define HEADER_CURL_ENDIAN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* Converts a 16-bit integer from little endian */ +unsigned short Curl_read16_le(const unsigned char *buf); + +/* Converts a 32-bit integer from little endian */ +unsigned int Curl_read32_le(const unsigned char *buf); + +/* Converts a 16-bit integer from big endian */ +unsigned short Curl_read16_be(const unsigned char *buf); + +#endif /* HEADER_CURL_ENDIAN_H */ diff --git a/third_party/curl/lib/curl_fnmatch.c b/third_party/curl/lib/curl_fnmatch.c new file mode 100644 index 0000000000..ab848e8ffe --- /dev/null +++ b/third_party/curl/lib/curl_fnmatch.c @@ -0,0 +1,390 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#ifndef CURL_DISABLE_FTP +#include + +#include "curl_fnmatch.h" +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +#ifndef HAVE_FNMATCH + +#define CURLFNM_CHARSET_LEN (sizeof(char) * 256) +#define CURLFNM_CHSET_SIZE (CURLFNM_CHARSET_LEN + 15) + +#define CURLFNM_NEGATE CURLFNM_CHARSET_LEN + +#define CURLFNM_ALNUM (CURLFNM_CHARSET_LEN + 1) +#define CURLFNM_DIGIT (CURLFNM_CHARSET_LEN + 2) +#define CURLFNM_XDIGIT (CURLFNM_CHARSET_LEN + 3) +#define CURLFNM_ALPHA (CURLFNM_CHARSET_LEN + 4) +#define CURLFNM_PRINT (CURLFNM_CHARSET_LEN + 5) +#define CURLFNM_BLANK (CURLFNM_CHARSET_LEN + 6) +#define CURLFNM_LOWER (CURLFNM_CHARSET_LEN + 7) +#define CURLFNM_GRAPH (CURLFNM_CHARSET_LEN + 8) +#define CURLFNM_SPACE (CURLFNM_CHARSET_LEN + 9) +#define CURLFNM_UPPER (CURLFNM_CHARSET_LEN + 10) + +typedef enum { + CURLFNM_SCHS_DEFAULT = 0, + CURLFNM_SCHS_RIGHTBR, + CURLFNM_SCHS_RIGHTBRLEFTBR +} setcharset_state; + +typedef enum { + CURLFNM_PKW_INIT = 0, + CURLFNM_PKW_DDOT +} parsekey_state; + +typedef enum { + CCLASS_OTHER = 0, + CCLASS_DIGIT, + CCLASS_UPPER, + CCLASS_LOWER +} char_class; + +#define SETCHARSET_OK 1 +#define SETCHARSET_FAIL 0 + +static int parsekeyword(unsigned char **pattern, unsigned char *charset) +{ + parsekey_state state = CURLFNM_PKW_INIT; +#define KEYLEN 10 + char keyword[KEYLEN] = { 0 }; + int i; + unsigned char *p = *pattern; + bool found = FALSE; + for(i = 0; !found; i++) { + char c = (char)*p++; + if(i >= KEYLEN) + return SETCHARSET_FAIL; + switch(state) { + case CURLFNM_PKW_INIT: + if(ISLOWER(c)) + keyword[i] = c; + else if(c == ':') + state = CURLFNM_PKW_DDOT; + else + return SETCHARSET_FAIL; + break; + case CURLFNM_PKW_DDOT: + if(c == ']') + found = TRUE; + else + return SETCHARSET_FAIL; + } + } +#undef KEYLEN + + *pattern = p; /* move caller's pattern pointer */ + if(strcmp(keyword, "digit") == 0) + charset[CURLFNM_DIGIT] = 1; + else if(strcmp(keyword, "alnum") == 0) + charset[CURLFNM_ALNUM] = 1; + else if(strcmp(keyword, "alpha") == 0) + charset[CURLFNM_ALPHA] = 1; + else if(strcmp(keyword, "xdigit") == 0) + charset[CURLFNM_XDIGIT] = 1; + else if(strcmp(keyword, "print") == 0) + charset[CURLFNM_PRINT] = 1; + else if(strcmp(keyword, "graph") == 0) + charset[CURLFNM_GRAPH] = 1; + else if(strcmp(keyword, "space") == 0) + charset[CURLFNM_SPACE] = 1; + else if(strcmp(keyword, "blank") == 0) + charset[CURLFNM_BLANK] = 1; + else if(strcmp(keyword, "upper") == 0) + charset[CURLFNM_UPPER] = 1; + else if(strcmp(keyword, "lower") == 0) + charset[CURLFNM_LOWER] = 1; + else + return SETCHARSET_FAIL; + return SETCHARSET_OK; +} + +/* Return the character class. */ +static char_class charclass(unsigned char c) +{ + if(ISUPPER(c)) + return CCLASS_UPPER; + if(ISLOWER(c)) + return CCLASS_LOWER; + if(ISDIGIT(c)) + return CCLASS_DIGIT; + return CCLASS_OTHER; +} + +/* Include a character or a range in set. */ +static void setcharorrange(unsigned char **pp, unsigned char *charset) +{ + unsigned char *p = (*pp)++; + unsigned char c = *p++; + + charset[c] = 1; + if(ISALNUM(c) && *p++ == '-') { + char_class cc = charclass(c); + unsigned char endrange = *p++; + + if(endrange == '\\') + endrange = *p++; + if(endrange >= c && charclass(endrange) == cc) { + while(c++ != endrange) + if(charclass(c) == cc) /* Chars in class may be not consecutive. */ + charset[c] = 1; + *pp = p; + } + } +} + +/* returns 1 (true) if pattern is OK, 0 if is bad ("p" is pattern pointer) */ +static int setcharset(unsigned char **p, unsigned char *charset) +{ + setcharset_state state = CURLFNM_SCHS_DEFAULT; + bool something_found = FALSE; + unsigned char c; + + memset(charset, 0, CURLFNM_CHSET_SIZE); + for(;;) { + c = **p; + if(!c) + return SETCHARSET_FAIL; + + switch(state) { + case CURLFNM_SCHS_DEFAULT: + if(c == ']') { + if(something_found) + return SETCHARSET_OK; + something_found = TRUE; + state = CURLFNM_SCHS_RIGHTBR; + charset[c] = 1; + (*p)++; + } + else if(c == '[') { + unsigned char *pp = *p + 1; + + if(*pp++ == ':' && parsekeyword(&pp, charset)) + *p = pp; + else { + charset[c] = 1; + (*p)++; + } + something_found = TRUE; + } + else if(c == '^' || c == '!') { + if(!something_found) { + if(charset[CURLFNM_NEGATE]) { + charset[c] = 1; + something_found = TRUE; + } + else + charset[CURLFNM_NEGATE] = 1; /* negate charset */ + } + else + charset[c] = 1; + (*p)++; + } + else if(c == '\\') { + c = *(++(*p)); + if(c) + setcharorrange(p, charset); + else + charset['\\'] = 1; + something_found = TRUE; + } + else { + setcharorrange(p, charset); + something_found = TRUE; + } + break; + case CURLFNM_SCHS_RIGHTBR: + if(c == '[') { + state = CURLFNM_SCHS_RIGHTBRLEFTBR; + charset[c] = 1; + (*p)++; + } + else if(c == ']') { + return SETCHARSET_OK; + } + else if(ISPRINT(c)) { + charset[c] = 1; + (*p)++; + state = CURLFNM_SCHS_DEFAULT; + } + else + /* used 'goto fail' instead of 'return SETCHARSET_FAIL' to avoid a + * nonsense warning 'statement not reached' at end of the fnc when + * compiling on Solaris */ + goto fail; + break; + case CURLFNM_SCHS_RIGHTBRLEFTBR: + if(c == ']') + return SETCHARSET_OK; + state = CURLFNM_SCHS_DEFAULT; + charset[c] = 1; + (*p)++; + break; + } + } +fail: + return SETCHARSET_FAIL; +} + +static int loop(const unsigned char *pattern, const unsigned char *string, + int maxstars) +{ + unsigned char *p = (unsigned char *)pattern; + unsigned char *s = (unsigned char *)string; + unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 }; + + for(;;) { + unsigned char *pp; + + switch(*p) { + case '*': + if(!maxstars) + return CURL_FNMATCH_NOMATCH; + /* Regroup consecutive stars and question marks. This can be done because + '*?*?*' can be expressed as '??*'. */ + for(;;) { + if(*++p == '\0') + return CURL_FNMATCH_MATCH; + if(*p == '?') { + if(!*s++) + return CURL_FNMATCH_NOMATCH; + } + else if(*p != '*') + break; + } + /* Skip string characters until we find a match with pattern suffix. */ + for(maxstars--; *s; s++) { + if(loop(p, s, maxstars) == CURL_FNMATCH_MATCH) + return CURL_FNMATCH_MATCH; + } + return CURL_FNMATCH_NOMATCH; + case '?': + if(!*s) + return CURL_FNMATCH_NOMATCH; + s++; + p++; + break; + case '\0': + return *s? CURL_FNMATCH_NOMATCH: CURL_FNMATCH_MATCH; + case '\\': + if(p[1]) + p++; + if(*s++ != *p++) + return CURL_FNMATCH_NOMATCH; + break; + case '[': + pp = p + 1; /* Copy in case of syntax error in set. */ + if(setcharset(&pp, charset)) { + int found = FALSE; + if(!*s) + return CURL_FNMATCH_NOMATCH; + if(charset[(unsigned int)*s]) + found = TRUE; + else if(charset[CURLFNM_ALNUM]) + found = ISALNUM(*s); + else if(charset[CURLFNM_ALPHA]) + found = ISALPHA(*s); + else if(charset[CURLFNM_DIGIT]) + found = ISDIGIT(*s); + else if(charset[CURLFNM_XDIGIT]) + found = ISXDIGIT(*s); + else if(charset[CURLFNM_PRINT]) + found = ISPRINT(*s); + else if(charset[CURLFNM_SPACE]) + found = ISSPACE(*s); + else if(charset[CURLFNM_UPPER]) + found = ISUPPER(*s); + else if(charset[CURLFNM_LOWER]) + found = ISLOWER(*s); + else if(charset[CURLFNM_BLANK]) + found = ISBLANK(*s); + else if(charset[CURLFNM_GRAPH]) + found = ISGRAPH(*s); + + if(charset[CURLFNM_NEGATE]) + found = !found; + + if(!found) + return CURL_FNMATCH_NOMATCH; + p = pp + 1; + s++; + break; + } + /* Syntax error in set; mismatch! */ + return CURL_FNMATCH_NOMATCH; + + default: + if(*p++ != *s++) + return CURL_FNMATCH_NOMATCH; + break; + } + } +} + +/* + * @unittest: 1307 + */ +int Curl_fnmatch(void *ptr, const char *pattern, const char *string) +{ + (void)ptr; /* the argument is specified by the curl_fnmatch_callback + prototype, but not used by Curl_fnmatch() */ + if(!pattern || !string) { + return CURL_FNMATCH_FAIL; + } + return loop((unsigned char *)pattern, (unsigned char *)string, 2); +} +#else +#include +/* + * @unittest: 1307 + */ +int Curl_fnmatch(void *ptr, const char *pattern, const char *string) +{ + (void)ptr; /* the argument is specified by the curl_fnmatch_callback + prototype, but not used by Curl_fnmatch() */ + if(!pattern || !string) { + return CURL_FNMATCH_FAIL; + } + + switch(fnmatch(pattern, string, 0)) { + case 0: + return CURL_FNMATCH_MATCH; + case FNM_NOMATCH: + return CURL_FNMATCH_NOMATCH; + default: + return CURL_FNMATCH_FAIL; + } + /* not reached */ +} + +#endif + +#endif /* if FTP is disabled */ diff --git a/third_party/curl/lib/curl_fnmatch.h b/third_party/curl/lib/curl_fnmatch.h new file mode 100644 index 0000000000..595646ff0d --- /dev/null +++ b/third_party/curl/lib/curl_fnmatch.h @@ -0,0 +1,46 @@ +#ifndef HEADER_CURL_FNMATCH_H +#define HEADER_CURL_FNMATCH_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#define CURL_FNMATCH_MATCH 0 +#define CURL_FNMATCH_NOMATCH 1 +#define CURL_FNMATCH_FAIL 2 + +/* default pattern matching function + * ================================= + * Implemented with recursive backtracking, if you want to use Curl_fnmatch, + * please note that there is not implemented UTF/UNICODE support. + * + * Implemented features: + * '?' notation, does not match UTF characters + * '*' can also work with UTF string + * [a-zA-Z0-9] enumeration support + * + * keywords: alnum, digit, xdigit, alpha, print, blank, lower, graph, space + * and upper (use as "[[:alnum:]]") + */ +int Curl_fnmatch(void *ptr, const char *pattern, const char *string); + +#endif /* HEADER_CURL_FNMATCH_H */ diff --git a/third_party/curl/lib/curl_get_line.c b/third_party/curl/lib/curl_get_line.c new file mode 100644 index 0000000000..100207331d --- /dev/null +++ b/third_party/curl/lib/curl_get_line.c @@ -0,0 +1,77 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ + !defined(CURL_DISABLE_HSTS) || !defined(CURL_DISABLE_NETRC) + +#include "curl_get_line.h" +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* + * Curl_get_line() makes sure to only return complete whole lines that end + * newlines. + */ +int Curl_get_line(struct dynbuf *buf, FILE *input) +{ + CURLcode result; + char buffer[128]; + Curl_dyn_reset(buf); + while(1) { + char *b = fgets(buffer, sizeof(buffer), input); + + if(b) { + size_t rlen = strlen(b); + + if(!rlen) + break; + + result = Curl_dyn_addn(buf, b, rlen); + if(result) + /* too long line or out of memory */ + return 0; /* error */ + + else if(b[rlen-1] == '\n') + /* end of the line */ + return 1; /* all good */ + + else if(feof(input)) { + /* append a newline */ + result = Curl_dyn_addn(buf, "\n", 1); + if(result) + /* too long line or out of memory */ + return 0; /* error */ + return 1; /* all good */ + } + } + else + break; + } + return 0; +} + +#endif /* if not disabled */ diff --git a/third_party/curl/lib/curl_get_line.h b/third_party/curl/lib/curl_get_line.h new file mode 100644 index 0000000000..7907cde880 --- /dev/null +++ b/third_party/curl/lib/curl_get_line.h @@ -0,0 +1,32 @@ +#ifndef HEADER_CURL_GET_LINE_H +#define HEADER_CURL_GET_LINE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "dynbuf.h" + +/* Curl_get_line() returns complete lines that end with a newline. */ +int Curl_get_line(struct dynbuf *buf, FILE *input); + +#endif /* HEADER_CURL_GET_LINE_H */ diff --git a/third_party/curl/lib/curl_gethostname.c b/third_party/curl/lib/curl_gethostname.c new file mode 100644 index 0000000000..cd111231d9 --- /dev/null +++ b/third_party/curl/lib/curl_gethostname.c @@ -0,0 +1,102 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "curl_gethostname.h" + +/* + * Curl_gethostname() is a wrapper around gethostname() which allows + * overriding the hostname that the function would normally return. + * This capability is used by the test suite to verify exact matching + * of NTLM authentication, which exercises libcurl's MD4 and DES code + * as well as by the SMTP module when a hostname is not provided. + * + * For libcurl debug enabled builds hostname overriding takes place + * when environment variable CURL_GETHOSTNAME is set, using the value + * held by the variable to override returned hostname. + * + * Note: The function always returns the un-qualified hostname rather + * than being provider dependent. + * + * For libcurl shared library release builds the test suite preloads + * another shared library named libhostname using the LD_PRELOAD + * mechanism which intercepts, and might override, the gethostname() + * function call. In this case a given platform must support the + * LD_PRELOAD mechanism and additionally have environment variable + * CURL_GETHOSTNAME set in order to override the returned hostname. + * + * For libcurl static library release builds no overriding takes place. + */ + +int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen) +{ +#ifndef HAVE_GETHOSTNAME + + /* Allow compilation and return failure when unavailable */ + (void) name; + (void) namelen; + return -1; + +#else + int err; + char *dot; + +#ifdef DEBUGBUILD + + /* Override hostname when environment variable CURL_GETHOSTNAME is set */ + const char *force_hostname = getenv("CURL_GETHOSTNAME"); + if(force_hostname) { + strncpy(name, force_hostname, namelen - 1); + err = 0; + } + else { + name[0] = '\0'; + err = gethostname(name, namelen); + } + +#else /* DEBUGBUILD */ + + /* The call to system's gethostname() might get intercepted by the + libhostname library when libcurl is built as a non-debug shared + library when running the test suite. */ + name[0] = '\0'; + err = gethostname(name, namelen); + +#endif + + name[namelen - 1] = '\0'; + + if(err) + return err; + + /* Truncate domain, leave only machine name */ + dot = strchr(name, '.'); + if(dot) + *dot = '\0'; + + return 0; +#endif + +} diff --git a/third_party/curl/lib/curl_gethostname.h b/third_party/curl/lib/curl_gethostname.h new file mode 100644 index 0000000000..9281d9c242 --- /dev/null +++ b/third_party/curl/lib/curl_gethostname.h @@ -0,0 +1,33 @@ +#ifndef HEADER_CURL_GETHOSTNAME_H +#define HEADER_CURL_GETHOSTNAME_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* Hostname buffer size */ +#define HOSTNAME_MAX 1024 + +/* This returns the local machine's un-qualified hostname */ +int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen); + +#endif /* HEADER_CURL_GETHOSTNAME_H */ diff --git a/third_party/curl/lib/curl_gssapi.c b/third_party/curl/lib/curl_gssapi.c new file mode 100644 index 0000000000..c6fe1256b2 --- /dev/null +++ b/third_party/curl/lib/curl_gssapi.c @@ -0,0 +1,152 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_GSSAPI + +#include "curl_gssapi.h" +#include "sendf.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if defined(__GNUC__) +#define CURL_ALIGN8 __attribute__ ((aligned(8))) +#else +#define CURL_ALIGN8 +#endif + +gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = { + 6, (char *)"\x2b\x06\x01\x05\x05\x02" +}; +gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = { + 9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" +}; + +OM_uint32 Curl_gss_init_sec_context( + struct Curl_easy *data, + OM_uint32 *minor_status, + gss_ctx_id_t *context, + gss_name_t target_name, + gss_OID mech_type, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_buffer_t output_token, + const bool mutual_auth, + OM_uint32 *ret_flags) +{ + OM_uint32 req_flags = GSS_C_REPLAY_FLAG; + + if(mutual_auth) + req_flags |= GSS_C_MUTUAL_FLAG; + + if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_POLICY_FLAG) { +#ifdef GSS_C_DELEG_POLICY_FLAG + req_flags |= GSS_C_DELEG_POLICY_FLAG; +#else + infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not " + "compiled in"); +#endif + } + + if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG) + req_flags |= GSS_C_DELEG_FLAG; + + return gss_init_sec_context(minor_status, + GSS_C_NO_CREDENTIAL, /* cred_handle */ + context, + target_name, + mech_type, + req_flags, + 0, /* time_req */ + input_chan_bindings, + input_token, + NULL, /* actual_mech_type */ + output_token, + ret_flags, + NULL /* time_rec */); +} + +#define GSS_LOG_BUFFER_LEN 1024 +static size_t display_gss_error(OM_uint32 status, int type, + char *buf, size_t len) { + OM_uint32 maj_stat; + OM_uint32 min_stat; + OM_uint32 msg_ctx = 0; + gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER; + + do { + maj_stat = gss_display_status(&min_stat, + status, + type, + GSS_C_NO_OID, + &msg_ctx, + &status_string); + if(maj_stat == GSS_S_COMPLETE && status_string.length > 0) { + if(GSS_LOG_BUFFER_LEN > len + status_string.length + 3) { + len += msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len, + "%.*s. ", (int)status_string.length, + (char *)status_string.value); + } + } + gss_release_buffer(&min_stat, &status_string); + } while(!GSS_ERROR(maj_stat) && msg_ctx); + + return len; +} + +/* + * Curl_gss_log_error() + * + * This is used to log a GSS-API error status. + * + * Parameters: + * + * data [in] - The session handle. + * prefix [in] - The prefix of the log message. + * major [in] - The major status code. + * minor [in] - The minor status code. + */ +void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, + OM_uint32 major, OM_uint32 minor) +{ + char buf[GSS_LOG_BUFFER_LEN]; + size_t len = 0; + + if(major != GSS_S_FAILURE) + len = display_gss_error(major, GSS_C_GSS_CODE, buf, len); + + display_gss_error(minor, GSS_C_MECH_CODE, buf, len); + + infof(data, "%s%s", prefix, buf); +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)data; + (void)prefix; +#endif +} + +#endif /* HAVE_GSSAPI */ diff --git a/third_party/curl/lib/curl_gssapi.h b/third_party/curl/lib/curl_gssapi.h new file mode 100644 index 0000000000..7b9a534ea2 --- /dev/null +++ b/third_party/curl/lib/curl_gssapi.h @@ -0,0 +1,63 @@ +#ifndef HEADER_CURL_GSSAPI_H +#define HEADER_CURL_GSSAPI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "urldata.h" + +#ifdef HAVE_GSSAPI +extern gss_OID_desc Curl_spnego_mech_oid; +extern gss_OID_desc Curl_krb5_mech_oid; + +/* Common method for using GSS-API */ +OM_uint32 Curl_gss_init_sec_context( + struct Curl_easy *data, + OM_uint32 *minor_status, + gss_ctx_id_t *context, + gss_name_t target_name, + gss_OID mech_type, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_buffer_t output_token, + const bool mutual_auth, + OM_uint32 *ret_flags); + +/* Helper to log a GSS-API error status */ +void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, + OM_uint32 major, OM_uint32 minor); + +/* Provide some definitions missing in old headers */ +#ifdef HAVE_OLD_GSSMIT +#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name +#define NCOMPAT 1 +#endif + +/* Define our privacy and integrity protection values */ +#define GSSAUTH_P_NONE 1 +#define GSSAUTH_P_INTEGRITY 2 +#define GSSAUTH_P_PRIVACY 4 + +#endif /* HAVE_GSSAPI */ +#endif /* HEADER_CURL_GSSAPI_H */ diff --git a/third_party/curl/lib/curl_hmac.h b/third_party/curl/lib/curl_hmac.h new file mode 100644 index 0000000000..7a5387a948 --- /dev/null +++ b/third_party/curl/lib/curl_hmac.h @@ -0,0 +1,78 @@ +#ifndef HEADER_CURL_HMAC_H +#define HEADER_CURL_HMAC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ + || defined(USE_LIBSSH2) + +#include + +#define HMAC_MD5_LENGTH 16 + +typedef CURLcode (* HMAC_hinit_func)(void *context); +typedef void (* HMAC_hupdate_func)(void *context, + const unsigned char *data, + unsigned int len); +typedef void (* HMAC_hfinal_func)(unsigned char *result, void *context); + + +/* Per-hash function HMAC parameters. */ +struct HMAC_params { + HMAC_hinit_func + hmac_hinit; /* Initialize context procedure. */ + HMAC_hupdate_func hmac_hupdate; /* Update context with data. */ + HMAC_hfinal_func hmac_hfinal; /* Get final result procedure. */ + unsigned int hmac_ctxtsize; /* Context structure size. */ + unsigned int hmac_maxkeylen; /* Maximum key length (bytes). */ + unsigned int hmac_resultlen; /* Result length (bytes). */ +}; + + +/* HMAC computation context. */ +struct HMAC_context { + const struct HMAC_params *hmac_hash; /* Hash function definition. */ + void *hmac_hashctxt1; /* Hash function context 1. */ + void *hmac_hashctxt2; /* Hash function context 2. */ +}; + + +/* Prototypes. */ +struct HMAC_context *Curl_HMAC_init(const struct HMAC_params *hashparams, + const unsigned char *key, + unsigned int keylen); +int Curl_HMAC_update(struct HMAC_context *context, + const unsigned char *data, + unsigned int len); +int Curl_HMAC_final(struct HMAC_context *context, unsigned char *result); + +CURLcode Curl_hmacit(const struct HMAC_params *hashparams, + const unsigned char *key, const size_t keylen, + const unsigned char *data, const size_t datalen, + unsigned char *output); + +#endif + +#endif /* HEADER_CURL_HMAC_H */ diff --git a/third_party/curl/lib/curl_krb5.h b/third_party/curl/lib/curl_krb5.h new file mode 100644 index 0000000000..ccf6b96a87 --- /dev/null +++ b/third_party/curl/lib/curl_krb5.h @@ -0,0 +1,52 @@ +#ifndef HEADER_CURL_KRB5_H +#define HEADER_CURL_KRB5_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +struct Curl_sec_client_mech { + const char *name; + size_t size; + int (*init)(void *); + int (*auth)(void *, struct Curl_easy *data, struct connectdata *); + void (*end)(void *); + int (*check_prot)(void *, int); + int (*encode)(void *, const void *, int, int, void **); + int (*decode)(void *, void *, int, int, struct connectdata *); +}; + +#define AUTH_OK 0 +#define AUTH_CONTINUE 1 +#define AUTH_ERROR 2 + +#ifdef HAVE_GSSAPI +int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, char *, + enum protection_level); +void Curl_sec_end(struct connectdata *); +CURLcode Curl_sec_login(struct Curl_easy *, struct connectdata *); +int Curl_sec_request_prot(struct connectdata *conn, const char *level); +#else +#define Curl_sec_end(x) +#endif + +#endif /* HEADER_CURL_KRB5_H */ diff --git a/third_party/curl/lib/curl_ldap.h b/third_party/curl/lib/curl_ldap.h new file mode 100644 index 0000000000..8a1d807ed9 --- /dev/null +++ b/third_party/curl/lib/curl_ldap.h @@ -0,0 +1,36 @@ +#ifndef HEADER_CURL_LDAP_H +#define HEADER_CURL_LDAP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#ifndef CURL_DISABLE_LDAP +extern const struct Curl_handler Curl_handler_ldap; + +#if !defined(CURL_DISABLE_LDAPS) && \ + ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ + (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) +extern const struct Curl_handler Curl_handler_ldaps; +#endif + +#endif +#endif /* HEADER_CURL_LDAP_H */ diff --git a/third_party/curl/lib/curl_md4.h b/third_party/curl/lib/curl_md4.h new file mode 100644 index 0000000000..4706e49578 --- /dev/null +++ b/third_party/curl/lib/curl_md4.h @@ -0,0 +1,39 @@ +#ifndef HEADER_CURL_MD4_H +#define HEADER_CURL_MD4_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include + +#if defined(USE_CURL_NTLM_CORE) + +#define MD4_DIGEST_LENGTH 16 + +CURLcode Curl_md4it(unsigned char *output, const unsigned char *input, + const size_t len); + +#endif /* defined(USE_CURL_NTLM_CORE) */ + +#endif /* HEADER_CURL_MD4_H */ diff --git a/third_party/curl/lib/curl_md5.h b/third_party/curl/lib/curl_md5.h new file mode 100644 index 0000000000..61671c306a --- /dev/null +++ b/third_party/curl/lib/curl_md5.h @@ -0,0 +1,67 @@ +#ifndef HEADER_CURL_MD5_H +#define HEADER_CURL_MD5_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_DIGEST_AUTH) + +#include "curl_hmac.h" + +#define MD5_DIGEST_LEN 16 + +typedef CURLcode (* Curl_MD5_init_func)(void *context); +typedef void (* Curl_MD5_update_func)(void *context, + const unsigned char *data, + unsigned int len); +typedef void (* Curl_MD5_final_func)(unsigned char *result, void *context); + +struct MD5_params { + Curl_MD5_init_func md5_init_func; /* Initialize context procedure */ + Curl_MD5_update_func md5_update_func; /* Update context with data */ + Curl_MD5_final_func md5_final_func; /* Get final result procedure */ + unsigned int md5_ctxtsize; /* Context structure size */ + unsigned int md5_resultlen; /* Result length (bytes) */ +}; + +struct MD5_context { + const struct MD5_params *md5_hash; /* Hash function definition */ + void *md5_hashctx; /* Hash function context */ +}; + +extern const struct MD5_params Curl_DIGEST_MD5[1]; +extern const struct HMAC_params Curl_HMAC_MD5[1]; + +CURLcode Curl_md5it(unsigned char *output, const unsigned char *input, + const size_t len); + +struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params); +CURLcode Curl_MD5_update(struct MD5_context *context, + const unsigned char *data, + unsigned int len); +CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result); + +#endif + +#endif /* HEADER_CURL_MD5_H */ diff --git a/third_party/curl/lib/curl_memory.h b/third_party/curl/lib/curl_memory.h new file mode 100644 index 0000000000..714ad71c9e --- /dev/null +++ b/third_party/curl/lib/curl_memory.h @@ -0,0 +1,178 @@ +#ifndef HEADER_CURL_MEMORY_H +#define HEADER_CURL_MEMORY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Nasty internal details ahead... + * + * File curl_memory.h must be included by _all_ *.c source files + * that use memory related functions strdup, malloc, calloc, realloc + * or free, and given source file is used to build libcurl library. + * It should be included immediately before memdebug.h as the last files + * included to avoid undesired interaction with other memory function + * headers in dependent libraries. + * + * There is nearly no exception to above rule. All libcurl source + * files in 'lib' subdirectory as well as those living deep inside + * 'packages' subdirectories and linked together in order to build + * libcurl library shall follow it. + * + * File lib/strdup.c is an exception, given that it provides a strdup + * clone implementation while using malloc. Extra care needed inside + * this one. + * + * The need for curl_memory.h inclusion is due to libcurl's feature + * of allowing library user to provide memory replacement functions, + * memory callbacks, at runtime with curl_global_init_mem() + * + * Any *.c source file used to build libcurl library that does not + * include curl_memory.h and uses any memory function of the five + * mentioned above will compile without any indication, but it will + * trigger weird memory related issues at runtime. + * + */ + +#ifdef HEADER_CURL_MEMDEBUG_H +/* cleanup after memdebug.h */ + +#ifdef MEMDEBUG_NODEFINES +#ifdef CURLDEBUG + +#undef strdup +#undef malloc +#undef calloc +#undef realloc +#undef free +#undef send +#undef recv + +#ifdef _WIN32 +# ifdef UNICODE +# undef wcsdup +# undef _wcsdup +# undef _tcsdup +# else +# undef _tcsdup +# endif +#endif + +#undef socket +#undef accept +#ifdef HAVE_SOCKETPAIR +#undef socketpair +#endif + +#ifdef HAVE_GETADDRINFO +#if defined(getaddrinfo) && defined(__osf__) +#undef ogetaddrinfo +#else +#undef getaddrinfo +#endif +#endif /* HAVE_GETADDRINFO */ + +#ifdef HAVE_FREEADDRINFO +#undef freeaddrinfo +#endif /* HAVE_FREEADDRINFO */ + +/* sclose is probably already defined, redefine it! */ +#undef sclose +#undef fopen +#undef fdopen +#undef fclose + +#endif /* MEMDEBUG_NODEFINES */ +#endif /* CURLDEBUG */ + +#undef HEADER_CURL_MEMDEBUG_H +#endif /* HEADER_CURL_MEMDEBUG_H */ + +/* +** Following section applies even when CURLDEBUG is not defined. +*/ + +#undef fake_sclose + +#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */ +/* + * The following memory function replacement typedef's are COPIED from + * curl/curl.h and MUST match the originals. We copy them to avoid having to + * include curl/curl.h here. We avoid that include since it includes stdio.h + * and other headers that may get messed up with defines done here. + */ +typedef void *(*curl_malloc_callback)(size_t size); +typedef void (*curl_free_callback)(void *ptr); +typedef void *(*curl_realloc_callback)(void *ptr, size_t size); +typedef char *(*curl_strdup_callback)(const char *str); +typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); +#define CURL_DID_MEMORY_FUNC_TYPEDEFS +#endif + +extern curl_malloc_callback Curl_cmalloc; +extern curl_free_callback Curl_cfree; +extern curl_realloc_callback Curl_crealloc; +extern curl_strdup_callback Curl_cstrdup; +extern curl_calloc_callback Curl_ccalloc; +#if defined(_WIN32) && defined(UNICODE) +extern curl_wcsdup_callback Curl_cwcsdup; +#endif + +#ifndef CURLDEBUG + +/* + * libcurl's 'memory tracking' system defines strdup, malloc, calloc, + * realloc and free, along with others, in memdebug.h in a different + * way although still using memory callbacks forward declared above. + * When using the 'memory tracking' system (CURLDEBUG defined) we do + * not define here the five memory functions given that definitions + * from memdebug.h are the ones that shall be used. + */ + +#undef strdup +#define strdup(ptr) Curl_cstrdup(ptr) +#undef malloc +#define malloc(size) Curl_cmalloc(size) +#undef calloc +#define calloc(nbelem,size) Curl_ccalloc(nbelem, size) +#undef realloc +#define realloc(ptr,size) Curl_crealloc(ptr, size) +#undef free +#define free(ptr) Curl_cfree(ptr) + +#ifdef _WIN32 +# ifdef UNICODE +# undef wcsdup +# define wcsdup(ptr) Curl_cwcsdup(ptr) +# undef _wcsdup +# define _wcsdup(ptr) Curl_cwcsdup(ptr) +# undef _tcsdup +# define _tcsdup(ptr) Curl_cwcsdup(ptr) +# else +# undef _tcsdup +# define _tcsdup(ptr) Curl_cstrdup(ptr) +# endif +#endif + +#endif /* CURLDEBUG */ +#endif /* HEADER_CURL_MEMORY_H */ diff --git a/third_party/curl/lib/curl_memrchr.c b/third_party/curl/lib/curl_memrchr.c new file mode 100644 index 0000000000..3f3dc6de16 --- /dev/null +++ b/third_party/curl/lib/curl_memrchr.c @@ -0,0 +1,64 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "curl_memrchr.h" +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +#ifndef HAVE_MEMRCHR + +/* + * Curl_memrchr() + * + * Our memrchr() function clone for systems which lack this function. The + * memrchr() function is like the memchr() function, except that it searches + * backwards from the end of the n bytes pointed to by s instead of forward + * from the beginning. + */ + +void * +Curl_memrchr(const void *s, int c, size_t n) +{ + if(n > 0) { + const unsigned char *p = s; + const unsigned char *q = s; + + p += n - 1; + + while(p >= q) { + if(*p == (unsigned char)c) + return (void *)p; + p--; + } + } + return NULL; +} + +#endif /* HAVE_MEMRCHR */ diff --git a/third_party/curl/lib/curl_memrchr.h b/third_party/curl/lib/curl_memrchr.h new file mode 100644 index 0000000000..45bb38c68e --- /dev/null +++ b/third_party/curl/lib/curl_memrchr.h @@ -0,0 +1,44 @@ +#ifndef HEADER_CURL_MEMRCHR_H +#define HEADER_CURL_MEMRCHR_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_MEMRCHR + +#include +#ifdef HAVE_STRINGS_H +# include +#endif + +#else /* HAVE_MEMRCHR */ + +void *Curl_memrchr(const void *s, int c, size_t n); + +#define memrchr(x,y,z) Curl_memrchr((x),(y),(z)) + +#endif /* HAVE_MEMRCHR */ + +#endif /* HEADER_CURL_MEMRCHR_H */ diff --git a/third_party/curl/lib/curl_multibyte.c b/third_party/curl/lib/curl_multibyte.c new file mode 100644 index 0000000000..86ac74ff4b --- /dev/null +++ b/third_party/curl/lib/curl_multibyte.c @@ -0,0 +1,162 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * This file is 'mem-include-scan' clean, which means memdebug.h and + * curl_memory.h are purposely not included in this file. See test 1132. + * + * The functions in this file are curlx functions which are not tracked by the + * curl memory tracker memdebug. + */ + +#include "curl_setup.h" + +#if defined(_WIN32) + +#include "curl_multibyte.h" + +/* + * MultiByte conversions using Windows kernel32 library. + */ + +wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8) +{ + wchar_t *str_w = NULL; + + if(str_utf8) { + int str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + str_utf8, -1, NULL, 0); + if(str_w_len > 0) { + str_w = malloc(str_w_len * sizeof(wchar_t)); + if(str_w) { + if(MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w, + str_w_len) == 0) { + free(str_w); + return NULL; + } + } + } + } + + return str_w; +} + +char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w) +{ + char *str_utf8 = NULL; + + if(str_w) { + int bytes = WideCharToMultiByte(CP_UTF8, 0, str_w, -1, + NULL, 0, NULL, NULL); + if(bytes > 0) { + str_utf8 = malloc(bytes); + if(str_utf8) { + if(WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, bytes, + NULL, NULL) == 0) { + free(str_utf8); + return NULL; + } + } + } + } + + return str_utf8; +} + +#endif /* _WIN32 */ + +#if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES) + +int curlx_win32_open(const char *filename, int oflag, ...) +{ + int pmode = 0; + +#ifdef _UNICODE + int result = -1; + wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename); +#endif + + va_list param; + va_start(param, oflag); + if(oflag & O_CREAT) + pmode = va_arg(param, int); + va_end(param); + +#ifdef _UNICODE + if(filename_w) { + result = _wopen(filename_w, oflag, pmode); + curlx_unicodefree(filename_w); + } + else + errno = EINVAL; + return result; +#else + return (_open)(filename, oflag, pmode); +#endif +} + +FILE *curlx_win32_fopen(const char *filename, const char *mode) +{ +#ifdef _UNICODE + FILE *result = NULL; + wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename); + wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode); + if(filename_w && mode_w) + result = _wfopen(filename_w, mode_w); + else + errno = EINVAL; + curlx_unicodefree(filename_w); + curlx_unicodefree(mode_w); + return result; +#else + return (fopen)(filename, mode); +#endif +} + +int curlx_win32_stat(const char *path, struct_stat *buffer) +{ +#ifdef _UNICODE + int result = -1; + wchar_t *path_w = curlx_convert_UTF8_to_wchar(path); + if(path_w) { +#if defined(USE_WIN32_SMALL_FILES) + result = _wstat(path_w, buffer); +#else + result = _wstati64(path_w, buffer); +#endif + curlx_unicodefree(path_w); + } + else + errno = EINVAL; + return result; +#else +#if defined(USE_WIN32_SMALL_FILES) + return _stat(path, buffer); +#else + return _stati64(path, buffer); +#endif +#endif +} + +#endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */ diff --git a/third_party/curl/lib/curl_multibyte.h b/third_party/curl/lib/curl_multibyte.h new file mode 100644 index 0000000000..2d31c28f44 --- /dev/null +++ b/third_party/curl/lib/curl_multibyte.h @@ -0,0 +1,92 @@ +#ifndef HEADER_CURL_MULTIBYTE_H +#define HEADER_CURL_MULTIBYTE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if defined(_WIN32) + + /* + * MultiByte conversions using Windows kernel32 library. + */ + +wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8); +char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w); +#endif /* _WIN32 */ + +/* + * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8() + * and curlx_unicodefree() main purpose is to minimize the number of + * preprocessor conditional directives needed by code using these + * to differentiate UNICODE from non-UNICODE builds. + * + * In the case of a non-UNICODE build the tchar strings are char strings that + * are duplicated via strdup and remain in whatever the passed in encoding is, + * which is assumed to be UTF-8 but may be other encoding. Therefore the + * significance of the conversion functions is primarily for UNICODE builds. + * + * Allocated memory should be free'd with curlx_unicodefree(). + * + * Note: Because these are curlx functions their memory usage is not tracked + * by the curl memory tracker memdebug. you will notice that curlx + * function-like macros call free and strdup in parentheses, eg (strdup)(ptr), + * and that is to ensure that the curl memdebug override macros do not replace + * them. + */ + +#if defined(UNICODE) && defined(_WIN32) + +#define curlx_convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar((ptr)) +#define curlx_convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8((ptr)) + +typedef union { + unsigned short *tchar_ptr; + const unsigned short *const_tchar_ptr; + unsigned short *tbyte_ptr; + const unsigned short *const_tbyte_ptr; +} xcharp_u; + +#else + +#define curlx_convert_UTF8_to_tchar(ptr) (strdup)(ptr) +#define curlx_convert_tchar_to_UTF8(ptr) (strdup)(ptr) + +typedef union { + char *tchar_ptr; + const char *const_tchar_ptr; + unsigned char *tbyte_ptr; + const unsigned char *const_tbyte_ptr; +} xcharp_u; + +#endif /* UNICODE && _WIN32 */ + +#define curlx_unicodefree(ptr) \ + do { \ + if(ptr) { \ + (free)(ptr); \ + (ptr) = NULL; \ + } \ + } while(0) + +#endif /* HEADER_CURL_MULTIBYTE_H */ diff --git a/third_party/curl/lib/curl_ntlm_core.c b/third_party/curl/lib/curl_ntlm_core.c new file mode 100644 index 0000000000..62628c04ea --- /dev/null +++ b/third_party/curl/lib/curl_ntlm_core.c @@ -0,0 +1,669 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_CURL_NTLM_CORE) + +/* + * NTLM details: + * + * https://davenport.sourceforge.net/ntlm.html + * https://www.innovation.ch/java/ntlm.html + */ + +/* Please keep the SSL backend-specific #if branches in this order: + + 1. USE_OPENSSL + 2. USE_WOLFSSL + 3. USE_GNUTLS + 4. - + 5. USE_MBEDTLS + 6. USE_SECTRANSP + 7. USE_OS400CRYPTO + 8. USE_WIN32_CRYPTO + + This ensures that: + - the same SSL branch gets activated throughout this source + file even if multiple backends are enabled at the same time. + - OpenSSL has higher priority than Windows Crypt, due + to issues with the latter supporting NTLM2Session responses + in NTLM type-3 messages. + */ + +#if defined(USE_OPENSSL) + #include + #if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_DEPRECATED_3_0) + #define USE_OPENSSL_DES + #endif +#endif + +#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) + +#if defined(USE_OPENSSL) +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +#endif + +# if (defined(OPENSSL_VERSION_NUMBER) && \ + (OPENSSL_VERSION_NUMBER < 0x00907001L)) && !defined(USE_WOLFSSL) +# define DES_key_schedule des_key_schedule +# define DES_cblock des_cblock +# define DES_set_odd_parity des_set_odd_parity +# define DES_set_key des_set_key +# define DES_ecb_encrypt des_ecb_encrypt +# define DESKEY(x) x +# define DESKEYARG(x) x +# elif defined(OPENSSL_IS_AWSLC) +# define DES_set_key_unchecked (void)DES_set_key +# define DESKEYARG(x) *x +# define DESKEY(x) &x +# else +# define DESKEYARG(x) *x +# define DESKEY(x) &x +# endif + +#elif defined(USE_GNUTLS) + +# include + +#elif defined(USE_MBEDTLS) + +# include + +#elif defined(USE_SECTRANSP) + +# include +# include + +#elif defined(USE_OS400CRYPTO) +# include "cipher.mih" /* mih/cipher */ +#elif defined(USE_WIN32_CRYPTO) +# include +#else +# error "cannot compile NTLM support without a crypto library with DES." +# define CURL_NTLM_NOT_SUPPORTED +#endif + +#include "urldata.h" +#include "strcase.h" +#include "curl_ntlm_core.h" +#include "curl_md5.h" +#include "curl_hmac.h" +#include "warnless.h" +#include "curl_endian.h" +#include "curl_des.h" +#include "curl_md4.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00" +#define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4) + +#if !defined(CURL_NTLM_NOT_SUPPORTED) +/* +* Turns a 56-bit key into being 64-bit wide. +*/ +static void extend_key_56_to_64(const unsigned char *key_56, char *key) +{ + key[0] = (char)key_56[0]; + key[1] = (char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1)); + key[2] = (char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2)); + key[3] = (char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3)); + key[4] = (char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4)); + key[5] = (char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); + key[6] = (char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); + key[7] = (char) ((key_56[6] << 1) & 0xFF); +} +#endif + +#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) +/* + * Turns a 56-bit key into a 64-bit, odd parity key and sets the key. The + * key schedule ks is also set. + */ +static void setup_des_key(const unsigned char *key_56, + DES_key_schedule DESKEYARG(ks)) +{ + DES_cblock key; + + /* Expand the 56-bit key to 64 bits */ + extend_key_56_to_64(key_56, (char *) &key); + + /* Set the key parity to odd */ + DES_set_odd_parity(&key); + + /* Set the key */ + DES_set_key_unchecked(&key, ks); +} + +#elif defined(USE_GNUTLS) + +static void setup_des_key(const unsigned char *key_56, + struct des_ctx *des) +{ + char key[8]; + + /* Expand the 56-bit key to 64 bits */ + extend_key_56_to_64(key_56, key); + + /* Set the key parity to odd */ + Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); + + /* Set the key */ + des_set_key(des, (const uint8_t *) key); +} + +#elif defined(USE_MBEDTLS) + +static bool encrypt_des(const unsigned char *in, unsigned char *out, + const unsigned char *key_56) +{ + mbedtls_des_context ctx; + char key[8]; + + /* Expand the 56-bit key to 64 bits */ + extend_key_56_to_64(key_56, key); + + /* Set the key parity to odd */ + mbedtls_des_key_set_parity((unsigned char *) key); + + /* Perform the encryption */ + mbedtls_des_init(&ctx); + mbedtls_des_setkey_enc(&ctx, (unsigned char *) key); + return mbedtls_des_crypt_ecb(&ctx, in, out) == 0; +} + +#elif defined(USE_SECTRANSP) + +static bool encrypt_des(const unsigned char *in, unsigned char *out, + const unsigned char *key_56) +{ + char key[8]; + size_t out_len; + CCCryptorStatus err; + + /* Expand the 56-bit key to 64 bits */ + extend_key_56_to_64(key_56, key); + + /* Set the key parity to odd */ + Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); + + /* Perform the encryption */ + err = CCCrypt(kCCEncrypt, kCCAlgorithmDES, kCCOptionECBMode, key, + kCCKeySizeDES, NULL, in, 8 /* inbuflen */, out, + 8 /* outbuflen */, &out_len); + + return err == kCCSuccess; +} + +#elif defined(USE_OS400CRYPTO) + +static bool encrypt_des(const unsigned char *in, unsigned char *out, + const unsigned char *key_56) +{ + char key[8]; + _CIPHER_Control_T ctl; + + /* Setup the cipher control structure */ + ctl.Func_ID = ENCRYPT_ONLY; + ctl.Data_Len = sizeof(key); + + /* Expand the 56-bit key to 64 bits */ + extend_key_56_to_64(key_56, ctl.Crypto_Key); + + /* Set the key parity to odd */ + Curl_des_set_odd_parity((unsigned char *) ctl.Crypto_Key, ctl.Data_Len); + + /* Perform the encryption */ + _CIPHER((_SPCPTR *) &out, &ctl, (_SPCPTR *) &in); + + return TRUE; +} + +#elif defined(USE_WIN32_CRYPTO) + +static bool encrypt_des(const unsigned char *in, unsigned char *out, + const unsigned char *key_56) +{ + HCRYPTPROV hprov; + HCRYPTKEY hkey; + struct { + BLOBHEADER hdr; + unsigned int len; + char key[8]; + } blob; + DWORD len = 8; + + /* Acquire the crypto provider */ + if(!CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return FALSE; + + /* Setup the key blob structure */ + memset(&blob, 0, sizeof(blob)); + blob.hdr.bType = PLAINTEXTKEYBLOB; + blob.hdr.bVersion = 2; + blob.hdr.aiKeyAlg = CALG_DES; + blob.len = sizeof(blob.key); + + /* Expand the 56-bit key to 64 bits */ + extend_key_56_to_64(key_56, blob.key); + + /* Set the key parity to odd */ + Curl_des_set_odd_parity((unsigned char *) blob.key, sizeof(blob.key)); + + /* Import the key */ + if(!CryptImportKey(hprov, (BYTE *) &blob, sizeof(blob), 0, 0, &hkey)) { + CryptReleaseContext(hprov, 0); + + return FALSE; + } + + memcpy(out, in, 8); + + /* Perform the encryption */ + CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len); + + CryptDestroyKey(hkey); + CryptReleaseContext(hprov, 0); + + return TRUE; +} + +#endif /* defined(USE_WIN32_CRYPTO) */ + + /* + * takes a 21 byte array and treats it as 3 56-bit DES keys. The + * 8 byte plaintext is encrypted with each key and the resulting 24 + * bytes are stored in the results array. + */ +void Curl_ntlm_core_lm_resp(const unsigned char *keys, + const unsigned char *plaintext, + unsigned char *results) +{ +#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) + DES_key_schedule ks; + + setup_des_key(keys, DESKEY(ks)); + DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results, + DESKEY(ks), DES_ENCRYPT); + + setup_des_key(keys + 7, DESKEY(ks)); + DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results + 8), + DESKEY(ks), DES_ENCRYPT); + + setup_des_key(keys + 14, DESKEY(ks)); + DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results + 16), + DESKEY(ks), DES_ENCRYPT); +#elif defined(USE_GNUTLS) + struct des_ctx des; + setup_des_key(keys, &des); + des_encrypt(&des, 8, results, plaintext); + setup_des_key(keys + 7, &des); + des_encrypt(&des, 8, results + 8, plaintext); + setup_des_key(keys + 14, &des); + des_encrypt(&des, 8, results + 16, plaintext); +#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ + || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) + encrypt_des(plaintext, results, keys); + encrypt_des(plaintext, results + 8, keys + 7); + encrypt_des(plaintext, results + 16, keys + 14); +#else + (void)keys; + (void)plaintext; + (void)results; +#endif +} + +/* + * Set up lanmanager hashed password + */ +CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, + unsigned char *lmbuffer /* 21 bytes */) +{ + unsigned char pw[14]; +#if !defined(CURL_NTLM_NOT_SUPPORTED) + static const unsigned char magic[] = { + 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */ + }; +#endif + size_t len = CURLMIN(strlen(password), 14); + + Curl_strntoupper((char *)pw, password, len); + memset(&pw[len], 0, 14 - len); + + { + /* Create LanManager hashed password. */ + +#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) + DES_key_schedule ks; + + setup_des_key(pw, DESKEY(ks)); + DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer, + DESKEY(ks), DES_ENCRYPT); + + setup_des_key(pw + 7, DESKEY(ks)); + DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer + 8), + DESKEY(ks), DES_ENCRYPT); +#elif defined(USE_GNUTLS) + struct des_ctx des; + setup_des_key(pw, &des); + des_encrypt(&des, 8, lmbuffer, magic); + setup_des_key(pw + 7, &des); + des_encrypt(&des, 8, lmbuffer + 8, magic); +#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ + || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) + encrypt_des(magic, lmbuffer, pw); + encrypt_des(magic, lmbuffer + 8, pw + 7); +#endif + + memset(lmbuffer + 16, 0, 21 - 16); + } + + return CURLE_OK; +} + +static void ascii_to_unicode_le(unsigned char *dest, const char *src, + size_t srclen) +{ + size_t i; + for(i = 0; i < srclen; i++) { + dest[2 * i] = (unsigned char)src[i]; + dest[2 * i + 1] = '\0'; + } +} + +#if !defined(USE_WINDOWS_SSPI) + +static void ascii_uppercase_to_unicode_le(unsigned char *dest, + const char *src, size_t srclen) +{ + size_t i; + for(i = 0; i < srclen; i++) { + dest[2 * i] = (unsigned char)(Curl_raw_toupper(src[i])); + dest[2 * i + 1] = '\0'; + } +} + +#endif /* !USE_WINDOWS_SSPI */ + +/* + * Set up nt hashed passwords + * @unittest: 1600 + */ +CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, + unsigned char *ntbuffer /* 21 bytes */) +{ + size_t len = strlen(password); + unsigned char *pw; + CURLcode result; + if(len > SIZE_T_MAX/2) /* avoid integer overflow */ + return CURLE_OUT_OF_MEMORY; + pw = len ? malloc(len * 2) : (unsigned char *)strdup(""); + if(!pw) + return CURLE_OUT_OF_MEMORY; + + ascii_to_unicode_le(pw, password, len); + + /* Create NT hashed password. */ + result = Curl_md4it(ntbuffer, pw, 2 * len); + if(!result) + memset(ntbuffer + 16, 0, 21 - 16); + + free(pw); + + return result; +} + +#if !defined(USE_WINDOWS_SSPI) + +/* Timestamp in tenths of a microsecond since January 1, 1601 00:00:00 UTC. */ +struct ms_filetime { + unsigned int dwLowDateTime; + unsigned int dwHighDateTime; +}; + +/* Convert a time_t to an MS FILETIME (MS-DTYP section 2.3.3). */ +static void time2filetime(struct ms_filetime *ft, time_t t) +{ +#if SIZEOF_TIME_T > 4 + t = (t + CURL_OFF_T_C(11644473600)) * 10000000; + ft->dwLowDateTime = (unsigned int) (t & 0xFFFFFFFF); + ft->dwHighDateTime = (unsigned int) (t >> 32); +#else + unsigned int r, s; + unsigned int i; + + ft->dwLowDateTime = (unsigned int)t & 0xFFFFFFFF; + ft->dwHighDateTime = 0; + +# ifndef HAVE_TIME_T_UNSIGNED + /* Extend sign if needed. */ + if(ft->dwLowDateTime & 0x80000000) + ft->dwHighDateTime = ~(unsigned int)0; +# endif + + /* Bias seconds to Jan 1, 1601. + 134774 days = 11644473600 seconds = 0x2B6109100 */ + r = ft->dwLowDateTime; + ft->dwLowDateTime = (ft->dwLowDateTime + 0xB6109100U) & 0xFFFFFFFF; + ft->dwHighDateTime += ft->dwLowDateTime < r? 0x03: 0x02; + + /* Convert to tenths of microseconds. */ + ft->dwHighDateTime *= 10000000; + i = 32; + do { + i -= 8; + s = ((ft->dwLowDateTime >> i) & 0xFF) * (10000000 - 1); + r = (s << i) & 0xFFFFFFFF; + s >>= 1; /* Split shift to avoid width overflow. */ + s >>= 31 - i; + ft->dwLowDateTime = (ft->dwLowDateTime + r) & 0xFFFFFFFF; + if(ft->dwLowDateTime < r) + s++; + ft->dwHighDateTime += s; + } while(i); + ft->dwHighDateTime &= 0xFFFFFFFF; +#endif +} + +/* This creates the NTLMv2 hash by using NTLM hash as the key and Unicode + * (uppercase UserName + Domain) as the data + */ +CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, + const char *domain, size_t domlen, + unsigned char *ntlmhash, + unsigned char *ntlmv2hash) +{ + /* Unicode representation */ + size_t identity_len; + unsigned char *identity; + CURLcode result = CURLE_OK; + + if((userlen > CURL_MAX_INPUT_LENGTH) || (domlen > CURL_MAX_INPUT_LENGTH)) + return CURLE_OUT_OF_MEMORY; + + identity_len = (userlen + domlen) * 2; + identity = malloc(identity_len + 1); + + if(!identity) + return CURLE_OUT_OF_MEMORY; + + ascii_uppercase_to_unicode_le(identity, user, userlen); + ascii_to_unicode_le(identity + (userlen << 1), domain, domlen); + + result = Curl_hmacit(Curl_HMAC_MD5, ntlmhash, 16, identity, identity_len, + ntlmv2hash); + free(identity); + + return result; +} + +/* + * Curl_ntlm_core_mk_ntlmv2_resp() + * + * This creates the NTLMv2 response as set in the ntlm type-3 message. + * + * Parameters: + * + * ntlmv2hash [in] - The ntlmv2 hash (16 bytes) + * challenge_client [in] - The client nonce (8 bytes) + * ntlm [in] - The ntlm data struct being used to read TargetInfo + and Server challenge received in the type-2 message + * ntresp [out] - The address where a pointer to newly allocated + * memory holding the NTLMv2 response. + * ntresp_len [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, + unsigned char *challenge_client, + struct ntlmdata *ntlm, + unsigned char **ntresp, + unsigned int *ntresp_len) +{ +/* NTLMv2 response structure : +------------------------------------------------------------------------------ +0 HMAC MD5 16 bytes +------BLOB-------------------------------------------------------------------- +16 Signature 0x01010000 +20 Reserved long (0x00000000) +24 Timestamp LE, 64-bit signed value representing the number of + tenths of a microsecond since January 1, 1601. +32 Client Nonce 8 bytes +40 Unknown 4 bytes +44 Target Info N bytes (from the type-2 message) +44+N Unknown 4 bytes +------------------------------------------------------------------------------ +*/ + + unsigned int len = 0; + unsigned char *ptr = NULL; + unsigned char hmac_output[HMAC_MD5_LENGTH]; + struct ms_filetime tw; + + CURLcode result = CURLE_OK; + + /* Calculate the timestamp */ +#ifdef DEBUGBUILD + char *force_timestamp = getenv("CURL_FORCETIME"); + if(force_timestamp) + time2filetime(&tw, (time_t) 0); + else +#endif + time2filetime(&tw, time(NULL)); + + /* Calculate the response len */ + len = HMAC_MD5_LENGTH + NTLMv2_BLOB_LEN; + + /* Allocate the response */ + ptr = calloc(1, len); + if(!ptr) + return CURLE_OUT_OF_MEMORY; + + /* Create the BLOB structure */ + msnprintf((char *)ptr + HMAC_MD5_LENGTH, NTLMv2_BLOB_LEN, + "%c%c%c%c" /* NTLMv2_BLOB_SIGNATURE */ + "%c%c%c%c" /* Reserved = 0 */ + "%c%c%c%c%c%c%c%c", /* Timestamp */ + NTLMv2_BLOB_SIGNATURE[0], NTLMv2_BLOB_SIGNATURE[1], + NTLMv2_BLOB_SIGNATURE[2], NTLMv2_BLOB_SIGNATURE[3], + 0, 0, 0, 0, + LONGQUARTET(tw.dwLowDateTime), LONGQUARTET(tw.dwHighDateTime)); + + memcpy(ptr + 32, challenge_client, 8); + if(ntlm->target_info_len) + memcpy(ptr + 44, ntlm->target_info, ntlm->target_info_len); + + /* Concatenate the Type 2 challenge with the BLOB and do HMAC MD5 */ + memcpy(ptr + 8, &ntlm->nonce[0], 8); + result = Curl_hmacit(Curl_HMAC_MD5, ntlmv2hash, HMAC_MD5_LENGTH, ptr + 8, + NTLMv2_BLOB_LEN + 8, hmac_output); + if(result) { + free(ptr); + return result; + } + + /* Concatenate the HMAC MD5 output with the BLOB */ + memcpy(ptr, hmac_output, HMAC_MD5_LENGTH); + + /* Return the response */ + *ntresp = ptr; + *ntresp_len = len; + + return result; +} + +/* + * Curl_ntlm_core_mk_lmv2_resp() + * + * This creates the LMv2 response as used in the ntlm type-3 message. + * + * Parameters: + * + * ntlmv2hash [in] - The ntlmv2 hash (16 bytes) + * challenge_client [in] - The client nonce (8 bytes) + * challenge_client [in] - The server challenge (8 bytes) + * lmresp [out] - The LMv2 response (24 bytes) + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, + unsigned char *challenge_client, + unsigned char *challenge_server, + unsigned char *lmresp) +{ + unsigned char data[16]; + unsigned char hmac_output[16]; + CURLcode result = CURLE_OK; + + memcpy(&data[0], challenge_server, 8); + memcpy(&data[8], challenge_client, 8); + + result = Curl_hmacit(Curl_HMAC_MD5, ntlmv2hash, 16, &data[0], 16, + hmac_output); + if(result) + return result; + + /* Concatenate the HMAC MD5 output with the client nonce */ + memcpy(lmresp, hmac_output, 16); + memcpy(lmresp + 16, challenge_client, 8); + + return result; +} + +#endif /* !USE_WINDOWS_SSPI */ + +#endif /* USE_CURL_NTLM_CORE */ diff --git a/third_party/curl/lib/curl_ntlm_core.h b/third_party/curl/lib/curl_ntlm_core.h new file mode 100644 index 0000000000..e2e4b1bd43 --- /dev/null +++ b/third_party/curl/lib/curl_ntlm_core.h @@ -0,0 +1,72 @@ +#ifndef HEADER_CURL_NTLM_CORE_H +#define HEADER_CURL_NTLM_CORE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_CURL_NTLM_CORE) + +/* Helpers to generate function byte arguments in little endian order */ +#define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)) +#define LONGQUARTET(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)), \ + ((int)(((x) >> 16) & 0xff)), ((int)(((x) >> 24) & 0xff)) + +void Curl_ntlm_core_lm_resp(const unsigned char *keys, + const unsigned char *plaintext, + unsigned char *results); + +CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, + unsigned char *lmbuffer /* 21 bytes */); + +CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, + unsigned char *ntbuffer /* 21 bytes */); + +#if !defined(USE_WINDOWS_SSPI) + +CURLcode Curl_hmac_md5(const unsigned char *key, unsigned int keylen, + const unsigned char *data, unsigned int datalen, + unsigned char *output); + +CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, + const char *domain, size_t domlen, + unsigned char *ntlmhash, + unsigned char *ntlmv2hash); + +CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, + unsigned char *challenge_client, + struct ntlmdata *ntlm, + unsigned char **ntresp, + unsigned int *ntresp_len); + +CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, + unsigned char *challenge_client, + unsigned char *challenge_server, + unsigned char *lmresp); + +#endif /* !USE_WINDOWS_SSPI */ + +#endif /* USE_CURL_NTLM_CORE */ + +#endif /* HEADER_CURL_NTLM_CORE_H */ diff --git a/third_party/curl/lib/curl_path.c b/third_party/curl/lib/curl_path.c new file mode 100644 index 0000000000..144f8803d3 --- /dev/null +++ b/third_party/curl/lib/curl_path.c @@ -0,0 +1,203 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl AND ISC + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_SSH) + +#include +#include "curl_memory.h" +#include "curl_path.h" +#include "escape.h" +#include "memdebug.h" + +#define MAX_SSHPATH_LEN 100000 /* arbitrary */ + +/* figure out the path to work with in this particular request */ +CURLcode Curl_getworkingpath(struct Curl_easy *data, + char *homedir, /* when SFTP is used */ + char **path) /* returns the allocated + real path to work with */ +{ + char *working_path; + size_t working_path_len; + struct dynbuf npath; + CURLcode result = + Curl_urldecode(data->state.up.path, 0, &working_path, + &working_path_len, REJECT_ZERO); + if(result) + return result; + + /* new path to switch to in case we need to */ + Curl_dyn_init(&npath, MAX_SSHPATH_LEN); + + /* Check for /~/, indicating relative to the user's home directory */ + if((data->conn->handler->protocol & CURLPROTO_SCP) && + (working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) { + /* It is referenced to the home directory, so strip the leading '/~/' */ + if(Curl_dyn_addn(&npath, &working_path[3], working_path_len - 3)) { + free(working_path); + return CURLE_OUT_OF_MEMORY; + } + } + else if((data->conn->handler->protocol & CURLPROTO_SFTP) && + (!strcmp("/~", working_path) || + ((working_path_len > 2) && !memcmp(working_path, "/~/", 3)))) { + if(Curl_dyn_add(&npath, homedir)) { + free(working_path); + return CURLE_OUT_OF_MEMORY; + } + if(working_path_len > 2) { + size_t len; + const char *p; + int copyfrom = 3; + /* Copy a separating '/' if homedir does not end with one */ + len = Curl_dyn_len(&npath); + p = Curl_dyn_ptr(&npath); + if(len && (p[len-1] != '/')) + copyfrom = 2; + + if(Curl_dyn_addn(&npath, + &working_path[copyfrom], working_path_len - copyfrom)) { + free(working_path); + return CURLE_OUT_OF_MEMORY; + } + } + } + + if(Curl_dyn_len(&npath)) { + free(working_path); + + /* store the pointer for the caller to receive */ + *path = Curl_dyn_ptr(&npath); + } + else + *path = working_path; + + return CURLE_OK; +} + +/* The original get_pathname() function came from OpenSSH sftp.c version + 4.6p1. */ +/* + * Copyright (c) 2001-2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define MAX_PATHLENGTH 65535 /* arbitrary long */ + +CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir) +{ + const char *cp = *cpp, *end; + char quot; + unsigned int i; + static const char WHITESPACE[] = " \t\r\n"; + struct dynbuf out; + CURLcode result; + + DEBUGASSERT(homedir); + *path = NULL; + *cpp = NULL; + if(!*cp || !homedir) + return CURLE_QUOTE_ERROR; + + Curl_dyn_init(&out, MAX_PATHLENGTH); + + /* Ignore leading whitespace */ + cp += strspn(cp, WHITESPACE); + + /* Check for quoted filenames */ + if(*cp == '\"' || *cp == '\'') { + quot = *cp++; + + /* Search for terminating quote, unescape some chars */ + for(i = 0; i <= strlen(cp); i++) { + if(cp[i] == quot) { /* Found quote */ + i++; + break; + } + if(cp[i] == '\0') { /* End of string */ + goto fail; + } + if(cp[i] == '\\') { /* Escaped characters */ + i++; + if(cp[i] != '\'' && cp[i] != '\"' && + cp[i] != '\\') { + goto fail; + } + } + result = Curl_dyn_addn(&out, &cp[i], 1); + if(result) + return result; + } + + if(!Curl_dyn_len(&out)) + goto fail; + + /* return pointer to second parameter if it exists */ + *cpp = &cp[i] + strspn(&cp[i], WHITESPACE); + } + else { + /* Read to end of filename - either to whitespace or terminator */ + end = strpbrk(cp, WHITESPACE); + if(!end) + end = strchr(cp, '\0'); + + /* return pointer to second parameter if it exists */ + *cpp = end + strspn(end, WHITESPACE); + + /* Handling for relative path - prepend home directory */ + if(cp[0] == '/' && cp[1] == '~' && cp[2] == '/') { + result = Curl_dyn_add(&out, homedir); + if(!result) + result = Curl_dyn_addn(&out, "/", 1); + if(result) + return result; + cp += 3; + } + /* Copy path name up until first "whitespace" */ + result = Curl_dyn_addn(&out, cp, (end - cp)); + if(result) + return result; + } + *path = Curl_dyn_ptr(&out); + return CURLE_OK; + +fail: + Curl_dyn_free(&out); + return CURLE_QUOTE_ERROR; +} + +#endif /* if SSH is used */ diff --git a/third_party/curl/lib/curl_path.h b/third_party/curl/lib/curl_path.h new file mode 100644 index 0000000000..6fdb2fddff --- /dev/null +++ b/third_party/curl/lib/curl_path.h @@ -0,0 +1,49 @@ +#ifndef HEADER_CURL_PATH_H +#define HEADER_CURL_PATH_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include +#include "urldata.h" + +#ifdef _WIN32 +# undef PATH_MAX +# define PATH_MAX MAX_PATH +# ifndef R_OK +# define R_OK 4 +# endif +#endif + +#ifndef PATH_MAX +#define PATH_MAX 1024 /* just an extra precaution since there are systems that + have their definition hidden well */ +#endif + +CURLcode Curl_getworkingpath(struct Curl_easy *data, + char *homedir, + char **path); + +CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir); +#endif /* HEADER_CURL_PATH_H */ diff --git a/third_party/curl/lib/curl_printf.h b/third_party/curl/lib/curl_printf.h new file mode 100644 index 0000000000..c2457d2a64 --- /dev/null +++ b/third_party/curl/lib/curl_printf.h @@ -0,0 +1,55 @@ +#ifndef HEADER_CURL_PRINTF_H +#define HEADER_CURL_PRINTF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * This header should be included by ALL code in libcurl that uses any + * *rintf() functions. + */ + +#include + +#define MERR_OK 0 +#define MERR_MEM 1 +#define MERR_TOO_LARGE 2 + +# undef printf +# undef fprintf +# undef msnprintf +# undef vprintf +# undef vfprintf +# undef vsnprintf +# undef mvsnprintf +# undef aprintf +# undef vaprintf +# define printf curl_mprintf +# define fprintf curl_mfprintf +# define msnprintf curl_msnprintf +# define vprintf curl_mvprintf +# define vfprintf curl_mvfprintf +# define mvsnprintf curl_mvsnprintf +# define aprintf curl_maprintf +# define vaprintf curl_mvaprintf +#endif /* HEADER_CURL_PRINTF_H */ diff --git a/third_party/curl/lib/curl_range.c b/third_party/curl/lib/curl_range.c new file mode 100644 index 0000000000..d499953c9e --- /dev/null +++ b/third_party/curl/lib/curl_range.c @@ -0,0 +1,96 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include +#include "curl_range.h" +#include "sendf.h" +#include "strtoofft.h" + +/* Only include this function if one or more of FTP, FILE are enabled. */ +#if !defined(CURL_DISABLE_FTP) || !defined(CURL_DISABLE_FILE) + + /* + Check if this is a range download, and if so, set the internal variables + properly. + */ +CURLcode Curl_range(struct Curl_easy *data) +{ + curl_off_t from, to; + char *ptr; + char *ptr2; + + if(data->state.use_range && data->state.range) { + CURLofft from_t; + CURLofft to_t; + from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from); + if(from_t == CURL_OFFT_FLOW) + return CURLE_RANGE_ERROR; + while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) + ptr++; + to_t = curlx_strtoofft(ptr, &ptr2, 10, &to); + if(to_t == CURL_OFFT_FLOW) + return CURLE_RANGE_ERROR; + if((to_t == CURL_OFFT_INVAL) && !from_t) { + /* X - */ + data->state.resume_from = from; + DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file", + from)); + } + else if((from_t == CURL_OFFT_INVAL) && !to_t) { + /* -Y */ + data->req.maxdownload = to; + data->state.resume_from = -to; + DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes", + to)); + } + else { + /* X-Y */ + curl_off_t totalsize; + + /* Ensure the range is sensible - to should follow from. */ + if(from > to) + return CURLE_RANGE_ERROR; + + totalsize = to - from; + if(totalsize == CURL_OFF_T_MAX) + return CURLE_RANGE_ERROR; + + data->req.maxdownload = totalsize + 1; /* include last byte */ + data->state.resume_from = from; + DEBUGF(infof(data, "RANGE from %" CURL_FORMAT_CURL_OFF_T + " getting %" CURL_FORMAT_CURL_OFF_T " bytes", + from, data->req.maxdownload)); + } + DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T + " to %" CURL_FORMAT_CURL_OFF_T ", totally %" + CURL_FORMAT_CURL_OFF_T " bytes", + from, to, data->req.maxdownload)); + } + else + data->req.maxdownload = -1; + return CURLE_OK; +} + +#endif diff --git a/third_party/curl/lib/curl_range.h b/third_party/curl/lib/curl_range.h new file mode 100644 index 0000000000..77679e2b94 --- /dev/null +++ b/third_party/curl/lib/curl_range.h @@ -0,0 +1,31 @@ +#ifndef HEADER_CURL_RANGE_H +#define HEADER_CURL_RANGE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "urldata.h" + +CURLcode Curl_range(struct Curl_easy *data); +#endif /* HEADER_CURL_RANGE_H */ diff --git a/third_party/curl/lib/curl_rtmp.c b/third_party/curl/lib/curl_rtmp.c new file mode 100644 index 0000000000..54d4f979f7 --- /dev/null +++ b/third_party/curl/lib/curl_rtmp.c @@ -0,0 +1,362 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Howard Chu, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_LIBRTMP + +#include "curl_rtmp.h" +#include "urldata.h" +#include "nonblock.h" /* for curlx_nonblock */ +#include "progress.h" /* for Curl_pgrsSetUploadSize */ +#include "transfer.h" +#include "warnless.h" +#include +#include + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if defined(_WIN32) && !defined(USE_LWIPSOCK) +#define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e) +#define SET_RCVTIMEO(tv,s) int tv = s*1000 +#elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD) +#define SET_RCVTIMEO(tv,s) int tv = s*1000 +#else +#define SET_RCVTIMEO(tv,s) struct timeval tv = {s,0} +#endif + +#define DEF_BUFTIME (2*60*60*1000) /* 2 hours */ + +static CURLcode rtmp_setup_connection(struct Curl_easy *data, + struct connectdata *conn); +static CURLcode rtmp_do(struct Curl_easy *data, bool *done); +static CURLcode rtmp_done(struct Curl_easy *data, CURLcode, bool premature); +static CURLcode rtmp_connect(struct Curl_easy *data, bool *done); +static CURLcode rtmp_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead); + +static Curl_recv rtmp_recv; +static Curl_send rtmp_send; + +/* + * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu + */ + +const struct Curl_handler Curl_handler_rtmp = { + "rtmp", /* scheme */ + rtmp_setup_connection, /* setup_connection */ + rtmp_do, /* do_it */ + rtmp_done, /* done */ + ZERO_NULL, /* do_more */ + rtmp_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + rtmp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_RTMP, /* defport */ + CURLPROTO_RTMP, /* protocol */ + CURLPROTO_RTMP, /* family */ + PROTOPT_NONE /* flags */ +}; + +const struct Curl_handler Curl_handler_rtmpt = { + "rtmpt", /* scheme */ + rtmp_setup_connection, /* setup_connection */ + rtmp_do, /* do_it */ + rtmp_done, /* done */ + ZERO_NULL, /* do_more */ + rtmp_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + rtmp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_RTMPT, /* defport */ + CURLPROTO_RTMPT, /* protocol */ + CURLPROTO_RTMPT, /* family */ + PROTOPT_NONE /* flags */ +}; + +const struct Curl_handler Curl_handler_rtmpe = { + "rtmpe", /* scheme */ + rtmp_setup_connection, /* setup_connection */ + rtmp_do, /* do_it */ + rtmp_done, /* done */ + ZERO_NULL, /* do_more */ + rtmp_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + rtmp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_RTMP, /* defport */ + CURLPROTO_RTMPE, /* protocol */ + CURLPROTO_RTMPE, /* family */ + PROTOPT_NONE /* flags */ +}; + +const struct Curl_handler Curl_handler_rtmpte = { + "rtmpte", /* scheme */ + rtmp_setup_connection, /* setup_connection */ + rtmp_do, /* do_it */ + rtmp_done, /* done */ + ZERO_NULL, /* do_more */ + rtmp_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + rtmp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_RTMPT, /* defport */ + CURLPROTO_RTMPTE, /* protocol */ + CURLPROTO_RTMPTE, /* family */ + PROTOPT_NONE /* flags */ +}; + +const struct Curl_handler Curl_handler_rtmps = { + "rtmps", /* scheme */ + rtmp_setup_connection, /* setup_connection */ + rtmp_do, /* do_it */ + rtmp_done, /* done */ + ZERO_NULL, /* do_more */ + rtmp_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + rtmp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_RTMPS, /* defport */ + CURLPROTO_RTMPS, /* protocol */ + CURLPROTO_RTMP, /* family */ + PROTOPT_NONE /* flags */ +}; + +const struct Curl_handler Curl_handler_rtmpts = { + "rtmpts", /* scheme */ + rtmp_setup_connection, /* setup_connection */ + rtmp_do, /* do_it */ + rtmp_done, /* done */ + ZERO_NULL, /* do_more */ + rtmp_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + rtmp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_RTMPS, /* defport */ + CURLPROTO_RTMPTS, /* protocol */ + CURLPROTO_RTMPT, /* family */ + PROTOPT_NONE /* flags */ +}; + +static CURLcode rtmp_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + RTMP *r = RTMP_Alloc(); + if(!r) + return CURLE_OUT_OF_MEMORY; + + RTMP_Init(r); + RTMP_SetBufferMS(r, DEF_BUFTIME); + if(!RTMP_SetupURL(r, data->state.url)) { + RTMP_Free(r); + return CURLE_URL_MALFORMAT; + } + conn->proto.rtmp = r; + return CURLE_OK; +} + +static CURLcode rtmp_connect(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + RTMP *r = conn->proto.rtmp; + SET_RCVTIMEO(tv, 10); + + r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET]; + + /* We have to know if it is a write before we send the + * connect request packet + */ + if(data->state.upload) + r->Link.protocol |= RTMP_FEATURE_WRITE; + + /* For plain streams, use the buffer toggle trick to keep data flowing */ + if(!(r->Link.lFlags & RTMP_LF_LIVE) && + !(r->Link.protocol & RTMP_FEATURE_HTTP)) + r->Link.lFlags |= RTMP_LF_BUFX; + + (void)curlx_nonblock(r->m_sb.sb_socket, FALSE); + setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, + (char *)&tv, sizeof(tv)); + + if(!RTMP_Connect1(r, NULL)) + return CURLE_FAILED_INIT; + + /* Clients must send a periodic BytesReceived report to the server */ + r->m_bSendCounter = true; + + *done = TRUE; + conn->recv[FIRSTSOCKET] = rtmp_recv; + conn->send[FIRSTSOCKET] = rtmp_send; + return CURLE_OK; +} + +static CURLcode rtmp_do(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + RTMP *r = conn->proto.rtmp; + + if(!RTMP_ConnectStream(r, 0)) + return CURLE_FAILED_INIT; + + if(data->state.upload) { + Curl_pgrsSetUploadSize(data, data->state.infilesize); + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); + } + else + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); + *done = TRUE; + return CURLE_OK; +} + +static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + (void)data; /* unused */ + (void)status; /* unused */ + (void)premature; /* unused */ + + return CURLE_OK; +} + +static CURLcode rtmp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + RTMP *r = conn->proto.rtmp; + (void)data; + (void)dead_connection; + if(r) { + conn->proto.rtmp = NULL; + RTMP_Close(r); + RTMP_Free(r); + } + return CURLE_OK; +} + +static ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf, + size_t len, CURLcode *err) +{ + struct connectdata *conn = data->conn; + RTMP *r = conn->proto.rtmp; + ssize_t nread; + + (void)sockindex; /* unused */ + + nread = RTMP_Read(r, buf, curlx_uztosi(len)); + if(nread < 0) { + if(r->m_read.status == RTMP_READ_COMPLETE || + r->m_read.status == RTMP_READ_EOF) { + data->req.size = data->req.bytecount; + nread = 0; + } + else + *err = CURLE_RECV_ERROR; + } + return nread; +} + +static ssize_t rtmp_send(struct Curl_easy *data, int sockindex, + const void *buf, size_t len, CURLcode *err) +{ + struct connectdata *conn = data->conn; + RTMP *r = conn->proto.rtmp; + ssize_t num; + + (void)sockindex; /* unused */ + + num = RTMP_Write(r, (char *)buf, curlx_uztosi(len)); + if(num < 0) + *err = CURLE_SEND_ERROR; + + return num; +} + +void Curl_rtmp_version(char *version, size_t len) +{ + char suff[2]; + if(RTMP_LIB_VERSION & 0xff) { + suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1; + suff[1] = '\0'; + } + else + suff[0] = '\0'; + + msnprintf(version, len, "librtmp/%d.%d%s", + RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff, + suff); +} + +#endif /* USE_LIBRTMP */ diff --git a/third_party/curl/lib/curl_rtmp.h b/third_party/curl/lib/curl_rtmp.h new file mode 100644 index 0000000000..339d3a4384 --- /dev/null +++ b/third_party/curl/lib/curl_rtmp.h @@ -0,0 +1,37 @@ +#ifndef HEADER_CURL_RTMP_H +#define HEADER_CURL_RTMP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Howard Chu, + * + * 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 + * + ***************************************************************************/ +#ifdef USE_LIBRTMP +extern const struct Curl_handler Curl_handler_rtmp; +extern const struct Curl_handler Curl_handler_rtmpt; +extern const struct Curl_handler Curl_handler_rtmpe; +extern const struct Curl_handler Curl_handler_rtmpte; +extern const struct Curl_handler Curl_handler_rtmps; +extern const struct Curl_handler Curl_handler_rtmpts; + +void Curl_rtmp_version(char *version, size_t len); +#endif + +#endif /* HEADER_CURL_RTMP_H */ diff --git a/third_party/curl/lib/curl_sasl.c b/third_party/curl/lib/curl_sasl.c new file mode 100644 index 0000000000..24f8c8c53c --- /dev/null +++ b/third_party/curl/lib/curl_sasl.c @@ -0,0 +1,760 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC2195 CRAM-MD5 authentication + * RFC2617 Basic and Digest Access Authentication + * RFC2831 DIGEST-MD5 authentication + * RFC4422 Simple Authentication and Security Layer (SASL) + * RFC4616 PLAIN authentication + * RFC5802 SCRAM-SHA-1 authentication + * RFC7677 SCRAM-SHA-256 authentication + * RFC6749 OAuth 2.0 Authorization Framework + * RFC7628 A Set of SASL Mechanisms for OAuth + * Draft LOGIN SASL Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_POP3) || \ + (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) + +#include +#include "urldata.h" + +#include "curl_base64.h" +#include "curl_md5.h" +#include "vauth/vauth.h" +#include "cfilters.h" +#include "vtls/vtls.h" +#include "curl_hmac.h" +#include "curl_sasl.h" +#include "warnless.h" +#include "strtok.h" +#include "sendf.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* Supported mechanisms */ +static const struct { + const char *name; /* Name */ + size_t len; /* Name length */ + unsigned short bit; /* Flag bit */ +} mechtable[] = { + { "LOGIN", 5, SASL_MECH_LOGIN }, + { "PLAIN", 5, SASL_MECH_PLAIN }, + { "CRAM-MD5", 8, SASL_MECH_CRAM_MD5 }, + { "DIGEST-MD5", 10, SASL_MECH_DIGEST_MD5 }, + { "GSSAPI", 6, SASL_MECH_GSSAPI }, + { "EXTERNAL", 8, SASL_MECH_EXTERNAL }, + { "NTLM", 4, SASL_MECH_NTLM }, + { "XOAUTH2", 7, SASL_MECH_XOAUTH2 }, + { "OAUTHBEARER", 11, SASL_MECH_OAUTHBEARER }, + { "SCRAM-SHA-1", 11, SASL_MECH_SCRAM_SHA_1 }, + { "SCRAM-SHA-256",13, SASL_MECH_SCRAM_SHA_256 }, + { ZERO_NULL, 0, 0 } +}; + +/* + * Curl_sasl_cleanup() + * + * This is used to cleanup any libraries or curl modules used by the sasl + * functions. + * + * Parameters: + * + * conn [in] - The connection data. + * authused [in] - The authentication mechanism used. + */ +void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused) +{ + (void)conn; + (void)authused; + +#if defined(USE_KERBEROS5) + /* Cleanup the gssapi structure */ + if(authused == SASL_MECH_GSSAPI) { + Curl_auth_cleanup_gssapi(&conn->krb5); + } +#endif + +#if defined(USE_GSASL) + /* Cleanup the GSASL structure */ + if(authused & (SASL_MECH_SCRAM_SHA_1 | SASL_MECH_SCRAM_SHA_256)) { + Curl_auth_gsasl_cleanup(&conn->gsasl); + } +#endif + +#if defined(USE_NTLM) + /* Cleanup the NTLM structure */ + if(authused == SASL_MECH_NTLM) { + Curl_auth_cleanup_ntlm(&conn->ntlm); + } +#endif +} + +/* + * Curl_sasl_decode_mech() + * + * Convert a SASL mechanism name into a token. + * + * Parameters: + * + * ptr [in] - The mechanism string. + * maxlen [in] - Maximum mechanism string length. + * len [out] - If not NULL, effective name length. + * + * Returns the SASL mechanism token or 0 if no match. + */ +unsigned short Curl_sasl_decode_mech(const char *ptr, size_t maxlen, + size_t *len) +{ + unsigned int i; + char c; + + for(i = 0; mechtable[i].name; i++) { + if(maxlen >= mechtable[i].len && + !memcmp(ptr, mechtable[i].name, mechtable[i].len)) { + if(len) + *len = mechtable[i].len; + + if(maxlen == mechtable[i].len) + return mechtable[i].bit; + + c = ptr[mechtable[i].len]; + if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_') + return mechtable[i].bit; + } + } + + return 0; +} + +/* + * Curl_sasl_parse_url_auth_option() + * + * Parse the URL login options. + */ +CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, + const char *value, size_t len) +{ + CURLcode result = CURLE_OK; + size_t mechlen; + + if(!len) + return CURLE_URL_MALFORMAT; + + if(sasl->resetprefs) { + sasl->resetprefs = FALSE; + sasl->prefmech = SASL_AUTH_NONE; + } + + if(!strncmp(value, "*", len)) + sasl->prefmech = SASL_AUTH_DEFAULT; + else { + unsigned short mechbit = Curl_sasl_decode_mech(value, len, &mechlen); + if(mechbit && mechlen == len) + sasl->prefmech |= mechbit; + else + result = CURLE_URL_MALFORMAT; + } + + return result; +} + +/* + * Curl_sasl_init() + * + * Initializes the SASL structure. + */ +void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data, + const struct SASLproto *params) +{ + unsigned long auth = data->set.httpauth; + + sasl->params = params; /* Set protocol dependent parameters */ + sasl->state = SASL_STOP; /* Not yet running */ + sasl->curmech = NULL; /* No mechanism yet. */ + sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */ + sasl->prefmech = params->defmechs; /* Default preferred mechanisms */ + sasl->authused = SASL_AUTH_NONE; /* The authentication mechanism used */ + sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */ + sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */ + sasl->force_ir = FALSE; /* Respect external option */ + + if(auth != CURLAUTH_BASIC) { + unsigned short mechs = SASL_AUTH_NONE; + + /* If some usable http authentication options have been set, determine + new defaults from them. */ + if(auth & CURLAUTH_BASIC) + mechs |= SASL_MECH_PLAIN | SASL_MECH_LOGIN; + if(auth & CURLAUTH_DIGEST) + mechs |= SASL_MECH_DIGEST_MD5; + if(auth & CURLAUTH_NTLM) + mechs |= SASL_MECH_NTLM; + if(auth & CURLAUTH_BEARER) + mechs |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2; + if(auth & CURLAUTH_GSSAPI) + mechs |= SASL_MECH_GSSAPI; + + if(mechs != SASL_AUTH_NONE) + sasl->prefmech = mechs; + } +} + +/* + * sasl_state() + * + * This is the ONLY way to change SASL state! + */ +static void sasl_state(struct SASL *sasl, struct Curl_easy *data, + saslstate newstate) +{ +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char * const names[]={ + "STOP", + "PLAIN", + "LOGIN", + "LOGIN_PASSWD", + "EXTERNAL", + "CRAMMD5", + "DIGESTMD5", + "DIGESTMD5_RESP", + "NTLM", + "NTLM_TYPE2MSG", + "GSSAPI", + "GSSAPI_TOKEN", + "GSSAPI_NO_DATA", + "OAUTH2", + "OAUTH2_RESP", + "GSASL", + "CANCEL", + "FINAL", + /* LAST */ + }; + + if(sasl->state != newstate) + infof(data, "SASL %p state change from %s to %s", + (void *)sasl, names[sasl->state], names[newstate]); +#else + (void) data; +#endif + + sasl->state = newstate; +} + +#if defined(USE_NTLM) || defined(USE_GSASL) || defined(USE_KERBEROS5) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) +/* Get the SASL server message and convert it to binary. */ +static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data, + struct bufref *out) +{ + CURLcode result = CURLE_OK; + + result = sasl->params->getmessage(data, out); + if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) { + unsigned char *msg; + size_t msglen; + const char *serverdata = (const char *) Curl_bufref_ptr(out); + + if(!*serverdata || *serverdata == '=') + Curl_bufref_set(out, NULL, 0, NULL); + else { + result = Curl_base64_decode(serverdata, &msg, &msglen); + if(!result) + Curl_bufref_set(out, msg, msglen, curl_free); + } + } + return result; +} +#endif + +/* Encode the outgoing SASL message. */ +static CURLcode build_message(struct SASL *sasl, struct bufref *msg) +{ + CURLcode result = CURLE_OK; + + if(sasl->params->flags & SASL_FLAG_BASE64) { + if(!Curl_bufref_ptr(msg)) /* Empty message. */ + Curl_bufref_set(msg, "", 0, NULL); + else if(!Curl_bufref_len(msg)) /* Explicit empty response. */ + Curl_bufref_set(msg, "=", 1, NULL); + else { + char *base64; + size_t base64len; + + result = Curl_base64_encode((const char *) Curl_bufref_ptr(msg), + Curl_bufref_len(msg), &base64, &base64len); + if(!result) + Curl_bufref_set(msg, base64, base64len, curl_free); + } + } + + return result; +} + +/* + * Curl_sasl_can_authenticate() + * + * Check if we have enough auth data and capabilities to authenticate. + */ +bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) +{ + /* Have credentials been provided? */ + if(data->state.aptr.user) + return TRUE; + + /* EXTERNAL can authenticate without a username and/or password */ + if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL) + return TRUE; + + return FALSE; +} + +/* + * Curl_sasl_start() + * + * Calculate the required login details for SASL authentication. + */ +CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, + bool force_ir, saslprogress *progress) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + unsigned short enabledmechs; + const char *mech = NULL; + struct bufref resp; + saslstate state1 = SASL_STOP; + saslstate state2 = SASL_FINAL; + const char *hostname, *disp_hostname; + int port; +#if defined(USE_KERBEROS5) || defined(USE_NTLM) + const char *service = data->set.str[STRING_SERVICE_NAME] ? + data->set.str[STRING_SERVICE_NAME] : + sasl->params->service; +#endif + const char *oauth_bearer = data->set.str[STRING_BEARER]; + struct bufref nullmsg; + + Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port); + Curl_bufref_init(&nullmsg); + Curl_bufref_init(&resp); + sasl->force_ir = force_ir; /* Latch for future use */ + sasl->authused = 0; /* No mechanism used yet */ + enabledmechs = sasl->authmechs & sasl->prefmech; + *progress = SASL_IDLE; + + /* Calculate the supported authentication mechanism, by decreasing order of + security, as well as the initial response where appropriate */ + if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) { + mech = SASL_MECH_STRING_EXTERNAL; + state1 = SASL_EXTERNAL; + sasl->authused = SASL_MECH_EXTERNAL; + + if(force_ir || data->set.sasl_ir) + Curl_auth_create_external_message(conn->user, &resp); + } + else if(data->state.aptr.user) { +#if defined(USE_KERBEROS5) + if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && + Curl_auth_user_contains_domain(conn->user)) { + sasl->mutual_auth = FALSE; + mech = SASL_MECH_STRING_GSSAPI; + state1 = SASL_GSSAPI; + state2 = SASL_GSSAPI_TOKEN; + sasl->authused = SASL_MECH_GSSAPI; + + if(force_ir || data->set.sasl_ir) + result = Curl_auth_create_gssapi_user_message(data, conn->user, + conn->passwd, + service, + conn->host.name, + sasl->mutual_auth, + NULL, &conn->krb5, + &resp); + } + else +#endif +#ifdef USE_GSASL + if((enabledmechs & SASL_MECH_SCRAM_SHA_256) && + Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256, + &conn->gsasl)) { + mech = SASL_MECH_STRING_SCRAM_SHA_256; + sasl->authused = SASL_MECH_SCRAM_SHA_256; + state1 = SASL_GSASL; + state2 = SASL_GSASL; + + result = Curl_auth_gsasl_start(data, conn->user, + conn->passwd, &conn->gsasl); + if(result == CURLE_OK && (force_ir || data->set.sasl_ir)) + result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp); + } + else if((enabledmechs & SASL_MECH_SCRAM_SHA_1) && + Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1, + &conn->gsasl)) { + mech = SASL_MECH_STRING_SCRAM_SHA_1; + sasl->authused = SASL_MECH_SCRAM_SHA_1; + state1 = SASL_GSASL; + state2 = SASL_GSASL; + + result = Curl_auth_gsasl_start(data, conn->user, + conn->passwd, &conn->gsasl); + if(result == CURLE_OK && (force_ir || data->set.sasl_ir)) + result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp); + } + else +#endif +#ifndef CURL_DISABLE_DIGEST_AUTH + if((enabledmechs & SASL_MECH_DIGEST_MD5) && + Curl_auth_is_digest_supported()) { + mech = SASL_MECH_STRING_DIGEST_MD5; + state1 = SASL_DIGESTMD5; + sasl->authused = SASL_MECH_DIGEST_MD5; + } + else if(enabledmechs & SASL_MECH_CRAM_MD5) { + mech = SASL_MECH_STRING_CRAM_MD5; + state1 = SASL_CRAMMD5; + sasl->authused = SASL_MECH_CRAM_MD5; + } + else +#endif +#ifdef USE_NTLM + if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) { + mech = SASL_MECH_STRING_NTLM; + state1 = SASL_NTLM; + state2 = SASL_NTLM_TYPE2MSG; + sasl->authused = SASL_MECH_NTLM; + + if(force_ir || data->set.sasl_ir) + result = Curl_auth_create_ntlm_type1_message(data, + conn->user, conn->passwd, + service, + hostname, + &conn->ntlm, &resp); + } + else +#endif + if((enabledmechs & SASL_MECH_OAUTHBEARER) && oauth_bearer) { + mech = SASL_MECH_STRING_OAUTHBEARER; + state1 = SASL_OAUTH2; + state2 = SASL_OAUTH2_RESP; + sasl->authused = SASL_MECH_OAUTHBEARER; + + if(force_ir || data->set.sasl_ir) + result = Curl_auth_create_oauth_bearer_message(conn->user, + hostname, + port, + oauth_bearer, + &resp); + } + else if((enabledmechs & SASL_MECH_XOAUTH2) && oauth_bearer) { + mech = SASL_MECH_STRING_XOAUTH2; + state1 = SASL_OAUTH2; + sasl->authused = SASL_MECH_XOAUTH2; + + if(force_ir || data->set.sasl_ir) + result = Curl_auth_create_xoauth_bearer_message(conn->user, + oauth_bearer, + &resp); + } + else if(enabledmechs & SASL_MECH_PLAIN) { + mech = SASL_MECH_STRING_PLAIN; + state1 = SASL_PLAIN; + sasl->authused = SASL_MECH_PLAIN; + + if(force_ir || data->set.sasl_ir) + result = Curl_auth_create_plain_message(conn->sasl_authzid, + conn->user, conn->passwd, + &resp); + } + else if(enabledmechs & SASL_MECH_LOGIN) { + mech = SASL_MECH_STRING_LOGIN; + state1 = SASL_LOGIN; + state2 = SASL_LOGIN_PASSWD; + sasl->authused = SASL_MECH_LOGIN; + + if(force_ir || data->set.sasl_ir) + Curl_auth_create_login_message(conn->user, &resp); + } + } + + if(!result && mech) { + sasl->curmech = mech; + if(Curl_bufref_ptr(&resp)) + result = build_message(sasl, &resp); + + if(sasl->params->maxirlen && + strlen(mech) + Curl_bufref_len(&resp) > sasl->params->maxirlen) + Curl_bufref_free(&resp); + + if(!result) + result = sasl->params->sendauth(data, mech, &resp); + + if(!result) { + *progress = SASL_INPROGRESS; + sasl_state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1); + } + } + + Curl_bufref_free(&resp); + return result; +} + +/* + * Curl_sasl_continue() + * + * Continue the authentication. + */ +CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, + int code, saslprogress *progress) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + saslstate newstate = SASL_FINAL; + struct bufref resp; + const char *hostname, *disp_hostname; + int port; +#if defined(USE_KERBEROS5) || defined(USE_NTLM) \ + || !defined(CURL_DISABLE_DIGEST_AUTH) + const char *service = data->set.str[STRING_SERVICE_NAME] ? + data->set.str[STRING_SERVICE_NAME] : + sasl->params->service; +#endif + const char *oauth_bearer = data->set.str[STRING_BEARER]; + struct bufref serverdata; + + Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port); + Curl_bufref_init(&serverdata); + Curl_bufref_init(&resp); + *progress = SASL_INPROGRESS; + + if(sasl->state == SASL_FINAL) { + if(code != sasl->params->finalcode) + result = CURLE_LOGIN_DENIED; + *progress = SASL_DONE; + sasl_state(sasl, data, SASL_STOP); + return result; + } + + if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP && + code != sasl->params->contcode) { + *progress = SASL_DONE; + sasl_state(sasl, data, SASL_STOP); + return CURLE_LOGIN_DENIED; + } + + switch(sasl->state) { + case SASL_STOP: + *progress = SASL_DONE; + return result; + case SASL_PLAIN: + result = Curl_auth_create_plain_message(conn->sasl_authzid, + conn->user, conn->passwd, &resp); + break; + case SASL_LOGIN: + Curl_auth_create_login_message(conn->user, &resp); + newstate = SASL_LOGIN_PASSWD; + break; + case SASL_LOGIN_PASSWD: + Curl_auth_create_login_message(conn->passwd, &resp); + break; + case SASL_EXTERNAL: + Curl_auth_create_external_message(conn->user, &resp); + break; +#ifdef USE_GSASL + case SASL_GSASL: + result = get_server_message(sasl, data, &serverdata); + if(!result) + result = Curl_auth_gsasl_token(data, &serverdata, &conn->gsasl, &resp); + if(!result && Curl_bufref_len(&resp) > 0) + newstate = SASL_GSASL; + break; +#endif +#ifndef CURL_DISABLE_DIGEST_AUTH + case SASL_CRAMMD5: + result = get_server_message(sasl, data, &serverdata); + if(!result) + result = Curl_auth_create_cram_md5_message(&serverdata, conn->user, + conn->passwd, &resp); + break; + case SASL_DIGESTMD5: + result = get_server_message(sasl, data, &serverdata); + if(!result) + result = Curl_auth_create_digest_md5_message(data, &serverdata, + conn->user, conn->passwd, + service, &resp); + if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) + newstate = SASL_DIGESTMD5_RESP; + break; + case SASL_DIGESTMD5_RESP: + /* Keep response NULL to output an empty line. */ + break; +#endif + +#ifdef USE_NTLM + case SASL_NTLM: + /* Create the type-1 message */ + result = Curl_auth_create_ntlm_type1_message(data, + conn->user, conn->passwd, + service, hostname, + &conn->ntlm, &resp); + newstate = SASL_NTLM_TYPE2MSG; + break; + case SASL_NTLM_TYPE2MSG: + /* Decode the type-2 message */ + result = get_server_message(sasl, data, &serverdata); + if(!result) + result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, + &conn->ntlm); + if(!result) + result = Curl_auth_create_ntlm_type3_message(data, conn->user, + conn->passwd, &conn->ntlm, + &resp); + break; +#endif + +#if defined(USE_KERBEROS5) + case SASL_GSSAPI: + result = Curl_auth_create_gssapi_user_message(data, conn->user, + conn->passwd, + service, + conn->host.name, + sasl->mutual_auth, NULL, + &conn->krb5, + &resp); + newstate = SASL_GSSAPI_TOKEN; + break; + case SASL_GSSAPI_TOKEN: + result = get_server_message(sasl, data, &serverdata); + if(!result) { + if(sasl->mutual_auth) { + /* Decode the user token challenge and create the optional response + message */ + result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, + NULL, NULL, + sasl->mutual_auth, + &serverdata, + &conn->krb5, + &resp); + newstate = SASL_GSSAPI_NO_DATA; + } + else + /* Decode the security challenge and create the response message */ + result = Curl_auth_create_gssapi_security_message(data, + conn->sasl_authzid, + &serverdata, + &conn->krb5, + &resp); + } + break; + case SASL_GSSAPI_NO_DATA: + /* Decode the security challenge and create the response message */ + result = get_server_message(sasl, data, &serverdata); + if(!result) + result = Curl_auth_create_gssapi_security_message(data, + conn->sasl_authzid, + &serverdata, + &conn->krb5, + &resp); + break; +#endif + + case SASL_OAUTH2: + /* Create the authorization message */ + if(sasl->authused == SASL_MECH_OAUTHBEARER) { + result = Curl_auth_create_oauth_bearer_message(conn->user, + hostname, + port, + oauth_bearer, + &resp); + + /* Failures maybe sent by the server as continuations for OAUTHBEARER */ + newstate = SASL_OAUTH2_RESP; + } + else + result = Curl_auth_create_xoauth_bearer_message(conn->user, + oauth_bearer, + &resp); + break; + + case SASL_OAUTH2_RESP: + /* The continuation is optional so check the response code */ + if(code == sasl->params->finalcode) { + /* Final response was received so we are done */ + *progress = SASL_DONE; + sasl_state(sasl, data, SASL_STOP); + return result; + } + else if(code == sasl->params->contcode) { + /* Acknowledge the continuation by sending a 0x01 response. */ + Curl_bufref_set(&resp, "\x01", 1, NULL); + break; + } + else { + *progress = SASL_DONE; + sasl_state(sasl, data, SASL_STOP); + return CURLE_LOGIN_DENIED; + } + + case SASL_CANCEL: + /* Remove the offending mechanism from the supported list */ + sasl->authmechs ^= sasl->authused; + + /* Start an alternative SASL authentication */ + return Curl_sasl_start(sasl, data, sasl->force_ir, progress); + default: + failf(data, "Unsupported SASL authentication mechanism"); + result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */ + break; + } + + Curl_bufref_free(&serverdata); + + switch(result) { + case CURLE_BAD_CONTENT_ENCODING: + /* Cancel dialog */ + result = sasl->params->cancelauth(data, sasl->curmech); + newstate = SASL_CANCEL; + break; + case CURLE_OK: + result = build_message(sasl, &resp); + if(!result) + result = sasl->params->contauth(data, sasl->curmech, &resp); + break; + default: + newstate = SASL_STOP; /* Stop on error */ + *progress = SASL_DONE; + break; + } + + Curl_bufref_free(&resp); + + sasl_state(sasl, data, newstate); + + return result; +} +#endif /* protocols are enabled that use SASL */ diff --git a/third_party/curl/lib/curl_sasl.h b/third_party/curl/lib/curl_sasl.h new file mode 100644 index 0000000000..e94e6431a2 --- /dev/null +++ b/third_party/curl/lib/curl_sasl.h @@ -0,0 +1,165 @@ +#ifndef HEADER_CURL_SASL_H +#define HEADER_CURL_SASL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include + +#include "bufref.h" + +struct Curl_easy; +struct connectdata; + +/* Authentication mechanism flags */ +#define SASL_MECH_LOGIN (1 << 0) +#define SASL_MECH_PLAIN (1 << 1) +#define SASL_MECH_CRAM_MD5 (1 << 2) +#define SASL_MECH_DIGEST_MD5 (1 << 3) +#define SASL_MECH_GSSAPI (1 << 4) +#define SASL_MECH_EXTERNAL (1 << 5) +#define SASL_MECH_NTLM (1 << 6) +#define SASL_MECH_XOAUTH2 (1 << 7) +#define SASL_MECH_OAUTHBEARER (1 << 8) +#define SASL_MECH_SCRAM_SHA_1 (1 << 9) +#define SASL_MECH_SCRAM_SHA_256 (1 << 10) + +/* Authentication mechanism values */ +#define SASL_AUTH_NONE 0 +#define SASL_AUTH_ANY 0xffff +#define SASL_AUTH_DEFAULT (SASL_AUTH_ANY & ~SASL_MECH_EXTERNAL) + +/* Authentication mechanism strings */ +#define SASL_MECH_STRING_LOGIN "LOGIN" +#define SASL_MECH_STRING_PLAIN "PLAIN" +#define SASL_MECH_STRING_CRAM_MD5 "CRAM-MD5" +#define SASL_MECH_STRING_DIGEST_MD5 "DIGEST-MD5" +#define SASL_MECH_STRING_GSSAPI "GSSAPI" +#define SASL_MECH_STRING_EXTERNAL "EXTERNAL" +#define SASL_MECH_STRING_NTLM "NTLM" +#define SASL_MECH_STRING_XOAUTH2 "XOAUTH2" +#define SASL_MECH_STRING_OAUTHBEARER "OAUTHBEARER" +#define SASL_MECH_STRING_SCRAM_SHA_1 "SCRAM-SHA-1" +#define SASL_MECH_STRING_SCRAM_SHA_256 "SCRAM-SHA-256" + +/* SASL flags */ +#define SASL_FLAG_BASE64 0x0001 /* Messages are base64-encoded */ + +/* SASL machine states */ +typedef enum { + SASL_STOP, + SASL_PLAIN, + SASL_LOGIN, + SASL_LOGIN_PASSWD, + SASL_EXTERNAL, + SASL_CRAMMD5, + SASL_DIGESTMD5, + SASL_DIGESTMD5_RESP, + SASL_NTLM, + SASL_NTLM_TYPE2MSG, + SASL_GSSAPI, + SASL_GSSAPI_TOKEN, + SASL_GSSAPI_NO_DATA, + SASL_OAUTH2, + SASL_OAUTH2_RESP, + SASL_GSASL, + SASL_CANCEL, + SASL_FINAL +} saslstate; + +/* Progress indicator */ +typedef enum { + SASL_IDLE, + SASL_INPROGRESS, + SASL_DONE +} saslprogress; + +/* Protocol dependent SASL parameters */ +struct SASLproto { + const char *service; /* The service name */ + CURLcode (*sendauth)(struct Curl_easy *data, const char *mech, + const struct bufref *ir); + /* Send authentication command */ + CURLcode (*contauth)(struct Curl_easy *data, const char *mech, + const struct bufref *contauth); + /* Send authentication continuation */ + CURLcode (*cancelauth)(struct Curl_easy *data, const char *mech); + /* Cancel authentication. */ + CURLcode (*getmessage)(struct Curl_easy *data, struct bufref *out); + /* Get SASL response message */ + size_t maxirlen; /* Maximum initial response + mechanism length, + or zero if no max. This is normally the max + command length - other characters count. + This has to be zero for non-base64 protocols. */ + int contcode; /* Code to receive when continuation is expected */ + int finalcode; /* Code to receive upon authentication success */ + unsigned short defmechs; /* Mechanisms enabled by default */ + unsigned short flags; /* Configuration flags. */ +}; + +/* Per-connection parameters */ +struct SASL { + const struct SASLproto *params; /* Protocol dependent parameters */ + saslstate state; /* Current machine state */ + const char *curmech; /* Current mechanism id. */ + unsigned short authmechs; /* Accepted authentication mechanisms */ + unsigned short prefmech; /* Preferred authentication mechanism */ + unsigned short authused; /* Auth mechanism used for the connection */ + BIT(resetprefs); /* For URL auth option parsing. */ + BIT(mutual_auth); /* Mutual authentication enabled (GSSAPI only) */ + BIT(force_ir); /* Protocol always supports initial response */ +}; + +/* This is used to test whether the line starts with the given mechanism */ +#define sasl_mech_equal(line, wordlen, mech) \ + (wordlen == (sizeof(mech) - 1) / sizeof(char) && \ + !memcmp(line, mech, wordlen)) + +/* This is used to cleanup any libraries or curl modules used by the sasl + functions */ +void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused); + +/* Convert a mechanism name to a token */ +unsigned short Curl_sasl_decode_mech(const char *ptr, + size_t maxlen, size_t *len); + +/* Parse the URL login options */ +CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, + const char *value, size_t len); + +/* Initializes an SASL structure */ +void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data, + const struct SASLproto *params); + +/* Check if we have enough auth data and capabilities to authenticate */ +bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data); + +/* Calculate the required login details for SASL authentication */ +CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, + bool force_ir, saslprogress *progress); + +/* Continue an SASL authentication */ +CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, + int code, saslprogress *progress); + +#endif /* HEADER_CURL_SASL_H */ diff --git a/third_party/curl/lib/curl_setup.h b/third_party/curl/lib/curl_setup.h new file mode 100644 index 0000000000..c8dba5e2b4 --- /dev/null +++ b/third_party/curl/lib/curl_setup.h @@ -0,0 +1,972 @@ +#ifndef HEADER_CURL_SETUP_H +#define HEADER_CURL_SETUP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#if defined(BUILDING_LIBCURL) && !defined(CURL_NO_OLDIES) +#define CURL_NO_OLDIES +#endif + +/* FIXME: Delete this once the warnings have been fixed. */ +#if !defined(CURL_WARN_SIGN_CONVERSION) +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +#endif + +/* Set default _WIN32_WINNT */ +#ifdef __MINGW32__ +#include <_mingw.h> +#endif + +/* Workaround for Homebrew gcc 12.4.0, 13.3.0, 14.1.0 and newer (as of 14.1.0) + that started advertising the `availability` attribute, which then gets used + by Apple SDK, but, in a way incompatible with gcc, resulting in a misc + errors inside SDK headers, e.g.: + error: attributes should be specified before the declarator in a function + definition + error: expected ',' or '}' before + Followed by missing declarations. + Fix it by overriding the built-in feature-check macro used by the headers + to enable the problematic attributes. This makes the feature check fail. */ +#if defined(__APPLE__) && \ + !defined(__clang__) && \ + defined(__GNUC__) && __GNUC__ >= 12 && \ + defined(__has_attribute) +#define availability curl_pp_attribute_disabled +#endif + +#if defined(__APPLE__) +#include +#include +/* Fixup faulty target macro initialization in macOS SDK since v14.4 (as of + 15.0 beta). The SDK target detection in `TargetConditionals.h` correctly + detects macOS, but fails to set the macro's old name `TARGET_OS_OSX`, then + continues to set it to a default value of 0. Other parts of the SDK still + rely on the old name, and with this inconsistency our builds fail due to + missing declarations. It happens when using mainline llvm older than v18. + Later versions fixed it by predefining these target macros, avoiding the + faulty dynamic detection. gcc is not affected (for now) because it lacks + the necessary dynamic detection features, so the SDK falls back to + a codepath that sets both the old and new macro to 1. */ +#if defined(TARGET_OS_MAC) && TARGET_OS_MAC && \ + defined(TARGET_OS_OSX) && !TARGET_OS_OSX && \ + (!defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE) && \ + (!defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR) +#undef TARGET_OS_OSX +#define TARGET_OS_OSX TARGET_OS_MAC +#endif +#endif + +/* + * Disable Visual Studio warnings: + * 4127 "conditional expression is constant" + */ +#ifdef _MSC_VER +#pragma warning(disable:4127) +#endif + +#ifdef _WIN32 +/* + * Do not include unneeded stuff in Windows headers to avoid compiler + * warnings and macro clashes. + * Make sure to define this macro before including any Windows headers. + */ +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# ifndef NOGDI +# define NOGDI +# endif +/* Detect Windows App environment which has a restricted access + * to the Win32 APIs. */ +# if (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)) || \ + defined(WINAPI_FAMILY) +# include +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ + !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define CURL_WINDOWS_APP +# endif +# endif +#endif + +/* Compatibility */ +#if defined(ENABLE_IPV6) +# define USE_IPV6 1 +#endif + +/* + * Include configuration script results or hand-crafted + * configuration file for platforms which lack config tool. + */ + +#ifdef HAVE_CONFIG_H + +#include "curl_config.h" + +#else /* HAVE_CONFIG_H */ + +#ifdef _WIN32_WCE +# include "config-win32ce.h" +#else +# ifdef _WIN32 +# include "config-win32.h" +# endif +#endif + +#ifdef macintosh +# include "config-mac.h" +#endif + +#ifdef __riscos__ +# include "config-riscos.h" +#endif + +#ifdef __AMIGA__ +# include "config-amigaos.h" +#endif + +#ifdef __OS400__ +# include "config-os400.h" +#endif + +#ifdef __PLAN9__ +# include "config-plan9.h" +#endif + +#ifdef MSDOS +# include "config-dos.h" +#endif + +#endif /* HAVE_CONFIG_H */ + +/* ================================================================ */ +/* Definition of preprocessor macros/symbols which modify compiler */ +/* behavior or generated code characteristics must be done here, */ +/* as appropriate, before any system header file is included. It is */ +/* also possible to have them defined in the config file included */ +/* before this point. As a result of all this we frown inclusion of */ +/* system header files in our config files, avoid this at any cost. */ +/* ================================================================ */ + +/* + * AIX 4.3 and newer needs _THREAD_SAFE defined to build + * proper reentrant code. Others may also need it. + */ + +#ifdef NEED_THREAD_SAFE +# ifndef _THREAD_SAFE +# define _THREAD_SAFE +# endif +#endif + +/* + * Tru64 needs _REENTRANT set for a few function prototypes and + * things to appear in the system header files. Unixware needs it + * to build proper reentrant code. Others may also need it. + */ + +#ifdef NEED_REENTRANT +# ifndef _REENTRANT +# define _REENTRANT +# endif +#endif + +/* Solaris needs this to get a POSIX-conformant getpwuid_r */ +#if defined(sun) || defined(__sun) +# ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +# endif +#endif + +/* ================================================================ */ +/* If you need to include a system header file for your platform, */ +/* please, do it beyond the point further indicated in this file. */ +/* ================================================================ */ + +/* + * Disable other protocols when http is the only one desired. + */ + +#ifdef HTTP_ONLY +# ifndef CURL_DISABLE_DICT +# define CURL_DISABLE_DICT +# endif +# ifndef CURL_DISABLE_FILE +# define CURL_DISABLE_FILE +# endif +# ifndef CURL_DISABLE_FTP +# define CURL_DISABLE_FTP +# endif +# ifndef CURL_DISABLE_GOPHER +# define CURL_DISABLE_GOPHER +# endif +# ifndef CURL_DISABLE_IMAP +# define CURL_DISABLE_IMAP +# endif +# ifndef CURL_DISABLE_LDAP +# define CURL_DISABLE_LDAP +# endif +# ifndef CURL_DISABLE_LDAPS +# define CURL_DISABLE_LDAPS +# endif +# ifndef CURL_DISABLE_MQTT +# define CURL_DISABLE_MQTT +# endif +# ifndef CURL_DISABLE_POP3 +# define CURL_DISABLE_POP3 +# endif +# ifndef CURL_DISABLE_RTSP +# define CURL_DISABLE_RTSP +# endif +# ifndef CURL_DISABLE_SMB +# define CURL_DISABLE_SMB +# endif +# ifndef CURL_DISABLE_SMTP +# define CURL_DISABLE_SMTP +# endif +# ifndef CURL_DISABLE_TELNET +# define CURL_DISABLE_TELNET +# endif +# ifndef CURL_DISABLE_TFTP +# define CURL_DISABLE_TFTP +# endif +#endif + +/* + * When http is disabled rtsp is not supported. + */ + +#if defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_RTSP) +# define CURL_DISABLE_RTSP +#endif + +/* + * When HTTP is disabled, disable HTTP-only features + */ + +#if defined(CURL_DISABLE_HTTP) +# define CURL_DISABLE_ALTSVC 1 +# define CURL_DISABLE_COOKIES 1 +# define CURL_DISABLE_BASIC_AUTH 1 +# define CURL_DISABLE_BEARER_AUTH 1 +# define CURL_DISABLE_AWS 1 +# define CURL_DISABLE_DOH 1 +# define CURL_DISABLE_FORM_API 1 +# define CURL_DISABLE_HEADERS_API 1 +# define CURL_DISABLE_HSTS 1 +# define CURL_DISABLE_HTTP_AUTH 1 +#endif + +/* ================================================================ */ +/* No system header file shall be included in this file before this */ +/* point. */ +/* ================================================================ */ + +/* + * OS/400 setup file includes some system headers. + */ + +#ifdef __OS400__ +# include "setup-os400.h" +#endif + +/* + * VMS setup file includes some system headers. + */ + +#ifdef __VMS +# include "setup-vms.h" +#endif + +/* + * Windows setup file includes some system headers. + */ + +#ifdef _WIN32 +# include "setup-win32.h" +#endif + +#include + +/* Helper macro to expand and concatenate two macros. + * Direct macros concatenation does not work because macros + * are not expanded before direct concatenation. + */ +#define CURL_CONC_MACROS_(A,B) A ## B +#define CURL_CONC_MACROS(A,B) CURL_CONC_MACROS_(A,B) + +/* curl uses its own printf() function internally. It understands the GNU + * format. Use this format, so that is matches the GNU format attribute we + * use with the mingw compiler, allowing it to verify them at compile-time. + */ +#ifdef __MINGW32__ +# undef CURL_FORMAT_CURL_OFF_T +# undef CURL_FORMAT_CURL_OFF_TU +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +#endif + +/* based on logic in "curl/mprintf.h" */ + +#if (defined(__GNUC__) || defined(__clang__) || \ + defined(__IAR_SYSTEMS_ICC__)) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(CURL_NO_FMT_CHECKS) +#if defined(__MINGW32__) && !defined(__clang__) +#define CURL_PRINTF(fmt, arg) \ + __attribute__((format(gnu_printf, fmt, arg))) +#else +#define CURL_PRINTF(fmt, arg) \ + __attribute__((format(__printf__, fmt, arg))) +#endif +#else +#define CURL_PRINTF(fmt, arg) +#endif + +/* Workaround for mainline llvm v16 and earlier missing a built-in macro + expected by macOS SDK v14 / Xcode v15 (2023) and newer. + gcc (as of v14) is also missing it. */ +#if defined(__APPLE__) && \ + ((!defined(__apple_build_version__) && \ + defined(__clang__) && __clang_major__ < 17) || \ + (defined(__GNUC__) && __GNUC__ <= 14)) && \ + defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + !defined(__ENVIRONMENT_OS_VERSION_MIN_REQUIRED__) +#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +#endif + +/* + * Use getaddrinfo to resolve the IPv4 address literal. If the current network + * interface does not support IPv4, but supports IPv6, NAT64, and DNS64, + * performing this task will result in a synthesized IPv6 address. + */ +#if defined(__APPLE__) && !defined(USE_ARES) +#define USE_RESOLVE_ON_IPS 1 +# if TARGET_OS_MAC && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && \ + defined(USE_IPV6) +# define CURL_MACOS_CALL_COPYPROXIES 1 +# endif +#endif + +#ifdef USE_LWIPSOCK +# include +# include +# include +#endif + +#ifdef HAVE_EXTRA_STRICMP_H +# include +#endif + +#ifdef HAVE_EXTRA_STRDUP_H +# include +#endif + +#ifdef __AMIGA__ +# ifdef __amigaos4__ +# define __USE_INLINE__ + /* use our own resolver which uses runtime feature detection */ +# define CURLRES_AMIGA + /* getaddrinfo() currently crashes bsdsocket.library, so disable */ +# undef HAVE_GETADDRINFO +# if !(defined(__NEWLIB__) || \ + (defined(__CLIB2__) && defined(__THREAD_SAFE))) + /* disable threaded resolver with clib2 - requires newlib or clib-ts */ +# undef USE_THREADS_POSIX +# endif +# endif +# include +# include +# include +# include +# include +# if defined(HAVE_PROTO_BSDSOCKET_H) && \ + (!defined(__amigaos4__) || defined(USE_AMISSL)) + /* use bsdsocket.library directly, instead of libc networking functions */ +# define _SYS_MBUF_H /* m_len define clashes with curl */ +# include +# ifdef __amigaos4__ + int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *errorfds, struct timeval *timeout); +# define select(a,b,c,d,e) Curl_amiga_select(a,b,c,d,e) +# else +# define select(a,b,c,d,e) WaitSelect(a,b,c,d,e,0) +# endif + /* must not use libc's fcntl() on bsdsocket.library sockfds! */ +# undef HAVE_FCNTL +# undef HAVE_FCNTL_O_NONBLOCK +# else + /* use libc networking and hence close() and fnctl() */ +# undef HAVE_CLOSESOCKET_CAMEL +# undef HAVE_IOCTLSOCKET_CAMEL +# endif +/* + * In clib2 arpa/inet.h warns that some prototypes may clash + * with bsdsocket.library. This avoids the definition of those. + */ +# define __NO_NET_API +#endif + +#include +#include + +#ifdef __TANDEM /* for ns*-tandem-nsk systems */ +# if ! defined __LP64 +# include /* FLOSS is only used for 32-bit builds. */ +# endif +#endif + +#ifndef STDC_HEADERS /* no standard C headers! */ +#include +#endif + +/* + * Large file (>2Gb) support using WIN32 functions. + */ + +#ifdef USE_WIN32_LARGE_FILES +# include +# include +# include +# undef lseek +# define lseek(fdes,offset,whence) _lseeki64(fdes, offset, whence) +# undef fstat +# define fstat(fdes,stp) _fstati64(fdes, stp) +# undef stat +# define stat(fname,stp) curlx_win32_stat(fname, stp) +# define struct_stat struct _stati64 +# define LSEEK_ERROR (__int64)-1 +# define open curlx_win32_open +# define fopen(fname,mode) curlx_win32_fopen(fname, mode) + int curlx_win32_open(const char *filename, int oflag, ...); + int curlx_win32_stat(const char *path, struct_stat *buffer); + FILE *curlx_win32_fopen(const char *filename, const char *mode); +#endif + +/* + * Small file (<2Gb) support using WIN32 functions. + */ + +#ifdef USE_WIN32_SMALL_FILES +# include +# include +# include +# ifndef _WIN32_WCE +# undef lseek +# define lseek(fdes,offset,whence) _lseek(fdes, (long)offset, whence) +# define fstat(fdes,stp) _fstat(fdes, stp) +# define stat(fname,stp) curlx_win32_stat(fname, stp) +# define struct_stat struct _stat +# define open curlx_win32_open +# define fopen(fname,mode) curlx_win32_fopen(fname, mode) + int curlx_win32_stat(const char *path, struct_stat *buffer); + int curlx_win32_open(const char *filename, int oflag, ...); + FILE *curlx_win32_fopen(const char *filename, const char *mode); +# endif +# define LSEEK_ERROR (long)-1 +#endif + +#ifndef struct_stat +# define struct_stat struct stat +#endif + +#ifndef LSEEK_ERROR +# define LSEEK_ERROR (off_t)-1 +#endif + +#ifndef SIZEOF_TIME_T +/* assume default size of time_t to be 32 bits */ +#define SIZEOF_TIME_T 4 +#endif + +#ifndef SIZEOF_CURL_SOCKET_T +/* configure and cmake check and set the define */ +# ifdef _WIN64 +# define SIZEOF_CURL_SOCKET_T 8 +# else +/* default guess */ +# define SIZEOF_CURL_SOCKET_T 4 +# endif +#endif + +#if SIZEOF_CURL_SOCKET_T < 8 +# define CURL_FORMAT_SOCKET_T "d" +#elif defined(__MINGW32__) +# define CURL_FORMAT_SOCKET_T "zd" +#else +# define CURL_FORMAT_SOCKET_T "qd" +#endif + +/* + * Default sizeof(off_t) in case it has not been defined in config file. + */ + +#ifndef SIZEOF_OFF_T +# if defined(__VMS) && !defined(__VAX) +# if defined(_LARGEFILE) +# define SIZEOF_OFF_T 8 +# endif +# elif defined(__OS400__) && defined(__ILEC400__) +# if defined(_LARGE_FILES) +# define SIZEOF_OFF_T 8 +# endif +# elif defined(__MVS__) && defined(__IBMC__) +# if defined(_LP64) || defined(_LARGE_FILES) +# define SIZEOF_OFF_T 8 +# endif +# elif defined(__370__) && defined(__IBMC__) +# if defined(_LP64) || defined(_LARGE_FILES) +# define SIZEOF_OFF_T 8 +# endif +# endif +# ifndef SIZEOF_OFF_T +# define SIZEOF_OFF_T 4 +# endif +#endif + +#if (SIZEOF_CURL_OFF_T < 8) +#error "too small curl_off_t" +#else + /* assume SIZEOF_CURL_OFF_T == 8 */ +# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) +#endif +#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1)) + +#if (SIZEOF_CURL_OFF_T != 8) +# error "curl_off_t must be exactly 64 bits" +#else + typedef unsigned CURL_TYPEOF_CURL_OFF_T curl_uint64_t; + typedef CURL_TYPEOF_CURL_OFF_T curl_int64_t; +# ifndef CURL_SUFFIX_CURL_OFF_TU +# error "CURL_SUFFIX_CURL_OFF_TU must be defined" +# endif +# define CURL_UINT64_SUFFIX CURL_SUFFIX_CURL_OFF_TU +# define CURL_UINT64_C(val) CURL_CONC_MACROS(val,CURL_UINT64_SUFFIX) +# define CURL_PRId64 CURL_FORMAT_CURL_OFF_T +# define CURL_PRIu64 CURL_FORMAT_CURL_OFF_TU +#endif + +#if (SIZEOF_TIME_T == 4) +# ifdef HAVE_TIME_T_UNSIGNED +# define TIME_T_MAX UINT_MAX +# define TIME_T_MIN 0 +# else +# define TIME_T_MAX INT_MAX +# define TIME_T_MIN INT_MIN +# endif +#else +# ifdef HAVE_TIME_T_UNSIGNED +# define TIME_T_MAX 0xFFFFFFFFFFFFFFFF +# define TIME_T_MIN 0 +# else +# define TIME_T_MAX 0x7FFFFFFFFFFFFFFF +# define TIME_T_MIN (-TIME_T_MAX - 1) +# endif +#endif + +#ifndef SIZE_T_MAX +/* some limits.h headers have this defined, some do not */ +#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4) +#define SIZE_T_MAX 18446744073709551615U +#else +#define SIZE_T_MAX 4294967295U +#endif +#endif + +#ifndef SSIZE_T_MAX +/* some limits.h headers have this defined, some do not */ +#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4) +#define SSIZE_T_MAX 9223372036854775807 +#else +#define SSIZE_T_MAX 2147483647 +#endif +#endif + +/* + * Arg 2 type for gethostname in case it has not been defined in config file. + */ + +#ifndef GETHOSTNAME_TYPE_ARG2 +# ifdef USE_WINSOCK +# define GETHOSTNAME_TYPE_ARG2 int +# else +# define GETHOSTNAME_TYPE_ARG2 size_t +# endif +#endif + +/* Below we define some functions. They should + + 4. set the SIGALRM signal timeout + 5. set dir/file naming defines + */ + +#ifdef _WIN32 + +# define DIR_CHAR "\\" + +#else /* _WIN32 */ + +# ifdef MSDOS /* Watt-32 */ + +# include +# define select(n,r,w,x,t) select_s(n,r,w,x,t) +# define ioctl(x,y,z) ioctlsocket(x,y,(char *)(z)) +# include +# ifdef word +# undef word +# endif +# ifdef byte +# undef byte +# endif + +# endif /* MSDOS */ + +# ifdef __minix + /* Minix 3 versions up to at least 3.1.3 are missing these prototypes */ + extern char *strtok_r(char *s, const char *delim, char **last); + extern struct tm *gmtime_r(const time_t * const timep, struct tm *tmp); +# endif + +# define DIR_CHAR "/" + +#endif /* _WIN32 */ + +/* ---------------------------------------------------------------- */ +/* resolver specialty compile-time defines */ +/* CURLRES_* defines to use in the host*.c sources */ +/* ---------------------------------------------------------------- */ + +/* + * MSVC threads support requires a multi-threaded runtime library. + * _beginthreadex() is not available in single-threaded ones. + */ + +#if defined(_MSC_VER) && !defined(_MT) +# undef USE_THREADS_POSIX +# undef USE_THREADS_WIN32 +#endif + +/* + * Mutually exclusive CURLRES_* definitions. + */ + +#if defined(USE_IPV6) && defined(HAVE_GETADDRINFO) +# define CURLRES_IPV6 +#elif defined(USE_IPV6) && (defined(_WIN32) || defined(__CYGWIN__)) +/* assume on Windows that IPv6 without getaddrinfo is a broken build */ +# error "Unexpected build: IPv6 is enabled but getaddrinfo was not found." +#else +# define CURLRES_IPV4 +#endif + +#ifdef USE_ARES +# define CURLRES_ASYNCH +# define CURLRES_ARES +/* now undef the stock libc functions just to avoid them being used */ +# undef HAVE_GETADDRINFO +# undef HAVE_FREEADDRINFO +#elif defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +# define CURLRES_ASYNCH +# define CURLRES_THREADED +#else +# define CURLRES_SYNCH +#endif + +/* ---------------------------------------------------------------- */ + +#if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && \ + !defined(USE_WIN32_IDN) && !defined(USE_APPLE_IDN) +/* The lib and header are present */ +#define USE_LIBIDN2 +#endif + +#if defined(USE_LIBIDN2) && (defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN)) +#error "libidn2 cannot be enabled with WinIDN or AppleIDN, choose one." +#endif + +#define LIBIDN_REQUIRED_VERSION "0.4.1" + +#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_BEARSSL) || defined(USE_RUSTLS) +#define USE_SSL /* SSL support has been enabled */ +#endif + +/* Single point where USE_SPNEGO definition might be defined */ +#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \ + (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) +#define USE_SPNEGO +#endif + +/* Single point where USE_KERBEROS5 definition might be defined */ +#if !defined(CURL_DISABLE_KERBEROS_AUTH) && \ + (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) +#define USE_KERBEROS5 +#endif + +/* Single point where USE_NTLM definition might be defined */ +#if !defined(CURL_DISABLE_NTLM) +# if defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ + defined(USE_GNUTLS) || defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ + (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)) +# define USE_CURL_NTLM_CORE +# endif +# if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI) +# define USE_NTLM +# endif +#endif + +#ifdef CURL_WANTS_CA_BUNDLE_ENV +#error "No longer supported. Set CURLOPT_CAINFO at runtime instead." +#endif + +#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) || defined(USE_WOLFSSH) +#define USE_SSH +#endif + +/* + * Provide a mechanism to silence picky compilers, such as gcc 4.6+. + * Parameters should of course normally not be unused, but for example when + * we have multiple implementations of the same interface it may happen. + */ + +#if defined(__GNUC__) && ((__GNUC__ >= 3) || \ + ((__GNUC__ == 2) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 7))) +# define UNUSED_PARAM __attribute__((__unused__)) +# define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#elif defined(__IAR_SYSTEMS_ICC__) +# define UNUSED_PARAM __attribute__((__unused__)) +# if (__VER__ >= 9040001) +# define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# else +# define WARN_UNUSED_RESULT +# endif +#else +# define UNUSED_PARAM /* NOTHING */ +# define WARN_UNUSED_RESULT +#endif + +/* noreturn attribute */ + +#if !defined(CURL_NORETURN) +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) || \ + defined(__IAR_SYSTEMS_ICC__) +# define CURL_NORETURN __attribute__((__noreturn__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +# define CURL_NORETURN __declspec(noreturn) +#else +# define CURL_NORETURN +#endif +#endif + +/* fallthrough attribute */ + +#if !defined(FALLTHROUGH) +#if (defined(__GNUC__) && __GNUC__ >= 7) || \ + (defined(__clang__) && __clang_major__ >= 10) +# define FALLTHROUGH() __attribute__((fallthrough)) +#else +# define FALLTHROUGH() do {} while (0) +#endif +#endif + +/* + * Include macros and defines that should only be processed once. + */ + +#ifndef HEADER_CURL_SETUP_ONCE_H +#include "curl_setup_once.h" +#endif + +/* + * Definition of our NOP statement Object-like macro + */ + +#ifndef Curl_nop_stmt +# define Curl_nop_stmt do { } while(0) +#endif + +/* + * Ensure that Winsock and lwIP TCP/IP stacks are not mixed. + */ + +#if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H) +# if defined(SOCKET) || defined(USE_WINSOCK) +# error "WinSock and lwIP TCP/IP stack definitions shall not coexist!" +# endif +#endif + +/* + * shutdown() flags for systems that do not define them + */ + +#ifndef SHUT_RD +#define SHUT_RD 0x00 +#endif + +#ifndef SHUT_WR +#define SHUT_WR 0x01 +#endif + +#ifndef SHUT_RDWR +#define SHUT_RDWR 0x02 +#endif + +/* Define S_ISREG if not defined by system headers, e.g. MSVC */ +#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif + +/* Define S_ISDIR if not defined by system headers, e.g. MSVC */ +#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +/* In Windows the default file mode is text but an application can override it. +Therefore we specify it explicitly. https://github.com/curl/curl/pull/258 +*/ +#if defined(_WIN32) || defined(MSDOS) +#define FOPEN_READTEXT "rt" +#define FOPEN_WRITETEXT "wt" +#define FOPEN_APPENDTEXT "at" +#elif defined(__CYGWIN__) +/* Cygwin has specific behavior we need to address when WIN32 is not defined. +https://cygwin.com/cygwin-ug-net/using-textbinary.html +For write we want our output to have line endings of LF and be compatible with +other Cygwin utilities. For read we want to handle input that may have line +endings either CRLF or LF so 't' is appropriate. +*/ +#define FOPEN_READTEXT "rt" +#define FOPEN_WRITETEXT "w" +#define FOPEN_APPENDTEXT "a" +#else +#define FOPEN_READTEXT "r" +#define FOPEN_WRITETEXT "w" +#define FOPEN_APPENDTEXT "a" +#endif + +/* for systems that do not detect this in configure */ +#ifndef CURL_SA_FAMILY_T +# if defined(HAVE_SA_FAMILY_T) +# define CURL_SA_FAMILY_T sa_family_t +# elif defined(HAVE_ADDRESS_FAMILY) +# define CURL_SA_FAMILY_T ADDRESS_FAMILY +# else +/* use a sensible default */ +# define CURL_SA_FAMILY_T unsigned short +# endif +#endif + +/* Some convenience macros to get the larger/smaller value out of two given. + We prefix with CURL to prevent name collisions. */ +#define CURLMAX(x,y) ((x)>(y)?(x):(y)) +#define CURLMIN(x,y) ((x)<(y)?(x):(y)) + +/* A convenience macro to provide both the string literal and the length of + the string literal in one go, useful for functions that take "string,len" + as their argument */ +#define STRCONST(x) x,sizeof(x)-1 + +/* Some versions of the Android SDK is missing the declaration */ +#if defined(HAVE_GETPWUID_R) && defined(HAVE_DECL_GETPWUID_R_MISSING) +struct passwd; +int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, + size_t buflen, struct passwd **result); +#endif + +#ifdef UNITTESTS +#define UNITTEST +#else +#define UNITTEST static +#endif + +/* Hyper supports HTTP2 also, but Curl's integration with Hyper does not */ +#if defined(USE_NGHTTP2) +#define USE_HTTP2 +#endif + +#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ + (defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)) || \ + defined(USE_QUICHE) || defined(USE_MSH3) + +#ifdef CURL_WITH_MULTI_SSL +#error "Multi-SSL combined with QUIC is not supported" +#endif + +#define USE_HTTP3 +#endif + +/* Certain Windows implementations are not aligned with what curl expects, + so always use the local one on this platform. E.g. the mingw-w64 + implementation can return wrong results for non-ASCII inputs. */ +#if defined(HAVE_BASENAME) && defined(_WIN32) +#undef HAVE_BASENAME +#endif + +#if defined(USE_UNIX_SOCKETS) && defined(_WIN32) +# if !defined(UNIX_PATH_MAX) + /* Replicating logic present in afunix.h + (distributed with newer Windows 10 SDK versions only) */ +# define UNIX_PATH_MAX 108 + /* !checksrc! disable TYPEDEFSTRUCT 1 */ + typedef struct sockaddr_un { + ADDRESS_FAMILY sun_family; + char sun_path[UNIX_PATH_MAX]; + } SOCKADDR_UN, *PSOCKADDR_UN; +# define WIN32_SOCKADDR_UN +# endif +#endif + +/* OpenSSLv3 marks DES, MD5 and ENGINE functions deprecated but we have no + replacements (yet) so tell the compiler to not warn for them. */ +#ifdef USE_OPENSSL +#define OPENSSL_SUPPRESS_DEPRECATED +#endif + +#if defined(inline) + /* 'inline' is defined as macro and assumed to be correct */ + /* No need for 'inline' replacement */ +#elif defined(__cplusplus) + /* The code is compiled with C++ compiler. + C++ always supports 'inline'. */ + /* No need for 'inline' replacement */ +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901 + /* C99 (and later) supports 'inline' keyword */ + /* No need for 'inline' replacement */ +#elif defined(__GNUC__) && __GNUC__ >= 3 + /* GCC supports '__inline__' as an extension */ +# define inline __inline__ +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + /* MSC supports '__inline' from VS 2005 (or even earlier) */ +# define inline __inline +#else + /* Probably 'inline' is not supported by compiler. + Define to the empty string to be on the safe side. */ +# define inline /* empty */ +#endif + +#endif /* HEADER_CURL_SETUP_H */ diff --git a/third_party/curl/lib/curl_setup_once.h b/third_party/curl/lib/curl_setup_once.h new file mode 100644 index 0000000000..1521e69f91 --- /dev/null +++ b/third_party/curl/lib/curl_setup_once.h @@ -0,0 +1,414 @@ +#ifndef HEADER_CURL_SETUP_ONCE_H +#define HEADER_CURL_SETUP_ONCE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + + +/* + * Inclusion of common header files. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef NEED_MALLOC_H +#include +#endif + +#ifdef NEED_MEMORY_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef _WIN32 +#include +#include +#endif + +#if defined(HAVE_STDBOOL_H) && defined(HAVE_BOOL_T) +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef USE_WOLFSSL +#include +#endif + +#ifdef USE_SCHANNEL +/* Must set this before is included directly or indirectly by + another Windows header. */ +# define SCHANNEL_USE_BLACKLISTS 1 +#endif + +#ifdef __hpux +# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) +# ifdef _APP32_64BIT_OFF_T +# define OLD_APP32_64BIT_OFF_T _APP32_64BIT_OFF_T +# undef _APP32_64BIT_OFF_T +# else +# undef OLD_APP32_64BIT_OFF_T +# endif +# endif +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#include "functypes.h" + +#ifdef __hpux +# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) +# ifdef OLD_APP32_64BIT_OFF_T +# define _APP32_64BIT_OFF_T OLD_APP32_64BIT_OFF_T +# undef OLD_APP32_64BIT_OFF_T +# endif +# endif +#endif + +/* + * Definition of timeval struct for platforms that do not have it. + */ + +#ifndef HAVE_STRUCT_TIMEVAL +struct timeval { + long tv_sec; + long tv_usec; +}; +#endif + + +/* + * If we have the MSG_NOSIGNAL define, make sure we use + * it as the fourth argument of function send() + */ + +#ifdef HAVE_MSG_NOSIGNAL +#define SEND_4TH_ARG MSG_NOSIGNAL +#else +#define SEND_4TH_ARG 0 +#endif + + +#if defined(__minix) +/* Minix does not support recv on TCP sockets */ +#define sread(x,y,z) (ssize_t)read((RECV_TYPE_ARG1)(x), \ + (RECV_TYPE_ARG2)(y), \ + (RECV_TYPE_ARG3)(z)) + +#elif defined(HAVE_RECV) +/* + * The definitions for the return type and arguments types + * of functions recv() and send() belong and come from the + * configuration file. Do not define them in any other place. + * + * HAVE_RECV is defined if you have a function named recv() + * which is used to read incoming data from sockets. If your + * function has another name then do not define HAVE_RECV. + * + * If HAVE_RECV is defined then RECV_TYPE_ARG1, RECV_TYPE_ARG2, + * RECV_TYPE_ARG3, RECV_TYPE_ARG4 and RECV_TYPE_RETV must also + * be defined. + * + * HAVE_SEND is defined if you have a function named send() + * which is used to write outgoing data on a connected socket. + * If yours has another name then do not define HAVE_SEND. + * + * If HAVE_SEND is defined then SEND_TYPE_ARG1, SEND_QUAL_ARG2, + * SEND_TYPE_ARG2, SEND_TYPE_ARG3, SEND_TYPE_ARG4 and + * SEND_TYPE_RETV must also be defined. + */ + +#define sread(x,y,z) (ssize_t)recv((RECV_TYPE_ARG1)(x), \ + (RECV_TYPE_ARG2)(y), \ + (RECV_TYPE_ARG3)(z), \ + (RECV_TYPE_ARG4)(0)) +#else /* HAVE_RECV */ +#ifndef sread +#error "Missing definition of macro sread!" +#endif +#endif /* HAVE_RECV */ + + +#if defined(__minix) +/* Minix does not support send on TCP sockets */ +#define swrite(x,y,z) (ssize_t)write((SEND_TYPE_ARG1)(x), \ + (SEND_TYPE_ARG2)(y), \ + (SEND_TYPE_ARG3)(z)) + +#elif defined(HAVE_SEND) +#define swrite(x,y,z) (ssize_t)send((SEND_TYPE_ARG1)(x), \ + (SEND_QUAL_ARG2 SEND_TYPE_ARG2)(y), \ + (SEND_TYPE_ARG3)(z), \ + (SEND_TYPE_ARG4)(SEND_4TH_ARG)) +#else /* HAVE_SEND */ +#ifndef swrite +#error "Missing definition of macro swrite!" +#endif +#endif /* HAVE_SEND */ + + +/* + * Function-like macro definition used to close a socket. + */ + +#if defined(HAVE_CLOSESOCKET) +# define sclose(x) closesocket((x)) +#elif defined(HAVE_CLOSESOCKET_CAMEL) +# define sclose(x) CloseSocket((x)) +#elif defined(HAVE_CLOSE_S) +# define sclose(x) close_s((x)) +#elif defined(USE_LWIPSOCK) +# define sclose(x) lwip_close((x)) +#else +# define sclose(x) close((x)) +#endif + +/* + * Stack-independent version of fcntl() on sockets: + */ +#if defined(USE_LWIPSOCK) +# define sfcntl lwip_fcntl +#else +# define sfcntl fcntl +#endif + +/* + * 'bool' stuff compatible with HP-UX headers. + */ + +#if defined(__hpux) && !defined(HAVE_BOOL_T) + typedef int bool; +# define false 0 +# define true 1 +# define HAVE_BOOL_T +#endif + + +/* + * 'bool' exists on platforms with , i.e. C99 platforms. + * On non-C99 platforms there is no bool, so define an enum for that. + * On C99 platforms 'false' and 'true' also exist. Enum uses a + * global namespace though, so use bool_false and bool_true. + */ + +#ifndef HAVE_BOOL_T + typedef enum { + bool_false = 0, + bool_true = 1 + } bool; + +/* + * Use a define to let 'true' and 'false' use those enums. There + * are currently no use of true and false in libcurl proper, but + * there are some in the examples. This will cater for any later + * code happening to use true and false. + */ +# define false bool_false +# define true bool_true +# define HAVE_BOOL_T +#endif + +/* the type we use for storing a single boolean bit */ +#ifdef _MSC_VER +typedef bool bit; +#define BIT(x) bool x +#else +typedef unsigned int bit; +#define BIT(x) bit x:1 +#endif + +/* + * Redefine TRUE and FALSE too, to catch current use. With this + * change, 'bool found = 1' will give a warning on MIPSPro, but + * 'bool found = TRUE' will not. Change tested on IRIX/MIPSPro, + * AIX 5.1/Xlc, Tru64 5.1/cc, w/make test too. + */ + +#ifndef TRUE +#define TRUE true +#endif +#ifndef FALSE +#define FALSE false +#endif + +#include "curl_ctype.h" + + +/* + * Macro used to include code only in debug builds. + */ + +#ifdef DEBUGBUILD +#define DEBUGF(x) x +#else +#define DEBUGF(x) do { } while(0) +#endif + + +/* + * Macro used to include assertion code only in debug builds. + */ + +#undef DEBUGASSERT +#if defined(DEBUGBUILD) +#define DEBUGASSERT(x) assert(x) +#else +#define DEBUGASSERT(x) do { } while(0) +#endif + + +/* + * Macro SOCKERRNO / SET_SOCKERRNO() returns / sets the *socket-related* errno + * (or equivalent) on this platform to hide platform details to code using it. + */ + +#ifdef USE_WINSOCK +#define SOCKERRNO ((int)WSAGetLastError()) +#define SET_SOCKERRNO(x) (WSASetLastError((int)(x))) +#else +#define SOCKERRNO (errno) +#define SET_SOCKERRNO(x) (errno = (x)) +#endif + + +/* + * Portable error number symbolic names defined to Winsock error codes. + */ + +#ifdef USE_WINSOCK +#undef EBADF /* override definition in errno.h */ +#define EBADF WSAEBADF +#undef EINTR /* override definition in errno.h */ +#define EINTR WSAEINTR +#undef EINVAL /* override definition in errno.h */ +#define EINVAL WSAEINVAL +#undef EWOULDBLOCK /* override definition in errno.h */ +#define EWOULDBLOCK WSAEWOULDBLOCK +#undef EINPROGRESS /* override definition in errno.h */ +#define EINPROGRESS WSAEINPROGRESS +#undef EALREADY /* override definition in errno.h */ +#define EALREADY WSAEALREADY +#undef ENOTSOCK /* override definition in errno.h */ +#define ENOTSOCK WSAENOTSOCK +#undef EDESTADDRREQ /* override definition in errno.h */ +#define EDESTADDRREQ WSAEDESTADDRREQ +#undef EMSGSIZE /* override definition in errno.h */ +#define EMSGSIZE WSAEMSGSIZE +#undef EPROTOTYPE /* override definition in errno.h */ +#define EPROTOTYPE WSAEPROTOTYPE +#undef ENOPROTOOPT /* override definition in errno.h */ +#define ENOPROTOOPT WSAENOPROTOOPT +#undef EPROTONOSUPPORT /* override definition in errno.h */ +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#undef EOPNOTSUPP /* override definition in errno.h */ +#define EOPNOTSUPP WSAEOPNOTSUPP +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#undef EAFNOSUPPORT /* override definition in errno.h */ +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#undef EADDRINUSE /* override definition in errno.h */ +#define EADDRINUSE WSAEADDRINUSE +#undef EADDRNOTAVAIL /* override definition in errno.h */ +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#undef ENETDOWN /* override definition in errno.h */ +#define ENETDOWN WSAENETDOWN +#undef ENETUNREACH /* override definition in errno.h */ +#define ENETUNREACH WSAENETUNREACH +#undef ENETRESET /* override definition in errno.h */ +#define ENETRESET WSAENETRESET +#undef ECONNABORTED /* override definition in errno.h */ +#define ECONNABORTED WSAECONNABORTED +#undef ECONNRESET /* override definition in errno.h */ +#define ECONNRESET WSAECONNRESET +#undef ENOBUFS /* override definition in errno.h */ +#define ENOBUFS WSAENOBUFS +#undef EISCONN /* override definition in errno.h */ +#define EISCONN WSAEISCONN +#undef ENOTCONN /* override definition in errno.h */ +#define ENOTCONN WSAENOTCONN +#define ESHUTDOWN WSAESHUTDOWN +#define ETOOMANYREFS WSAETOOMANYREFS +#undef ETIMEDOUT /* override definition in errno.h */ +#define ETIMEDOUT WSAETIMEDOUT +#undef ECONNREFUSED /* override definition in errno.h */ +#define ECONNREFUSED WSAECONNREFUSED +#undef ELOOP /* override definition in errno.h */ +#define ELOOP WSAELOOP +#ifndef ENAMETOOLONG /* possible previous definition in errno.h */ +#define ENAMETOOLONG WSAENAMETOOLONG +#endif +#define EHOSTDOWN WSAEHOSTDOWN +#undef EHOSTUNREACH /* override definition in errno.h */ +#define EHOSTUNREACH WSAEHOSTUNREACH +#ifndef ENOTEMPTY /* possible previous definition in errno.h */ +#define ENOTEMPTY WSAENOTEMPTY +#endif +#define EPROCLIM WSAEPROCLIM +#define EUSERS WSAEUSERS +#define EDQUOT WSAEDQUOT +#define ESTALE WSAESTALE +#define EREMOTE WSAEREMOTE +#endif + +/* + * Macro argv_item_t hides platform details to code using it. + */ + +#ifdef __VMS +#define argv_item_t __char_ptr32 +#elif defined(_UNICODE) +#define argv_item_t wchar_t * +#else +#define argv_item_t char * +#endif + + +/* + * We use this ZERO_NULL to avoid picky compiler warnings, + * when assigning a NULL pointer to a function pointer var. + */ + +#define ZERO_NULL 0 + + +#endif /* HEADER_CURL_SETUP_ONCE_H */ diff --git a/third_party/curl/lib/curl_sha256.h b/third_party/curl/lib/curl_sha256.h new file mode 100644 index 0000000000..d99f958f90 --- /dev/null +++ b/third_party/curl/lib/curl_sha256.h @@ -0,0 +1,50 @@ +#ifndef HEADER_CURL_SHA256_H +#define HEADER_CURL_SHA256_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Florin Petriuc, + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ + || defined(USE_LIBSSH2) + +#include +#include "curl_hmac.h" + +extern const struct HMAC_params Curl_HMAC_SHA256[1]; + +#ifdef USE_WOLFSSL +/* SHA256_DIGEST_LENGTH is an enum value in wolfSSL. Need to import it from + * sha.h */ +#include +#include +#else +#define SHA256_DIGEST_LENGTH 32 +#endif + +CURLcode Curl_sha256it(unsigned char *outbuffer, const unsigned char *input, + const size_t len); + +#endif + +#endif /* HEADER_CURL_SHA256_H */ diff --git a/third_party/curl/lib/curl_sha512_256.c b/third_party/curl/lib/curl_sha512_256.c new file mode 100644 index 0000000000..e3533d8fc0 --- /dev/null +++ b/third_party/curl/lib/curl_sha512_256.c @@ -0,0 +1,850 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Evgeny Grin (Karlson2k), . + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256) + +#include "curl_sha512_256.h" +#include "warnless.h" + +/* The recommended order of the TLS backends: + * * OpenSSL + * * GnuTLS + * * wolfSSL + * * Schannel SSPI + * * SecureTransport (Darwin) + * * mbedTLS + * * BearSSL + * * rustls + * Skip the backend if it does not support the required algorithm */ + +#if defined(USE_OPENSSL) +# include +# if (!defined(LIBRESSL_VERSION_NUMBER) && \ + defined(OPENSSL_VERSION_NUMBER) && \ + (OPENSSL_VERSION_NUMBER >= 0x10101000L)) || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + (LIBRESSL_VERSION_NUMBER >= 0x3080000fL)) +# include +# if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA512) +# include +# define USE_OPENSSL_SHA512_256 1 +# define HAS_SHA512_256_IMPLEMENTATION 1 +# ifdef __NetBSD__ +/* Some NetBSD versions has a bug in SHA-512/256. + * See https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 + * The problematic versions: + * - NetBSD before 9.4 + * - NetBSD 9 all development versions (9.99.x) + * - NetBSD 10 development versions (10.99.x) before 10.99.11 + * The bug was fixed in NetBSD 9.4 release, NetBSD 10.0 release, + * NetBSD 10.99.11 development. + * It is safe to apply the workaround even if the bug is not present, as + * the workaround just reduces performance slightly. */ +# include +# if __NetBSD_Version__ < 904000000 || \ + (__NetBSD_Version__ >= 999000000 && \ + __NetBSD_Version__ < 1000000000) || \ + (__NetBSD_Version__ >= 1099000000 && \ + __NetBSD_Version__ < 1099001100) +# define NEED_NETBSD_SHA512_256_WORKAROUND 1 +# include +# endif +# endif +# endif +# endif +#endif /* USE_OPENSSL */ + + +#if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS) +# include +# if defined(SHA512_256_DIGEST_SIZE) +# define USE_GNUTLS_SHA512_256 1 +# define HAS_SHA512_256_IMPLEMENTATION 1 +# endif +#endif /* ! HAS_SHA512_256_IMPLEMENTATION && USE_GNUTLS */ + +#if defined(USE_OPENSSL_SHA512_256) + +/* OpenSSL does not provide macros for SHA-512/256 sizes */ + +/** + * Size of the SHA-512/256 single processing block in bytes. + */ +#define SHA512_256_BLOCK_SIZE 128 + +/** + * Size of the SHA-512/256 resulting digest in bytes. + * This is the final digest size, not intermediate hash. + */ +#define SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_LENGTH + +/** + * Context type used for SHA-512/256 calculations + */ +typedef EVP_MD_CTX *Curl_sha512_256_ctx; + +/** + * Initialise structure for SHA-512/256 calculation. + * + * @param context the calculation context + * @return CURLE_OK if succeed, + * error code otherwise + */ +static CURLcode +Curl_sha512_256_init(void *context) +{ + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + + *ctx = EVP_MD_CTX_create(); + if(!*ctx) + return CURLE_OUT_OF_MEMORY; + + if(EVP_DigestInit_ex(*ctx, EVP_sha512_256(), NULL)) { + /* Check whether the header and this file use the same numbers */ + DEBUGASSERT(EVP_MD_CTX_size(*ctx) == SHA512_256_DIGEST_SIZE); + /* Check whether the block size is correct */ + DEBUGASSERT(EVP_MD_CTX_block_size(*ctx) == SHA512_256_BLOCK_SIZE); + + return CURLE_OK; /* Success */ + } + + /* Cleanup */ + EVP_MD_CTX_destroy(*ctx); + return CURLE_FAILED_INIT; +} + + +/** + * Process portion of bytes. + * + * @param context the calculation context + * @param data bytes to add to hash + * @return CURLE_OK if succeed, + * error code otherwise + */ +static CURLcode +Curl_sha512_256_update(void *context, + const unsigned char *data, + size_t length) +{ + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + + if(!EVP_DigestUpdate(*ctx, data, length)) + return CURLE_SSL_CIPHER; + + return CURLE_OK; +} + + +/** + * Finalise SHA-512/256 calculation, return digest. + * + * @param context the calculation context + * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes + * @return CURLE_OK if succeed, + * error code otherwise + */ +static CURLcode +Curl_sha512_256_finish(unsigned char *digest, + void *context) +{ + CURLcode ret; + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + +#ifdef NEED_NETBSD_SHA512_256_WORKAROUND + /* Use a larger buffer to work around a bug in NetBSD: + https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */ + unsigned char tmp_digest[SHA512_256_DIGEST_SIZE * 2]; + ret = EVP_DigestFinal_ex(*ctx, + tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; + if(ret == CURLE_OK) + memcpy(digest, tmp_digest, SHA512_256_DIGEST_SIZE); + explicit_memset(tmp_digest, 0, sizeof(tmp_digest)); +#else /* ! NEED_NETBSD_SHA512_256_WORKAROUND */ + ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; +#endif /* ! NEED_NETBSD_SHA512_256_WORKAROUND */ + + EVP_MD_CTX_destroy(*ctx); + *ctx = NULL; + + return ret; +} + +#elif defined(USE_GNUTLS_SHA512_256) + +/** + * Context type used for SHA-512/256 calculations + */ +typedef struct sha512_256_ctx Curl_sha512_256_ctx; + +/** + * Initialise structure for SHA-512/256 calculation. + * + * @param context the calculation context + * @return always CURLE_OK + */ +static CURLcode +Curl_sha512_256_init(void *context) +{ + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + + /* Check whether the header and this file use the same numbers */ + DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE); + + sha512_256_init(ctx); + + return CURLE_OK; +} + + +/** + * Process portion of bytes. + * + * @param context the calculation context + * @param data bytes to add to hash + * @param length number of bytes in @a data + * @return always CURLE_OK + */ +static CURLcode +Curl_sha512_256_update(void *context, + const unsigned char *data, + size_t length) +{ + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + + DEBUGASSERT((data != NULL) || (length == 0)); + + sha512_256_update(ctx, length, (const uint8_t *)data); + + return CURLE_OK; +} + + +/** + * Finalise SHA-512/256 calculation, return digest. + * + * @param context the calculation context + * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes + * @return always CURLE_OK + */ +static CURLcode +Curl_sha512_256_finish(unsigned char *digest, + void *context) +{ + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + + sha512_256_digest(ctx, (size_t)SHA512_256_DIGEST_SIZE, (uint8_t *)digest); + + return CURLE_OK; +} + +#else /* No system or TLS backend SHA-512/256 implementation available */ + +/* Use local implementation */ +#define HAS_SHA512_256_IMPLEMENTATION 1 + +/* ** This implementation of SHA-512/256 hash calculation was originally ** * + * ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd. ** * + * ** The author ported the code to libcurl. The ported code is provided ** * + * ** under curl license. ** * + * ** This is a minimal version with minimal optimizations. Performance ** * + * ** can be significantly improved. Big-endian store and load macros ** * + * ** are obvious targets for optimization. ** */ + +#ifdef __GNUC__ +# if defined(__has_attribute) && defined(__STDC_VERSION__) +# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901 +# define MHDX_INLINE inline __attribute__((always_inline)) +# endif +# endif +#endif + +#if !defined(MHDX_INLINE) && \ + defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__) +# if _MSC_VER >= 1400 +# define MHDX_INLINE __forceinline +# endif +#endif + +#if !defined(MHDX_INLINE) + /* Assume that 'inline' keyword works or the + * macro was already defined correctly. */ +# define MHDX_INLINE inline +#endif + +/* Bits manipulation macros and functions. + Can be moved to other headers to reuse. */ + +#define MHDX_GET_64BIT_BE(ptr) \ + ( ((curl_uint64_t)(((const unsigned char*)(ptr))[0]) << 56) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[1]) << 48) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[2]) << 40) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[3]) << 32) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[4]) << 24) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[5]) << 16) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[6]) << 8) | \ + (curl_uint64_t)(((const unsigned char*)(ptr))[7]) ) + +#define MHDX_PUT_64BIT_BE(ptr,val) do { \ + ((unsigned char*)(ptr))[7]=(unsigned char)((curl_uint64_t)(val)); \ + ((unsigned char*)(ptr))[6]=(unsigned char)(((curl_uint64_t)(val)) >> 8); \ + ((unsigned char*)(ptr))[5]=(unsigned char)(((curl_uint64_t)(val)) >> 16); \ + ((unsigned char*)(ptr))[4]=(unsigned char)(((curl_uint64_t)(val)) >> 24); \ + ((unsigned char*)(ptr))[3]=(unsigned char)(((curl_uint64_t)(val)) >> 32); \ + ((unsigned char*)(ptr))[2]=(unsigned char)(((curl_uint64_t)(val)) >> 40); \ + ((unsigned char*)(ptr))[1]=(unsigned char)(((curl_uint64_t)(val)) >> 48); \ + ((unsigned char*)(ptr))[0]=(unsigned char)(((curl_uint64_t)(val)) >> 56); \ + } while(0) + +/* Defined as a function. The macro version may duplicate the binary code + * size as each argument is used twice, so if any calculation is used + * as an argument, the calculation could be done twice. */ +static MHDX_INLINE curl_uint64_t +MHDx_rotr64(curl_uint64_t value, unsigned int bits) +{ + bits %= 64; + if(0 == bits) + return value; + /* Defined in a form which modern compiler could optimize. */ + return (value >> bits) | (value << (64 - bits)); +} + +/* SHA-512/256 specific data */ + +/** + * Number of bits in a single SHA-512/256 word. + */ +#define SHA512_256_WORD_SIZE_BITS 64 + +/** + * Number of bytes in a single SHA-512/256 word. + */ +#define SHA512_256_BYTES_IN_WORD (SHA512_256_WORD_SIZE_BITS / 8) + +/** + * Hash is kept internally as 8 64-bit words. + * This is the intermediate hash size, used during computing the final digest. + */ +#define SHA512_256_HASH_SIZE_WORDS 8 + +/** + * Size of the SHA-512/256 resulting digest in words. + * This is the final digest size, not intermediate hash. + */ +#define SHA512_256_DIGEST_SIZE_WORDS (SHA512_256_HASH_SIZE_WORDS / 2) + +/** + * Size of the SHA-512/256 resulting digest in bytes + * This is the final digest size, not intermediate hash. + */ +#define SHA512_256_DIGEST_SIZE \ + (SHA512_256_DIGEST_SIZE_WORDS * SHA512_256_BYTES_IN_WORD) + +/** + * Size of the SHA-512/256 single processing block in bits. + */ +#define SHA512_256_BLOCK_SIZE_BITS 1024 + +/** + * Size of the SHA-512/256 single processing block in bytes. + */ +#define SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8) + +/** + * Size of the SHA-512/256 single processing block in words. + */ +#define SHA512_256_BLOCK_SIZE_WORDS \ + (SHA512_256_BLOCK_SIZE_BITS / SHA512_256_WORD_SIZE_BITS) + +/** + * SHA-512/256 calculation context + */ +struct mhdx_sha512_256ctx +{ + /** + * Intermediate hash value. The variable is properly aligned. Smart + * compilers may automatically use fast load/store instruction for big + * endian data on little endian machine. + */ + curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS]; + /** + * SHA-512/256 input data buffer. The buffer is properly aligned. Smart + * compilers may automatically use fast load/store instruction for big + * endian data on little endian machine. + */ + curl_uint64_t buffer[SHA512_256_BLOCK_SIZE_WORDS]; + /** + * The number of bytes, lower part + */ + curl_uint64_t count; + /** + * The number of bits, high part. Unlike lower part, this counts the number + * of bits, not bytes. + */ + curl_uint64_t count_bits_hi; +}; + +/** + * Context type used for SHA-512/256 calculations + */ +typedef struct mhdx_sha512_256ctx Curl_sha512_256_ctx; + + +/** + * Initialise structure for SHA-512/256 calculation. + * + * @param context the calculation context + * @return always CURLE_OK + */ +static CURLcode +MHDx_sha512_256_init(void *context) +{ + struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *) context; + + /* Check whether the header and this file use the same numbers */ + DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE); + + DEBUGASSERT(sizeof(curl_uint64_t) == 8); + + /* Initial hash values, see FIPS PUB 180-4 section 5.3.6.2 */ + /* Values generated by "IV Generation Function" as described in + * section 5.3.6 */ + ctx->H[0] = CURL_UINT64_C(0x22312194FC2BF72C); + ctx->H[1] = CURL_UINT64_C(0x9F555FA3C84C64C2); + ctx->H[2] = CURL_UINT64_C(0x2393B86B6F53B151); + ctx->H[3] = CURL_UINT64_C(0x963877195940EABD); + ctx->H[4] = CURL_UINT64_C(0x96283EE2A88EFFE3); + ctx->H[5] = CURL_UINT64_C(0xBE5E1E2553863992); + ctx->H[6] = CURL_UINT64_C(0x2B0199FC2C85B8AA); + ctx->H[7] = CURL_UINT64_C(0x0EB72DDC81C52CA2); + + /* Initialise number of bytes and high part of number of bits. */ + ctx->count = CURL_UINT64_C(0); + ctx->count_bits_hi = CURL_UINT64_C(0); + + return CURLE_OK; +} + + +/** + * Base of the SHA-512/256 transformation. + * Gets a full 128 bytes block of data and updates hash values; + * @param H hash values + * @param data the data buffer with #SHA512_256_BLOCK_SIZE bytes block + */ +static void +MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], + const void *data) +{ + /* Working variables, + see FIPS PUB 180-4 section 6.7, 6.4. */ + curl_uint64_t a = H[0]; + curl_uint64_t b = H[1]; + curl_uint64_t c = H[2]; + curl_uint64_t d = H[3]; + curl_uint64_t e = H[4]; + curl_uint64_t f = H[5]; + curl_uint64_t g = H[6]; + curl_uint64_t h = H[7]; + + /* Data buffer, used as a cyclic buffer. + See FIPS PUB 180-4 section 5.2.2, 6.7, 6.4. */ + curl_uint64_t W[16]; + + /* 'Ch' and 'Maj' macro functions are defined with widely-used optimization. + See FIPS PUB 180-4 formulae 4.8, 4.9. */ +#define Sha512_Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) ) +#define Sha512_Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) ) + + /* Four 'Sigma' macro functions. + See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */ +#define SIG0(x) \ + ( MHDx_rotr64((x), 28) ^ MHDx_rotr64((x), 34) ^ MHDx_rotr64((x), 39) ) +#define SIG1(x) \ + ( MHDx_rotr64((x), 14) ^ MHDx_rotr64((x), 18) ^ MHDx_rotr64((x), 41) ) +#define sig0(x) \ + ( MHDx_rotr64((x), 1) ^ MHDx_rotr64((x), 8) ^ ((x) >> 7) ) +#define sig1(x) \ + ( MHDx_rotr64((x), 19) ^ MHDx_rotr64((x), 61) ^ ((x) >> 6) ) + + if(1) { + unsigned int t; + /* K constants array. + See FIPS PUB 180-4 section 4.2.3 for K values. */ + static const curl_uint64_t K[80] = { + CURL_UINT64_C(0x428a2f98d728ae22), CURL_UINT64_C(0x7137449123ef65cd), + CURL_UINT64_C(0xb5c0fbcfec4d3b2f), CURL_UINT64_C(0xe9b5dba58189dbbc), + CURL_UINT64_C(0x3956c25bf348b538), CURL_UINT64_C(0x59f111f1b605d019), + CURL_UINT64_C(0x923f82a4af194f9b), CURL_UINT64_C(0xab1c5ed5da6d8118), + CURL_UINT64_C(0xd807aa98a3030242), CURL_UINT64_C(0x12835b0145706fbe), + CURL_UINT64_C(0x243185be4ee4b28c), CURL_UINT64_C(0x550c7dc3d5ffb4e2), + CURL_UINT64_C(0x72be5d74f27b896f), CURL_UINT64_C(0x80deb1fe3b1696b1), + CURL_UINT64_C(0x9bdc06a725c71235), CURL_UINT64_C(0xc19bf174cf692694), + CURL_UINT64_C(0xe49b69c19ef14ad2), CURL_UINT64_C(0xefbe4786384f25e3), + CURL_UINT64_C(0x0fc19dc68b8cd5b5), CURL_UINT64_C(0x240ca1cc77ac9c65), + CURL_UINT64_C(0x2de92c6f592b0275), CURL_UINT64_C(0x4a7484aa6ea6e483), + CURL_UINT64_C(0x5cb0a9dcbd41fbd4), CURL_UINT64_C(0x76f988da831153b5), + CURL_UINT64_C(0x983e5152ee66dfab), CURL_UINT64_C(0xa831c66d2db43210), + CURL_UINT64_C(0xb00327c898fb213f), CURL_UINT64_C(0xbf597fc7beef0ee4), + CURL_UINT64_C(0xc6e00bf33da88fc2), CURL_UINT64_C(0xd5a79147930aa725), + CURL_UINT64_C(0x06ca6351e003826f), CURL_UINT64_C(0x142929670a0e6e70), + CURL_UINT64_C(0x27b70a8546d22ffc), CURL_UINT64_C(0x2e1b21385c26c926), + CURL_UINT64_C(0x4d2c6dfc5ac42aed), CURL_UINT64_C(0x53380d139d95b3df), + CURL_UINT64_C(0x650a73548baf63de), CURL_UINT64_C(0x766a0abb3c77b2a8), + CURL_UINT64_C(0x81c2c92e47edaee6), CURL_UINT64_C(0x92722c851482353b), + CURL_UINT64_C(0xa2bfe8a14cf10364), CURL_UINT64_C(0xa81a664bbc423001), + CURL_UINT64_C(0xc24b8b70d0f89791), CURL_UINT64_C(0xc76c51a30654be30), + CURL_UINT64_C(0xd192e819d6ef5218), CURL_UINT64_C(0xd69906245565a910), + CURL_UINT64_C(0xf40e35855771202a), CURL_UINT64_C(0x106aa07032bbd1b8), + CURL_UINT64_C(0x19a4c116b8d2d0c8), CURL_UINT64_C(0x1e376c085141ab53), + CURL_UINT64_C(0x2748774cdf8eeb99), CURL_UINT64_C(0x34b0bcb5e19b48a8), + CURL_UINT64_C(0x391c0cb3c5c95a63), CURL_UINT64_C(0x4ed8aa4ae3418acb), + CURL_UINT64_C(0x5b9cca4f7763e373), CURL_UINT64_C(0x682e6ff3d6b2b8a3), + CURL_UINT64_C(0x748f82ee5defb2fc), CURL_UINT64_C(0x78a5636f43172f60), + CURL_UINT64_C(0x84c87814a1f0ab72), CURL_UINT64_C(0x8cc702081a6439ec), + CURL_UINT64_C(0x90befffa23631e28), CURL_UINT64_C(0xa4506cebde82bde9), + CURL_UINT64_C(0xbef9a3f7b2c67915), CURL_UINT64_C(0xc67178f2e372532b), + CURL_UINT64_C(0xca273eceea26619c), CURL_UINT64_C(0xd186b8c721c0c207), + CURL_UINT64_C(0xeada7dd6cde0eb1e), CURL_UINT64_C(0xf57d4f7fee6ed178), + CURL_UINT64_C(0x06f067aa72176fba), CURL_UINT64_C(0x0a637dc5a2c898a6), + CURL_UINT64_C(0x113f9804bef90dae), CURL_UINT64_C(0x1b710b35131c471b), + CURL_UINT64_C(0x28db77f523047d84), CURL_UINT64_C(0x32caab7b40c72493), + CURL_UINT64_C(0x3c9ebe0a15c9bebc), CURL_UINT64_C(0x431d67c49c100d4c), + CURL_UINT64_C(0x4cc5d4becb3e42b6), CURL_UINT64_C(0x597f299cfc657e2a), + CURL_UINT64_C(0x5fcb6fab3ad6faec), CURL_UINT64_C(0x6c44198c4a475817) + }; + + /* One step of SHA-512/256 computation, + see FIPS PUB 180-4 section 6.4.2 step 3. + * Note: this macro updates working variables in-place, without rotation. + * Note: the first (vH += SIG1(vE) + Ch(vE,vF,vG) + kt + wt) equals T1 in + FIPS PUB 180-4 section 6.4.2 step 3. + the second (vH += SIG0(vA) + Maj(vE,vF,vC) equals T1 + T2 in + FIPS PUB 180-4 section 6.4.2 step 3. + * Note: 'wt' must be used exactly one time in this macro as macro for + 'wt' calculation may change other data as well every time when + used. */ +#define SHA2STEP64(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \ + (vD) += ((vH) += SIG1((vE)) + Sha512_Ch((vE),(vF),(vG)) + (kt) + (wt)); \ + (vH) += SIG0((vA)) + Sha512_Maj((vA),(vB),(vC)); } while (0) + + /* One step of SHA-512/256 computation with working variables rotation, + see FIPS PUB 180-4 section 6.4.2 step 3. This macro version reassigns + all working variables on each step. */ +#define SHA2STEP64RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \ + curl_uint64_t tmp_h_ = (vH); \ + SHA2STEP64((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt)); \ + (vH) = (vG); \ + (vG) = (vF); \ + (vF) = (vE); \ + (vE) = (vD); \ + (vD) = (vC); \ + (vC) = (vB); \ + (vB) = (vA); \ + (vA) = tmp_h_; } while(0) + + /* Get value of W(t) from input data buffer for 0 <= t <= 15, + See FIPS PUB 180-4 section 6.2. + Input data must be read in big-endian bytes order, + see FIPS PUB 180-4 section 3.1.2. */ +#define SHA512_GET_W_FROM_DATA(buf,t) \ + MHDX_GET_64BIT_BE( \ + ((const unsigned char*) (buf)) + (t) * SHA512_256_BYTES_IN_WORD) + + /* During first 16 steps, before making any calculation on each step, the + W element is read from the input data buffer as a big-endian value and + stored in the array of W elements. */ + for(t = 0; t < 16; ++t) { + SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \ + W[t] = SHA512_GET_W_FROM_DATA(data, t)); + } + + /* 'W' generation and assignment for 16 <= t <= 79. + See FIPS PUB 180-4 section 6.4.2. + As only the last 16 'W' are used in calculations, it is possible to + use 16 elements array of W as a cyclic buffer. + Note: ((t-16) & 15) have same value as (t & 15) */ +#define Wgen(w,t) \ + (curl_uint64_t)( (w)[(t - 16) & 15] + sig1((w)[((t) - 2) & 15]) \ + + (w)[((t) - 7) & 15] + sig0((w)[((t) - 15) & 15]) ) + + /* During the last 64 steps, before making any calculation on each step, + current W element is generated from other W elements of the cyclic + buffer and the generated value is stored back in the cyclic buffer. */ + for(t = 16; t < 80; ++t) { + SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \ + W[t & 15] = Wgen(W, t)); + } + } + + /* Compute and store the intermediate hash. + See FIPS PUB 180-4 section 6.4.2 step 4. */ + H[0] += a; + H[1] += b; + H[2] += c; + H[3] += d; + H[4] += e; + H[5] += f; + H[6] += g; + H[7] += h; +} + + +/** + * Process portion of bytes. + * + * @param context the calculation context + * @param data bytes to add to hash + * @param length number of bytes in @a data + * @return always CURLE_OK + */ +static CURLcode +MHDx_sha512_256_update(void *context, + const unsigned char *data, + size_t length) +{ + unsigned int bytes_have; /**< Number of bytes in the context buffer */ + struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context; + /* the void pointer here is required to mute Intel compiler warning */ + void *const ctx_buf = ctx->buffer; + + DEBUGASSERT((data != NULL) || (length == 0)); + + if(0 == length) + return CURLE_OK; /* Shortcut, do nothing */ + + /* Note: (count & (SHA512_256_BLOCK_SIZE-1)) + equals (count % SHA512_256_BLOCK_SIZE) for this block size. */ + bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1)); + ctx->count += length; + if(length > ctx->count) + ctx->count_bits_hi += 1U << 3; /* Value wrap */ + ctx->count_bits_hi += ctx->count >> 61; + ctx->count &= CURL_UINT64_C(0x1FFFFFFFFFFFFFFF); + + if(0 != bytes_have) { + unsigned int bytes_left = SHA512_256_BLOCK_SIZE - bytes_have; + if(length >= bytes_left) { + /* Combine new data with data in the buffer and process the full + block. */ + memcpy(((unsigned char *) ctx_buf) + bytes_have, + data, + bytes_left); + data += bytes_left; + length -= bytes_left; + MHDx_sha512_256_transform(ctx->H, ctx->buffer); + bytes_have = 0; + } + } + + while(SHA512_256_BLOCK_SIZE <= length) { + /* Process any full blocks of new data directly, + without copying to the buffer. */ + MHDx_sha512_256_transform(ctx->H, data); + data += SHA512_256_BLOCK_SIZE; + length -= SHA512_256_BLOCK_SIZE; + } + + if(0 != length) { + /* Copy incomplete block of new data (if any) + to the buffer. */ + memcpy(((unsigned char *) ctx_buf) + bytes_have, data, length); + } + + return CURLE_OK; +} + + + +/** + * Size of "length" insertion in bits. + * See FIPS PUB 180-4 section 5.1.2. + */ +#define SHA512_256_SIZE_OF_LEN_ADD_BITS 128 + +/** + * Size of "length" insertion in bytes. + */ +#define SHA512_256_SIZE_OF_LEN_ADD (SHA512_256_SIZE_OF_LEN_ADD_BITS / 8) + +/** + * Finalise SHA-512/256 calculation, return digest. + * + * @param context the calculation context + * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes + * @return always CURLE_OK + */ +static CURLcode +MHDx_sha512_256_finish(unsigned char *digest, + void *context) +{ + struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context; + curl_uint64_t num_bits; /**< Number of processed bits */ + unsigned int bytes_have; /**< Number of bytes in the context buffer */ + /* the void pointer here is required to mute Intel compiler warning */ + void *const ctx_buf = ctx->buffer; + + /* Memorise the number of processed bits. + The padding and other data added here during the postprocessing must + not change the amount of hashed data. */ + num_bits = ctx->count << 3; + + /* Note: (count & (SHA512_256_BLOCK_SIZE-1)) + equals (count % SHA512_256_BLOCK_SIZE) for this block size. */ + bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1)); + + /* Input data must be padded with a single bit "1", then with zeros and + the finally the length of data in bits must be added as the final bytes + of the last block. + See FIPS PUB 180-4 section 5.1.2. */ + + /* Data is always processed in form of bytes (not by individual bits), + therefore position of the first padding bit in byte is always + predefined (0x80). */ + /* Buffer always have space at least for one byte (as full buffers are + processed when formed). */ + ((unsigned char *) ctx_buf)[bytes_have++] = 0x80U; + + if(SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD) { + /* No space in the current block to put the total length of message. + Pad the current block with zeros and process it. */ + if(bytes_have < SHA512_256_BLOCK_SIZE) + memset(((unsigned char *) ctx_buf) + bytes_have, 0, + SHA512_256_BLOCK_SIZE - bytes_have); + /* Process the full block. */ + MHDx_sha512_256_transform(ctx->H, ctx->buffer); + /* Start the new block. */ + bytes_have = 0; + } + + /* Pad the rest of the buffer with zeros. */ + memset(((unsigned char *) ctx_buf) + bytes_have, 0, + SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have); + /* Put high part of number of bits in processed message and then lower + part of number of bits as big-endian values. + See FIPS PUB 180-4 section 5.1.2. */ + /* Note: the target location is predefined and buffer is always aligned */ + MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ + + SHA512_256_BLOCK_SIZE \ + - SHA512_256_SIZE_OF_LEN_ADD, \ + ctx->count_bits_hi); + MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ + + SHA512_256_BLOCK_SIZE \ + - SHA512_256_SIZE_OF_LEN_ADD \ + + SHA512_256_BYTES_IN_WORD, \ + num_bits); + /* Process the full final block. */ + MHDx_sha512_256_transform(ctx->H, ctx->buffer); + + /* Put in BE mode the leftmost part of the hash as the final digest. + See FIPS PUB 180-4 section 6.7. */ + + MHDX_PUT_64BIT_BE((digest + 0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]); + MHDX_PUT_64BIT_BE((digest + 1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]); + MHDX_PUT_64BIT_BE((digest + 2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]); + MHDX_PUT_64BIT_BE((digest + 3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]); + + /* Erase potentially sensitive data. */ + memset(ctx, 0, sizeof(struct mhdx_sha512_256ctx)); + + return CURLE_OK; +} + +/* Map to the local implementation */ +#define Curl_sha512_256_init MHDx_sha512_256_init +#define Curl_sha512_256_update MHDx_sha512_256_update +#define Curl_sha512_256_finish MHDx_sha512_256_finish + +#endif /* Local SHA-512/256 code */ + + +/** + * Compute SHA-512/256 hash for the given data in one function call + * @param[out] output the pointer to put the hash + * @param[in] input the pointer to the data to process + * @param input_size the size of the data pointed by @a input + * @return always #CURLE_OK + */ +CURLcode +Curl_sha512_256it(unsigned char *output, const unsigned char *input, + size_t input_size) +{ + Curl_sha512_256_ctx ctx; + CURLcode res; + + res = Curl_sha512_256_init(&ctx); + if(res != CURLE_OK) + return res; + + res = Curl_sha512_256_update(&ctx, (const void *) input, input_size); + + if(res != CURLE_OK) { + (void) Curl_sha512_256_finish(output, &ctx); + return res; + } + + return Curl_sha512_256_finish(output, &ctx); +} + +/* Wrapper function, takes 'unsigned int' as length type, returns void */ +static void +Curl_sha512_256_update_i(void *context, + const unsigned char *data, + unsigned int length) +{ + /* Hypothetically the function may fail, but assume it does not */ + (void) Curl_sha512_256_update(context, data, length); +} + +/* Wrapper function, returns void */ +static void +Curl_sha512_256_finish_v(unsigned char *result, + void *context) +{ + /* Hypothetically the function may fail, but assume it does not */ + (void) Curl_sha512_256_finish(result, context); +} + +/* Wrapper function, takes 'unsigned int' as length type, returns void */ + +const struct HMAC_params Curl_HMAC_SHA512_256[] = { + { + /* Initialize context procedure. */ + Curl_sha512_256_init, + /* Update context with data. */ + Curl_sha512_256_update_i, + /* Get final result procedure. */ + Curl_sha512_256_finish_v, + /* Context structure size. */ + sizeof(Curl_sha512_256_ctx), + /* Maximum key length (bytes). */ + SHA512_256_BLOCK_SIZE, + /* Result length (bytes). */ + SHA512_256_DIGEST_SIZE + } +}; + +#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */ diff --git a/third_party/curl/lib/curl_sha512_256.h b/third_party/curl/lib/curl_sha512_256.h new file mode 100644 index 0000000000..30a9f140ea --- /dev/null +++ b/third_party/curl/lib/curl_sha512_256.h @@ -0,0 +1,44 @@ +#ifndef HEADER_CURL_SHA512_256_H +#define HEADER_CURL_SHA512_256_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Evgeny Grin (Karlson2k), . + * + * 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 + * + ***************************************************************************/ + +#if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256) + +#include +#include "curl_hmac.h" + +#define CURL_HAVE_SHA512_256 + +extern const struct HMAC_params Curl_HMAC_SHA512_256[1]; + +#define SHA512_256_DIGEST_LENGTH 32 + +CURLcode +Curl_sha512_256it(unsigned char *output, const unsigned char *input, + size_t input_size); + +#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */ + +#endif /* HEADER_CURL_SHA256_H */ diff --git a/third_party/curl/lib/curl_sspi.c b/third_party/curl/lib/curl_sspi.c new file mode 100644 index 0000000000..2c9e38d90e --- /dev/null +++ b/third_party/curl/lib/curl_sspi.c @@ -0,0 +1,239 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_WINDOWS_SSPI + +#include +#include "curl_sspi.h" +#include "curl_multibyte.h" +#include "system_win32.h" +#include "version_win32.h" +#include "warnless.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* We use our own typedef here since some headers might lack these */ +typedef PSecurityFunctionTable (APIENTRY *INITSECURITYINTERFACE_FN)(VOID); + +/* See definition of SECURITY_ENTRYPOINT in sspi.h */ +#ifdef UNICODE +# ifdef _WIN32_WCE +# define SECURITYENTRYPOINT L"InitSecurityInterfaceW" +# else +# define SECURITYENTRYPOINT "InitSecurityInterfaceW" +# endif +#else +# define SECURITYENTRYPOINT "InitSecurityInterfaceA" +#endif + +/* Handle of security.dll or secur32.dll, depending on Windows version */ +HMODULE s_hSecDll = NULL; + +/* Pointer to SSPI dispatch table */ +PSecurityFunctionTable s_pSecFn = NULL; + +/* + * Curl_sspi_global_init() + * + * This is used to load the Security Service Provider Interface (SSPI) + * dynamic link library portably across all Windows versions, without + * the need to directly link libcurl, nor the application using it, at + * build time. + * + * Once this function has been executed, Windows SSPI functions can be + * called through the Security Service Provider Interface dispatch table. + * + * Parameters: + * + * None. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_sspi_global_init(void) +{ + INITSECURITYINTERFACE_FN pInitSecurityInterface; + + /* If security interface is not yet initialized try to do this */ + if(!s_hSecDll) { + /* Security Service Provider Interface (SSPI) functions are located in + * security.dll on WinNT 4.0 and in secur32.dll on Win9x. Win2K and XP + * have both these DLLs (security.dll forwards calls to secur32.dll) */ + + /* Load SSPI dll into the address space of the calling process */ + if(curlx_verify_windows_version(4, 0, 0, PLATFORM_WINNT, VERSION_EQUAL)) + s_hSecDll = Curl_load_library(TEXT("security.dll")); + else + s_hSecDll = Curl_load_library(TEXT("secur32.dll")); + if(!s_hSecDll) + return CURLE_FAILED_INIT; + + /* Get address of the InitSecurityInterfaceA function from the SSPI dll */ + pInitSecurityInterface = + CURLX_FUNCTION_CAST(INITSECURITYINTERFACE_FN, + (GetProcAddress(s_hSecDll, SECURITYENTRYPOINT))); + if(!pInitSecurityInterface) + return CURLE_FAILED_INIT; + + /* Get pointer to Security Service Provider Interface dispatch table */ + s_pSecFn = pInitSecurityInterface(); + if(!s_pSecFn) + return CURLE_FAILED_INIT; + } + + return CURLE_OK; +} + +/* + * Curl_sspi_global_cleanup() + * + * This deinitializes the Security Service Provider Interface from libcurl. + * + * Parameters: + * + * None. + */ +void Curl_sspi_global_cleanup(void) +{ + if(s_hSecDll) { + FreeLibrary(s_hSecDll); + s_hSecDll = NULL; + s_pSecFn = NULL; + } +} + +/* + * Curl_create_sspi_identity() + * + * This is used to populate a SSPI identity structure based on the supplied + * username and password. + * + * Parameters: + * + * userp [in] - The username in the format User or Domain\User. + * passwdp [in] - The user's password. + * identity [in/out] - The identity structure. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, + SEC_WINNT_AUTH_IDENTITY *identity) +{ + xcharp_u useranddomain; + xcharp_u user, dup_user; + xcharp_u domain, dup_domain; + xcharp_u passwd, dup_passwd; + size_t domlen = 0; + + domain.const_tchar_ptr = TEXT(""); + + /* Initialize the identity */ + memset(identity, 0, sizeof(*identity)); + + useranddomain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *)userp); + if(!useranddomain.tchar_ptr) + return CURLE_OUT_OF_MEMORY; + + user.const_tchar_ptr = _tcschr(useranddomain.const_tchar_ptr, TEXT('\\')); + if(!user.const_tchar_ptr) + user.const_tchar_ptr = _tcschr(useranddomain.const_tchar_ptr, TEXT('/')); + + if(user.tchar_ptr) { + domain.tchar_ptr = useranddomain.tchar_ptr; + domlen = user.tchar_ptr - useranddomain.tchar_ptr; + user.tchar_ptr++; + } + else { + user.tchar_ptr = useranddomain.tchar_ptr; + domain.const_tchar_ptr = TEXT(""); + domlen = 0; + } + + /* Setup the identity's user and length */ + dup_user.tchar_ptr = _tcsdup(user.tchar_ptr); + if(!dup_user.tchar_ptr) { + curlx_unicodefree(useranddomain.tchar_ptr); + return CURLE_OUT_OF_MEMORY; + } + identity->User = dup_user.tbyte_ptr; + identity->UserLength = curlx_uztoul(_tcslen(dup_user.tchar_ptr)); + dup_user.tchar_ptr = NULL; + + /* Setup the identity's domain and length */ + dup_domain.tchar_ptr = malloc(sizeof(TCHAR) * (domlen + 1)); + if(!dup_domain.tchar_ptr) { + curlx_unicodefree(useranddomain.tchar_ptr); + return CURLE_OUT_OF_MEMORY; + } + _tcsncpy(dup_domain.tchar_ptr, domain.tchar_ptr, domlen); + *(dup_domain.tchar_ptr + domlen) = TEXT('\0'); + identity->Domain = dup_domain.tbyte_ptr; + identity->DomainLength = curlx_uztoul(domlen); + dup_domain.tchar_ptr = NULL; + + curlx_unicodefree(useranddomain.tchar_ptr); + + /* Setup the identity's password and length */ + passwd.tchar_ptr = curlx_convert_UTF8_to_tchar((char *)passwdp); + if(!passwd.tchar_ptr) + return CURLE_OUT_OF_MEMORY; + dup_passwd.tchar_ptr = _tcsdup(passwd.tchar_ptr); + if(!dup_passwd.tchar_ptr) { + curlx_unicodefree(passwd.tchar_ptr); + return CURLE_OUT_OF_MEMORY; + } + identity->Password = dup_passwd.tbyte_ptr; + identity->PasswordLength = curlx_uztoul(_tcslen(dup_passwd.tchar_ptr)); + dup_passwd.tchar_ptr = NULL; + + curlx_unicodefree(passwd.tchar_ptr); + + /* Setup the identity's flags */ + identity->Flags = SECFLAG_WINNT_AUTH_IDENTITY; + + return CURLE_OK; +} + +/* + * Curl_sspi_free_identity() + * + * This is used to free the contents of a SSPI identifier structure. + * + * Parameters: + * + * identity [in/out] - The identity structure. + */ +void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity) +{ + if(identity) { + Curl_safefree(identity->User); + Curl_safefree(identity->Password); + Curl_safefree(identity->Domain); + } +} + +#endif /* USE_WINDOWS_SSPI */ diff --git a/third_party/curl/lib/curl_sspi.h b/third_party/curl/lib/curl_sspi.h new file mode 100644 index 0000000000..b26c391569 --- /dev/null +++ b/third_party/curl/lib/curl_sspi.h @@ -0,0 +1,123 @@ +#ifndef HEADER_CURL_SSPI_H +#define HEADER_CURL_SSPI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_WINDOWS_SSPI + +#include + +/* + * When including the following three headers, it is mandatory to define either + * SECURITY_WIN32 or SECURITY_KERNEL, indicating who is compiling the code. + */ + +#undef SECURITY_WIN32 +#undef SECURITY_KERNEL +#define SECURITY_WIN32 1 +#include +#include +#include + +CURLcode Curl_sspi_global_init(void); +void Curl_sspi_global_cleanup(void); + +/* This is used to populate the domain in a SSPI identity structure */ +CURLcode Curl_override_sspi_http_realm(const char *chlg, + SEC_WINNT_AUTH_IDENTITY *identity); + +/* This is used to generate an SSPI identity structure */ +CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, + SEC_WINNT_AUTH_IDENTITY *identity); + +/* This is used to free an SSPI identity structure */ +void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity); + +/* Forward-declaration of global variables defined in curl_sspi.c */ +extern HMODULE s_hSecDll; +extern PSecurityFunctionTable s_pSecFn; + +/* Provide some definitions missing in old headers */ +#define SP_NAME_DIGEST "WDigest" +#define SP_NAME_NTLM "NTLM" +#define SP_NAME_NEGOTIATE "Negotiate" +#define SP_NAME_KERBEROS "Kerberos" + +#ifndef ISC_REQ_USE_HTTP_STYLE +#define ISC_REQ_USE_HTTP_STYLE 0x01000000 +#endif + +#ifndef SEC_E_INVALID_PARAMETER +# define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL) +#endif +#ifndef SEC_E_DELEGATION_POLICY +# define SEC_E_DELEGATION_POLICY ((HRESULT)0x8009035EL) +#endif +#ifndef SEC_E_POLICY_NLTM_ONLY +# define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL) +#endif + +#ifndef SEC_I_SIGNATURE_NEEDED +# define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL) +#endif + +#ifndef CRYPT_E_REVOKED +# define CRYPT_E_REVOKED ((HRESULT)0x80092010L) +#endif + +#ifndef CRYPT_E_NO_REVOCATION_DLL +# define CRYPT_E_NO_REVOCATION_DLL ((HRESULT)0x80092011L) +#endif + +#ifndef CRYPT_E_NO_REVOCATION_CHECK +# define CRYPT_E_NO_REVOCATION_CHECK ((HRESULT)0x80092012L) +#endif + +#ifndef CRYPT_E_REVOCATION_OFFLINE +# define CRYPT_E_REVOCATION_OFFLINE ((HRESULT)0x80092013L) +#endif + +#ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE +# define CRYPT_E_NOT_IN_REVOCATION_DATABASE ((HRESULT)0x80092014L) +#endif + +#ifdef UNICODE +# define SECFLAG_WINNT_AUTH_IDENTITY \ + (unsigned long)SEC_WINNT_AUTH_IDENTITY_UNICODE +#else +# define SECFLAG_WINNT_AUTH_IDENTITY \ + (unsigned long)SEC_WINNT_AUTH_IDENTITY_ANSI +#endif + +/* + * Definitions required from ntsecapi.h are directly provided below this point + * to avoid including ntsecapi.h due to a conflict with OpenSSL's safestack.h + */ +#define KERB_WRAP_NO_ENCRYPT 0x80000001 + +#endif /* USE_WINDOWS_SSPI */ + +#endif /* HEADER_CURL_SSPI_H */ diff --git a/third_party/curl/lib/curl_threads.c b/third_party/curl/lib/curl_threads.c new file mode 100644 index 0000000000..fb4af73d02 --- /dev/null +++ b/third_party/curl/lib/curl_threads.c @@ -0,0 +1,159 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#if defined(USE_THREADS_POSIX) +# ifdef HAVE_PTHREAD_H +# include +# endif +#elif defined(USE_THREADS_WIN32) +# include +#endif + +#include "curl_threads.h" +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +#if defined(USE_THREADS_POSIX) + +struct Curl_actual_call { + unsigned int (*func)(void *); + void *arg; +}; + +static void *curl_thread_create_thunk(void *arg) +{ + struct Curl_actual_call *ac = arg; + unsigned int (*func)(void *) = ac->func; + void *real_arg = ac->arg; + + free(ac); + + (*func)(real_arg); + + return 0; +} + +curl_thread_t Curl_thread_create(unsigned int (*func) (void *), void *arg) +{ + curl_thread_t t = malloc(sizeof(pthread_t)); + struct Curl_actual_call *ac = malloc(sizeof(struct Curl_actual_call)); + if(!(ac && t)) + goto err; + + ac->func = func; + ac->arg = arg; + + if(pthread_create(t, NULL, curl_thread_create_thunk, ac) != 0) + goto err; + + return t; + +err: + free(t); + free(ac); + return curl_thread_t_null; +} + +void Curl_thread_destroy(curl_thread_t hnd) +{ + if(hnd != curl_thread_t_null) { + pthread_detach(*hnd); + free(hnd); + } +} + +int Curl_thread_join(curl_thread_t *hnd) +{ + int ret = (pthread_join(**hnd, NULL) == 0); + + free(*hnd); + *hnd = curl_thread_t_null; + + return ret; +} + +#elif defined(USE_THREADS_WIN32) + +curl_thread_t Curl_thread_create( +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) + DWORD +#else + unsigned int +#endif + (CURL_STDCALL *func) (void *), + void *arg) +{ +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) + typedef HANDLE curl_win_thread_handle_t; +#else + typedef uintptr_t curl_win_thread_handle_t; +#endif + curl_thread_t t; + curl_win_thread_handle_t thread_handle; +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) + thread_handle = CreateThread(NULL, 0, func, arg, 0, NULL); +#else + thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL); +#endif + t = (curl_thread_t)thread_handle; + if((t == 0) || (t == LongToHandle(-1L))) { +#ifdef _WIN32_WCE + DWORD gle = GetLastError(); + errno = ((gle == ERROR_ACCESS_DENIED || + gle == ERROR_NOT_ENOUGH_MEMORY) ? + EACCES : EINVAL); +#endif + return curl_thread_t_null; + } + return t; +} + +void Curl_thread_destroy(curl_thread_t hnd) +{ + if(hnd != curl_thread_t_null) + CloseHandle(hnd); +} + +int Curl_thread_join(curl_thread_t *hnd) +{ +#if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \ + (_WIN32_WINNT < _WIN32_WINNT_VISTA) + int ret = (WaitForSingleObject(*hnd, INFINITE) == WAIT_OBJECT_0); +#else + int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0); +#endif + + Curl_thread_destroy(*hnd); + + *hnd = curl_thread_t_null; + + return ret; +} + +#endif /* USE_THREADS_* */ diff --git a/third_party/curl/lib/curl_threads.h b/third_party/curl/lib/curl_threads.h new file mode 100644 index 0000000000..be22352dcb --- /dev/null +++ b/third_party/curl/lib/curl_threads.h @@ -0,0 +1,70 @@ +#ifndef HEADER_CURL_THREADS_H +#define HEADER_CURL_THREADS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if defined(USE_THREADS_POSIX) +# define CURL_STDCALL +# define curl_mutex_t pthread_mutex_t +# define curl_thread_t pthread_t * +# define curl_thread_t_null (pthread_t *)0 +# define Curl_mutex_init(m) pthread_mutex_init(m, NULL) +# define Curl_mutex_acquire(m) pthread_mutex_lock(m) +# define Curl_mutex_release(m) pthread_mutex_unlock(m) +# define Curl_mutex_destroy(m) pthread_mutex_destroy(m) +#elif defined(USE_THREADS_WIN32) +# define CURL_STDCALL __stdcall +# define curl_mutex_t CRITICAL_SECTION +# define curl_thread_t HANDLE +# define curl_thread_t_null (HANDLE)0 +# if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \ + (_WIN32_WINNT < _WIN32_WINNT_VISTA) +# define Curl_mutex_init(m) InitializeCriticalSection(m) +# else +# define Curl_mutex_init(m) InitializeCriticalSectionEx(m, 0, 1) +# endif +# define Curl_mutex_acquire(m) EnterCriticalSection(m) +# define Curl_mutex_release(m) LeaveCriticalSection(m) +# define Curl_mutex_destroy(m) DeleteCriticalSection(m) +#endif + +#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) + +curl_thread_t Curl_thread_create( +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) + DWORD +#else + unsigned int +#endif + (CURL_STDCALL *func) (void *), + void *arg); + +void Curl_thread_destroy(curl_thread_t hnd); + +int Curl_thread_join(curl_thread_t *hnd); + +#endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */ + +#endif /* HEADER_CURL_THREADS_H */ diff --git a/third_party/curl/lib/curl_trc.c b/third_party/curl/lib/curl_trc.c new file mode 100644 index 0000000000..f017e21a5a --- /dev/null +++ b/third_party/curl/lib/curl_trc.c @@ -0,0 +1,331 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "curl_trc.h" +#include "urldata.h" +#include "easyif.h" +#include "cfilters.h" +#include "timeval.h" +#include "multiif.h" +#include "strcase.h" + +#include "cf-socket.h" +#include "connect.h" +#include "doh.h" +#include "http2.h" +#include "http_proxy.h" +#include "cf-h1-proxy.h" +#include "cf-h2-proxy.h" +#include "cf-haproxy.h" +#include "cf-https-connect.h" +#include "socks.h" +#include "strtok.h" +#include "vtls/vtls.h" +#include "vquic/vquic.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +void Curl_debug(struct Curl_easy *data, curl_infotype type, + char *ptr, size_t size) +{ + if(data->set.verbose) { + static const char s_infotype[CURLINFO_END][3] = { + "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; + if(data->set.fdebug) { + bool inCallback = Curl_is_in_callback(data); + Curl_set_in_callback(data, true); + (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata); + Curl_set_in_callback(data, inCallback); + } + else { + switch(type) { + case CURLINFO_TEXT: + case CURLINFO_HEADER_OUT: + case CURLINFO_HEADER_IN: + fwrite(s_infotype[type], 2, 1, data->set.err); + fwrite(ptr, size, 1, data->set.err); + break; + default: /* nada */ + break; + } + } + } +} + + +/* Curl_failf() is for messages stating why we failed. + * The message SHALL NOT include any LF or CR. + */ +void Curl_failf(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(data->set.verbose || data->set.errorbuffer) { + va_list ap; + int len; + char error[CURL_ERROR_SIZE + 2]; + va_start(ap, fmt); + len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); + + if(data->set.errorbuffer && !data->state.errorbuf) { + strcpy(data->set.errorbuffer, error); + data->state.errorbuf = TRUE; /* wrote error string */ + } + error[len++] = '\n'; + error[len] = '\0'; + Curl_debug(data, CURLINFO_TEXT, error, len); + va_end(ap); + } +} + +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + +/* Curl_infof() is for info message along the way */ +#define MAXINFO 2048 + +static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat, + const char * const fmt, va_list ap) CURL_PRINTF(3, 0); + +static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat, + const char * const fmt, va_list ap) +{ + int len = 0; + char buffer[MAXINFO + 2]; + if(feat) + len = msnprintf(buffer, MAXINFO, "[%s] ", feat->name); + len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap); + buffer[len++] = '\n'; + buffer[len] = '\0'; + Curl_debug(data, CURLINFO_TEXT, buffer, len); +} + +void Curl_infof(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_is_verbose(data)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, data->state.feat, fmt, ap); + va_end(ap); + } +} + +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, + const char *fmt, ...) +{ + DEBUGASSERT(cf); + if(Curl_trc_cf_is_verbose(cf, data)) { + va_list ap; + int len = 0; + char buffer[MAXINFO + 2]; + if(data->state.feat) + len += msnprintf(buffer + len, MAXINFO - len, "[%s] ", + data->state.feat->name); + if(cf->sockindex) + len += msnprintf(buffer + len, MAXINFO - len, "[%s-%d] ", + cf->cft->name, cf->sockindex); + else + len += msnprintf(buffer + len, MAXINFO - len, "[%s] ", cf->cft->name); + va_start(ap, fmt); + len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap); + va_end(ap); + buffer[len++] = '\n'; + buffer[len] = '\0'; + Curl_debug(data, CURLINFO_TEXT, buffer, len); + } +} + +struct curl_trc_feat Curl_trc_feat_read = { + "READ", + CURL_LOG_LVL_NONE, +}; +struct curl_trc_feat Curl_trc_feat_write = { + "WRITE", + CURL_LOG_LVL_NONE, +}; + +void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_read, fmt, ap); + va_end(ap); + } +} + +void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_write, fmt, ap); + va_end(ap); + } +} + +#ifndef CURL_DISABLE_FTP +struct curl_trc_feat Curl_trc_feat_ftp = { + "FTP", + CURL_LOG_LVL_NONE, +}; + +void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_ftp, fmt, ap); + va_end(ap); + } +} +#endif /* !CURL_DISABLE_FTP */ + +static struct curl_trc_feat *trc_feats[] = { + &Curl_trc_feat_read, + &Curl_trc_feat_write, +#ifndef CURL_DISABLE_FTP + &Curl_trc_feat_ftp, +#endif +#ifndef CURL_DISABLE_DOH + &Curl_doh_trc, +#endif + NULL, +}; + +static struct Curl_cftype *cf_types[] = { + &Curl_cft_tcp, + &Curl_cft_udp, + &Curl_cft_unix, + &Curl_cft_tcp_accept, + &Curl_cft_happy_eyeballs, + &Curl_cft_setup, +#ifdef USE_NGHTTP2 + &Curl_cft_nghttp2, +#endif +#ifdef USE_SSL + &Curl_cft_ssl, +#ifndef CURL_DISABLE_PROXY + &Curl_cft_ssl_proxy, +#endif +#endif +#if !defined(CURL_DISABLE_PROXY) +#if !defined(CURL_DISABLE_HTTP) + &Curl_cft_h1_proxy, +#ifdef USE_NGHTTP2 + &Curl_cft_h2_proxy, +#endif + &Curl_cft_http_proxy, +#endif /* !CURL_DISABLE_HTTP */ + &Curl_cft_haproxy, + &Curl_cft_socks_proxy, +#endif /* !CURL_DISABLE_PROXY */ +#ifdef USE_HTTP3 + &Curl_cft_http3, +#endif +#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) + &Curl_cft_http_connect, +#endif + NULL, +}; + +CURLcode Curl_trc_opt(const char *config) +{ + char *token, *tok_buf, *tmp; + size_t i; + int lvl; + + tmp = strdup(config); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + token = strtok_r(tmp, ", ", &tok_buf); + while(token) { + switch(*token) { + case '-': + lvl = CURL_LOG_LVL_NONE; + ++token; + break; + case '+': + lvl = CURL_LOG_LVL_INFO; + ++token; + break; + default: + lvl = CURL_LOG_LVL_INFO; + break; + } + for(i = 0; cf_types[i]; ++i) { + if(strcasecompare(token, "all")) { + cf_types[i]->log_level = lvl; + } + else if(strcasecompare(token, cf_types[i]->name)) { + cf_types[i]->log_level = lvl; + break; + } + } + for(i = 0; trc_feats[i]; ++i) { + if(strcasecompare(token, "all")) { + trc_feats[i]->log_level = lvl; + } + else if(strcasecompare(token, trc_feats[i]->name)) { + trc_feats[i]->log_level = lvl; + break; + } + } + token = strtok_r(NULL, ", ", &tok_buf); + } + free(tmp); + return CURLE_OK; +} + +CURLcode Curl_trc_init(void) +{ +#ifdef DEBUGBUILD + /* WIP: we use the auto-init from an env var only in DEBUG builds for + * convenience. */ + const char *config = getenv("CURL_DEBUG"); + if(config) { + return Curl_trc_opt(config); + } +#endif /* DEBUGBUILD */ + return CURLE_OK; +} +#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */ + +CURLcode Curl_trc_init(void) +{ + return CURLE_OK; +} + +#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */ diff --git a/third_party/curl/lib/curl_trc.h b/third_party/curl/lib/curl_trc.h new file mode 100644 index 0000000000..3d38018342 --- /dev/null +++ b/third_party/curl/lib/curl_trc.h @@ -0,0 +1,200 @@ +#ifndef HEADER_CURL_TRC_H +#define HEADER_CURL_TRC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +struct Curl_easy; +struct Curl_cfilter; + +/** + * Init logging, return != 0 on failure. + */ +CURLcode Curl_trc_init(void); + +/** + * Configure tracing. May be called several times during global + * initialization. Later calls may not take effect. + * + * Configuration format supported: + * - comma-separated list of component names to enable logging on. + * E.g. 'http/2,ssl'. Unknown names are ignored. Names are compared + * case-insensitive. + * - component 'all' applies to all known log components + * - prefixing a component with '+' or '-' will en-/disable logging for + * that component + * Example: 'all,-ssl' would enable logging for all components but the + * SSL filters. + * + * @param config configuration string + */ +CURLcode Curl_trc_opt(const char *config); + +/* the function used to output verbose information */ +void Curl_debug(struct Curl_easy *data, curl_infotype type, + char *ptr, size_t size); + +/** + * Output a failure message on registered callbacks for transfer. + */ +void Curl_failf(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); + +#define failf Curl_failf + +#define CURL_LOG_LVL_NONE 0 +#define CURL_LOG_LVL_INFO 1 + + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define CURL_HAVE_C99 +#endif + +#ifdef CURL_HAVE_C99 +#define infof(data, ...) \ + do { if(Curl_trc_is_verbose(data)) \ + Curl_infof(data, __VA_ARGS__); } while(0) +#define CURL_TRC_CF(data, cf, ...) \ + do { if(Curl_trc_cf_is_verbose(cf, data)) \ + Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0) +#define CURL_TRC_WRITE(data, ...) \ + do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) \ + Curl_trc_write(data, __VA_ARGS__); } while(0) +#define CURL_TRC_READ(data, ...) \ + do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) \ + Curl_trc_read(data, __VA_ARGS__); } while(0) + +#ifndef CURL_DISABLE_FTP +#define CURL_TRC_FTP(data, ...) \ + do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) \ + Curl_trc_ftp(data, __VA_ARGS__); } while(0) +#endif /* !CURL_DISABLE_FTP */ + +#else /* CURL_HAVE_C99 */ + +#define infof Curl_infof +#define CURL_TRC_CF Curl_trc_cf_infof +#define CURL_TRC_WRITE Curl_trc_write +#define CURL_TRC_READ Curl_trc_read + +#ifndef CURL_DISABLE_FTP +#define CURL_TRC_FTP Curl_trc_ftp +#endif + +#endif /* !CURL_HAVE_C99 */ + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +/* informational messages enabled */ + +struct curl_trc_feat { + const char *name; + int log_level; +}; +extern struct curl_trc_feat Curl_trc_feat_read; +extern struct curl_trc_feat Curl_trc_feat_write; + +#define Curl_trc_is_verbose(data) \ + ((data) && (data)->set.verbose && \ + (!(data)->state.feat || \ + ((data)->state.feat->log_level >= CURL_LOG_LVL_INFO))) +#define Curl_trc_cf_is_verbose(cf, data) \ + (Curl_trc_is_verbose(data) && \ + (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) +#define Curl_trc_ft_is_verbose(data, ft) \ + (Curl_trc_is_verbose(data) && \ + (ft)->log_level >= CURL_LOG_LVL_INFO) + +/** + * Output an informational message when transfer's verbose logging is enabled. + */ +void Curl_infof(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); + +/** + * Output an informational message when both transfer's verbose logging + * and connection filters verbose logging are enabled. + */ +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, + const char *fmt, ...) CURL_PRINTF(3, 4); +void Curl_trc_ft_infof(struct Curl_easy *data, struct curl_trc_feat *ft, + const char *fmt, ...) CURL_PRINTF(3, 4); +void Curl_trc_write(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +void Curl_trc_read(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); + +#ifndef CURL_DISABLE_FTP +extern struct curl_trc_feat Curl_trc_feat_ftp; +void Curl_trc_ftp(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +#endif + + +#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */ +/* All informational messages are not compiled in for size savings */ + +#define Curl_trc_is_verbose(d) (FALSE) +#define Curl_trc_cf_is_verbose(x,y) (FALSE) +#define Curl_trc_ft_is_verbose(x,y) (FALSE) + +static void Curl_infof(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} + +static void Curl_trc_cf_infof(struct Curl_easy *data, + struct Curl_cfilter *cf, + const char *fmt, ...) +{ + (void)data; (void)cf; (void)fmt; +} + +struct curl_trc_feat; + +static void Curl_trc_ft_infof(struct Curl_easy *data, + struct curl_trc_feat *ft, + const char *fmt, ...) +{ + (void)data; (void)ft; (void)fmt; +} + +static void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} + +static void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} + +#ifndef CURL_DISABLE_FTP +static void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} +#endif + +#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */ + +#endif /* HEADER_CURL_TRC_H */ diff --git a/third_party/curl/lib/curlx.h b/third_party/curl/lib/curlx.h new file mode 100644 index 0000000000..54e4279572 --- /dev/null +++ b/third_party/curl/lib/curlx.h @@ -0,0 +1,117 @@ +#ifndef HEADER_CURL_CURLX_H +#define HEADER_CURL_CURLX_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Defines protos and includes all header files that provide the curlx_* + * functions. The curlx_* functions are not part of the libcurl API, but are + * stand-alone functions whose sources can be built and linked by apps if need + * be. + */ + +#include +/* this is still a public header file that provides the curl_mprintf() + functions while they still are offered publicly. They will be made library- + private one day */ + +#include "strcase.h" +/* "strcase.h" provides the strcasecompare protos */ + +#include "strtoofft.h" +/* "strtoofft.h" provides this function: curlx_strtoofft(), returns a + curl_off_t number from a given string. +*/ + +#include "nonblock.h" +/* "nonblock.h" provides curlx_nonblock() */ + +#include "warnless.h" +/* "warnless.h" provides functions: + + curlx_ultous() + curlx_ultouc() + curlx_uztosi() +*/ + +#include "curl_multibyte.h" +/* "curl_multibyte.h" provides these functions and macros: + + curlx_convert_UTF8_to_wchar() + curlx_convert_wchar_to_UTF8() + curlx_convert_UTF8_to_tchar() + curlx_convert_tchar_to_UTF8() + curlx_unicodefree() +*/ + +#include "version_win32.h" +/* "version_win32.h" provides curlx_verify_windows_version() */ + +/* Now setup curlx_ * names for the functions that are to become curlx_ and + be removed from a future libcurl official API: + curlx_getenv + curlx_mprintf (and its variations) + curlx_strcasecompare + curlx_strncasecompare + +*/ + +#define curlx_mvsnprintf curl_mvsnprintf +#define curlx_msnprintf curl_msnprintf +#define curlx_maprintf curl_maprintf +#define curlx_mvaprintf curl_mvaprintf +#define curlx_msprintf curl_msprintf +#define curlx_mprintf curl_mprintf +#define curlx_mfprintf curl_mfprintf +#define curlx_mvsprintf curl_mvsprintf +#define curlx_mvprintf curl_mvprintf +#define curlx_mvfprintf curl_mvfprintf + +#ifdef ENABLE_CURLX_PRINTF +/* If this define is set, we define all "standard" printf() functions to use + the curlx_* version instead. It makes the source code transparent and + easier to understand/patch. Undefine them first. */ +# undef printf +# undef fprintf +# undef sprintf +# undef msnprintf +# undef vprintf +# undef vfprintf +# undef vsprintf +# undef mvsnprintf +# undef aprintf +# undef vaprintf + +# define printf curlx_mprintf +# define fprintf curlx_mfprintf +# define sprintf curlx_msprintf +# define msnprintf curlx_msnprintf +# define vprintf curlx_mvprintf +# define vfprintf curlx_mvfprintf +# define mvsnprintf curlx_mvsnprintf +# define aprintf curlx_maprintf +# define vaprintf curlx_mvaprintf +#endif /* ENABLE_CURLX_PRINTF */ + +#endif /* HEADER_CURL_CURLX_H */ diff --git a/third_party/curl/lib/cw-out.c b/third_party/curl/lib/cw-out.c new file mode 100644 index 0000000000..56ec4162e8 --- /dev/null +++ b/third_party/curl/lib/cw-out.c @@ -0,0 +1,474 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "urldata.h" +#include "cfilters.h" +#include "headers.h" +#include "multiif.h" +#include "sendf.h" +#include "cw-out.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +/** + * OVERALL DESIGN of this client writer + * + * The 'cw-out' writer is supposed to be the last writer in a transfer's + * stack. It is always added when that stack is initialized. Its purpose + * is to pass BODY and HEADER bytes to the client-installed callback + * functions. + * + * These callback may return `CURL_WRITEFUNC_PAUSE` to indicate that the + * data had not been written and the whole transfer should stop receiving + * new data. Or at least, stop calling the functions. When the transfer + * is "unpaused" by the client, the previous data shall be passed as + * if nothing happened. + * + * The `cw-out` writer therefore manages buffers for bytes that could + * not be written. Data that was already in flight from the server also + * needs buffering on paused transfer when it arrives. + * + * In addition, the writer allows buffering of "small" body writes, + * so client functions are called less often. That is only enabled on a + * number of conditions. + * + * HEADER and BODY data may arrive in any order. For paused transfers, + * a list of `struct cw_out_buf` is kept for `cw_out_type` types. The + * list may be: [BODY]->[HEADER]->[BODY]->[HEADER].... + * When unpausing, this list is "played back" to the client callbacks. + * + * The amount of bytes being buffered is limited by `DYN_PAUSE_BUFFER` + * and when that is exceeded `CURLE_TOO_LARGE` is returned as error. + */ +typedef enum { + CW_OUT_NONE, + CW_OUT_BODY, + CW_OUT_HDS +} cw_out_type; + +struct cw_out_buf { + struct cw_out_buf *next; + struct dynbuf b; + cw_out_type type; +}; + +static struct cw_out_buf *cw_out_buf_create(cw_out_type otype) +{ + struct cw_out_buf *cwbuf = calloc(1, sizeof(*cwbuf)); + if(cwbuf) { + cwbuf->type = otype; + Curl_dyn_init(&cwbuf->b, DYN_PAUSE_BUFFER); + } + return cwbuf; +} + +static void cw_out_buf_free(struct cw_out_buf *cwbuf) +{ + if(cwbuf) { + Curl_dyn_free(&cwbuf->b); + free(cwbuf); + } +} + +struct cw_out_ctx { + struct Curl_cwriter super; + struct cw_out_buf *buf; + BIT(paused); + BIT(errored); +}; + +static CURLcode cw_out_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes); +static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer); +static CURLcode cw_out_init(struct Curl_easy *data, + struct Curl_cwriter *writer); + +struct Curl_cwtype Curl_cwt_out = { + "cw-out", + NULL, + cw_out_init, + cw_out_write, + cw_out_close, + sizeof(struct cw_out_ctx) +}; + +static CURLcode cw_out_init(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct cw_out_ctx *ctx = writer->ctx; + (void)data; + ctx->buf = NULL; + return CURLE_OK; +} + +static void cw_out_bufs_free(struct cw_out_ctx *ctx) +{ + while(ctx->buf) { + struct cw_out_buf *next = ctx->buf->next; + cw_out_buf_free(ctx->buf); + ctx->buf = next; + } +} + +static size_t cw_out_bufs_len(struct cw_out_ctx *ctx) +{ + struct cw_out_buf *cwbuf = ctx->buf; + size_t len = 0; + while(cwbuf) { + len += Curl_dyn_len(&cwbuf->b); + cwbuf = cwbuf->next; + } + return len; +} + +static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer) +{ + struct cw_out_ctx *ctx = writer->ctx; + + (void)data; + cw_out_bufs_free(ctx); +} + +/** + * Return the current curl_write_callback and user_data for the buf type + */ +static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype, + curl_write_callback *pwcb, void **pwcb_data, + size_t *pmax_write, size_t *pmin_write) +{ + switch(otype) { + case CW_OUT_BODY: + *pwcb = data->set.fwrite_func; + *pwcb_data = data->set.out; + *pmax_write = CURL_MAX_WRITE_SIZE; + /* if we ever want buffering of BODY output, we can set `min_write` + * the preferred size. The default should always be to pass data + * to the client as it comes without delay */ + *pmin_write = 0; + break; + case CW_OUT_HDS: + *pwcb = data->set.fwrite_header? data->set.fwrite_header : + (data->set.writeheader? data->set.fwrite_func : NULL); + *pwcb_data = data->set.writeheader; + *pmax_write = 0; /* do not chunk-write headers, write them as they are */ + *pmin_write = 0; + break; + default: + *pwcb = NULL; + *pwcb_data = NULL; + *pmax_write = CURL_MAX_WRITE_SIZE; + *pmin_write = 0; + } +} + +static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx, + struct Curl_easy *data, + cw_out_type otype, + bool flush_all, + const char *buf, size_t blen, + size_t *pconsumed) +{ + curl_write_callback wcb; + void *wcb_data; + size_t max_write, min_write; + size_t wlen, nwritten; + + /* If we errored once, we do not invoke the client callback again */ + if(ctx->errored) + return CURLE_WRITE_ERROR; + + /* write callbacks may get NULLed by the client between calls. */ + cw_get_writefunc(data, otype, &wcb, &wcb_data, &max_write, &min_write); + if(!wcb) { + *pconsumed = blen; + return CURLE_OK; + } + + *pconsumed = 0; + while(blen && !ctx->paused) { + if(!flush_all && blen < min_write) + break; + wlen = max_write? CURLMIN(blen, max_write) : blen; + Curl_set_in_callback(data, TRUE); + nwritten = wcb((char *)buf, 1, wlen, wcb_data); + Curl_set_in_callback(data, FALSE); + CURL_TRC_WRITE(data, "cw_out, wrote %zu %s bytes -> %zu", + wlen, (otype == CW_OUT_BODY)? "body" : "header", + nwritten); + if(CURL_WRITEFUNC_PAUSE == nwritten) { + if(data->conn && data->conn->handler->flags & PROTOPT_NONETWORK) { + /* Protocols that work without network cannot be paused. This is + actually only FILE:// just now, and it cannot pause since the + transfer is not done using the "normal" procedure. */ + failf(data, "Write callback asked for PAUSE when not supported"); + return CURLE_WRITE_ERROR; + } + /* mark the connection as RECV paused */ + data->req.keepon |= KEEP_RECV_PAUSE; + ctx->paused = TRUE; + CURL_TRC_WRITE(data, "cw_out, PAUSE requested by client"); + break; + } + else if(CURL_WRITEFUNC_ERROR == nwritten) { + failf(data, "client returned ERROR on write of %zu bytes", wlen); + return CURLE_WRITE_ERROR; + } + else if(nwritten != wlen) { + failf(data, "Failure writing output to destination, " + "passed %zu returned %zd", wlen, nwritten); + return CURLE_WRITE_ERROR; + } + *pconsumed += nwritten; + blen -= nwritten; + buf += nwritten; + } + return CURLE_OK; +} + +static CURLcode cw_out_buf_flush(struct cw_out_ctx *ctx, + struct Curl_easy *data, + struct cw_out_buf *cwbuf, + bool flush_all) +{ + CURLcode result = CURLE_OK; + + if(Curl_dyn_len(&cwbuf->b)) { + size_t consumed; + + result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all, + Curl_dyn_ptr(&cwbuf->b), + Curl_dyn_len(&cwbuf->b), + &consumed); + if(result) + return result; + + if(consumed) { + if(consumed == Curl_dyn_len(&cwbuf->b)) { + Curl_dyn_free(&cwbuf->b); + } + else { + DEBUGASSERT(consumed < Curl_dyn_len(&cwbuf->b)); + result = Curl_dyn_tail(&cwbuf->b, Curl_dyn_len(&cwbuf->b) - consumed); + if(result) + return result; + } + } + } + return result; +} + +static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx, + struct Curl_easy *data, + struct cw_out_buf **pcwbuf, + bool flush_all) +{ + struct cw_out_buf *cwbuf = *pcwbuf; + CURLcode result; + + if(!cwbuf) + return CURLE_OK; + if(ctx->paused) + return CURLE_OK; + + /* write the end of the chain until it blocks or gets empty */ + while(cwbuf->next) { + struct cw_out_buf **plast = &cwbuf->next; + while((*plast)->next) + plast = &(*plast)->next; + result = cw_out_flush_chain(ctx, data, plast, flush_all); + if(result) + return result; + if(*plast) { + /* could not write last, paused again? */ + DEBUGASSERT(ctx->paused); + return CURLE_OK; + } + } + + result = cw_out_buf_flush(ctx, data, cwbuf, flush_all); + if(result) + return result; + if(!Curl_dyn_len(&cwbuf->b)) { + cw_out_buf_free(cwbuf); + *pcwbuf = NULL; + } + return CURLE_OK; +} + +static CURLcode cw_out_append(struct cw_out_ctx *ctx, + cw_out_type otype, + const char *buf, size_t blen) +{ + if(cw_out_bufs_len(ctx) + blen > DYN_PAUSE_BUFFER) + return CURLE_TOO_LARGE; + + /* if we do not have a buffer, or it is of another type, make a new one. + * And for CW_OUT_HDS always make a new one, so we "replay" headers + * exactly as they came in */ + if(!ctx->buf || (ctx->buf->type != otype) || (otype == CW_OUT_HDS)) { + struct cw_out_buf *cwbuf = cw_out_buf_create(otype); + if(!cwbuf) + return CURLE_OUT_OF_MEMORY; + cwbuf->next = ctx->buf; + ctx->buf = cwbuf; + } + DEBUGASSERT(ctx->buf && (ctx->buf->type == otype)); + return Curl_dyn_addn(&ctx->buf->b, buf, blen); +} + +static CURLcode cw_out_do_write(struct cw_out_ctx *ctx, + struct Curl_easy *data, + cw_out_type otype, + bool flush_all, + const char *buf, size_t blen) +{ + CURLcode result = CURLE_OK; + + /* if we have buffered data and it is a different type than what + * we are writing now, try to flush all */ + if(ctx->buf && ctx->buf->type != otype) { + result = cw_out_flush_chain(ctx, data, &ctx->buf, TRUE); + if(result) + goto out; + } + + if(ctx->buf) { + /* still have buffered data, append and flush */ + result = cw_out_append(ctx, otype, buf, blen); + if(result) + return result; + result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); + if(result) + goto out; + } + else { + /* nothing buffered, try direct write */ + size_t consumed; + result = cw_out_ptr_flush(ctx, data, otype, flush_all, + buf, blen, &consumed); + if(result) + return result; + if(consumed < blen) { + /* did not write all, append the rest */ + result = cw_out_append(ctx, otype, buf + consumed, blen - consumed); + if(result) + goto out; + } + } + +out: + if(result) { + /* We do not want to invoked client callbacks a second time after + * encountering an error. See issue #13337 */ + ctx->errored = TRUE; + cw_out_bufs_free(ctx); + } + return result; +} + +static CURLcode cw_out_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t blen) +{ + struct cw_out_ctx *ctx = writer->ctx; + CURLcode result; + bool flush_all; + + flush_all = (type & CLIENTWRITE_EOS)? TRUE:FALSE; + if((type & CLIENTWRITE_BODY) || + ((type & CLIENTWRITE_HEADER) && data->set.include_header)) { + result = cw_out_do_write(ctx, data, CW_OUT_BODY, flush_all, buf, blen); + if(result) + return result; + } + + if(type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) { + result = cw_out_do_write(ctx, data, CW_OUT_HDS, flush_all, buf, blen); + if(result) + return result; + } + + return CURLE_OK; +} + +bool Curl_cw_out_is_paused(struct Curl_easy *data) +{ + struct Curl_cwriter *cw_out; + struct cw_out_ctx *ctx; + + cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out); + if(!cw_out) + return FALSE; + + ctx = (struct cw_out_ctx *)cw_out; + CURL_TRC_WRITE(data, "cw-out is%spaused", ctx->paused? "" : " not"); + return ctx->paused; +} + +static CURLcode cw_out_flush(struct Curl_easy *data, + bool unpause, bool flush_all) +{ + struct Curl_cwriter *cw_out; + CURLcode result = CURLE_OK; + + cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out); + if(cw_out) { + struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out; + if(ctx->errored) + return CURLE_WRITE_ERROR; + if(unpause && ctx->paused) + ctx->paused = FALSE; + if(ctx->paused) + return CURLE_OK; /* not doing it */ + + result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); + if(result) { + ctx->errored = TRUE; + cw_out_bufs_free(ctx); + return result; + } + } + return result; +} + +CURLcode Curl_cw_out_unpause(struct Curl_easy *data) +{ + CURL_TRC_WRITE(data, "cw-out unpause"); + return cw_out_flush(data, TRUE, FALSE); +} + +CURLcode Curl_cw_out_done(struct Curl_easy *data) +{ + CURL_TRC_WRITE(data, "cw-out done"); + return cw_out_flush(data, FALSE, TRUE); +} diff --git a/third_party/curl/lib/cw-out.h b/third_party/curl/lib/cw-out.h new file mode 100644 index 0000000000..ca4c2e435d --- /dev/null +++ b/third_party/curl/lib/cw-out.h @@ -0,0 +1,53 @@ +#ifndef HEADER_CURL_CW_OUT_H +#define HEADER_CURL_CW_OUT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "sendf.h" + +/** + * The client writer type "cw-out" that does the actual writing to + * the client callbacks. Intended to be the last installed in the + * client writer stack of a transfer. + */ +extern struct Curl_cwtype Curl_cwt_out; + +/** + * Return TRUE iff 'cw-out' client write has paused data. + */ +bool Curl_cw_out_is_paused(struct Curl_easy *data); + +/** + * Flush any buffered date to the client, chunk collation still applies. + */ +CURLcode Curl_cw_out_unpause(struct Curl_easy *data); + +/** + * Mark EndOfStream reached and flush ALL data to the client. + */ +CURLcode Curl_cw_out_done(struct Curl_easy *data); + +#endif /* HEADER_CURL_CW_OUT_H */ diff --git a/third_party/curl/lib/dict.c b/third_party/curl/lib/dict.c new file mode 100644 index 0000000000..35331ce224 --- /dev/null +++ b/third_party/curl/lib/dict.c @@ -0,0 +1,321 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_DICT + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#elif defined(HAVE_UNISTD_H) +#include +#endif + +#include "urldata.h" +#include +#include "transfer.h" +#include "sendf.h" +#include "escape.h" +#include "progress.h" +#include "dict.h" +#include "curl_printf.h" +#include "strcase.h" +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* + * Forward declarations. + */ + +static CURLcode dict_do(struct Curl_easy *data, bool *done); + +/* + * DICT protocol handler. + */ + +const struct Curl_handler Curl_handler_dict = { + "dict", /* scheme */ + ZERO_NULL, /* setup_connection */ + dict_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_DICT, /* defport */ + CURLPROTO_DICT, /* protocol */ + CURLPROTO_DICT, /* family */ + PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ +}; + +#define DYN_DICT_WORD 10000 +static char *unescape_word(const char *input) +{ + struct dynbuf out; + const char *ptr; + CURLcode result = CURLE_OK; + Curl_dyn_init(&out, DYN_DICT_WORD); + + /* According to RFC2229 section 2.2, these letters need to be escaped with + \[letter] */ + for(ptr = input; *ptr; ptr++) { + char ch = *ptr; + if((ch <= 32) || (ch == 127) || + (ch == '\'') || (ch == '\"') || (ch == '\\')) + result = Curl_dyn_addn(&out, "\\", 1); + if(!result) + result = Curl_dyn_addn(&out, ptr, 1); + if(result) + return NULL; + } + return Curl_dyn_ptr(&out); +} + +/* sendf() sends formatted data to the server */ +static CURLcode sendf(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); + +static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...) +{ + size_t bytes_written; + size_t write_len; + CURLcode result = CURLE_OK; + char *s; + char *sptr; + va_list ap; + va_start(ap, fmt); + s = vaprintf(fmt, ap); /* returns an allocated string */ + va_end(ap); + if(!s) + return CURLE_OUT_OF_MEMORY; /* failure */ + + bytes_written = 0; + write_len = strlen(s); + sptr = s; + + for(;;) { + /* Write the buffer to the socket */ + result = Curl_xfer_send(data, sptr, write_len, &bytes_written); + + if(result) + break; + + Curl_debug(data, CURLINFO_DATA_OUT, sptr, (size_t)bytes_written); + + if((size_t)bytes_written != write_len) { + /* if not all was written at once, we must advance the pointer, decrease + the size left and try again! */ + write_len -= bytes_written; + sptr += bytes_written; + } + else + break; + } + + free(s); /* free the output string */ + + return result; +} + +static CURLcode dict_do(struct Curl_easy *data, bool *done) +{ + char *word; + char *eword = NULL; + char *ppath; + char *database = NULL; + char *strategy = NULL; + char *nthdef = NULL; /* This is not part of the protocol, but required + by RFC 2229 */ + CURLcode result; + + char *path; + + *done = TRUE; /* unconditionally */ + + /* url-decode path before further evaluation */ + result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); + if(result) + return result; + + if(strncasecompare(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || + strncasecompare(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || + strncasecompare(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { + + word = strchr(path, ':'); + if(word) { + word++; + database = strchr(word, ':'); + if(database) { + *database++ = (char)0; + strategy = strchr(database, ':'); + if(strategy) { + *strategy++ = (char)0; + nthdef = strchr(strategy, ':'); + if(nthdef) { + *nthdef = (char)0; + } + } + } + } + + if(!word || (*word == (char)0)) { + infof(data, "lookup word is missing"); + word = (char *)"default"; + } + if(!database || (*database == (char)0)) { + database = (char *)"!"; + } + if(!strategy || (*strategy == (char)0)) { + strategy = (char *)"."; + } + + eword = unescape_word(word); + if(!eword) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + result = sendf(data, + "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" + "MATCH " + "%s " /* database */ + "%s " /* strategy */ + "%s\r\n" /* word */ + "QUIT\r\n", + database, + strategy, + eword); + + if(result) { + failf(data, "Failed sending DICT request"); + goto error; + } + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); /* no upload */ + } + else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) || + strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) || + strncasecompare(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) { + + word = strchr(path, ':'); + if(word) { + word++; + database = strchr(word, ':'); + if(database) { + *database++ = (char)0; + nthdef = strchr(database, ':'); + if(nthdef) { + *nthdef = (char)0; + } + } + } + + if(!word || (*word == (char)0)) { + infof(data, "lookup word is missing"); + word = (char *)"default"; + } + if(!database || (*database == (char)0)) { + database = (char *)"!"; + } + + eword = unescape_word(word); + if(!eword) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + result = sendf(data, + "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" + "DEFINE " + "%s " /* database */ + "%s\r\n" /* word */ + "QUIT\r\n", + database, + eword); + + if(result) { + failf(data, "Failed sending DICT request"); + goto error; + } + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); + } + else { + + ppath = strchr(path, '/'); + if(ppath) { + int i; + + ppath++; + for(i = 0; ppath[i]; i++) { + if(ppath[i] == ':') + ppath[i] = ' '; + } + result = sendf(data, + "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" + "%s\r\n" + "QUIT\r\n", ppath); + if(result) { + failf(data, "Failed sending DICT request"); + goto error; + } + + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); + } + } + +error: + free(eword); + free(path); + return result; +} +#endif /* CURL_DISABLE_DICT */ diff --git a/third_party/curl/lib/dict.h b/third_party/curl/lib/dict.h new file mode 100644 index 0000000000..ba9a92719b --- /dev/null +++ b/third_party/curl/lib/dict.h @@ -0,0 +1,31 @@ +#ifndef HEADER_CURL_DICT_H +#define HEADER_CURL_DICT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#ifndef CURL_DISABLE_DICT +extern const struct Curl_handler Curl_handler_dict; +#endif + +#endif /* HEADER_CURL_DICT_H */ diff --git a/third_party/curl/lib/dllmain.c b/third_party/curl/lib/dllmain.c new file mode 100644 index 0000000000..41e97b37eb --- /dev/null +++ b/third_party/curl/lib/dllmain.c @@ -0,0 +1,81 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_OPENSSL +#include +#endif + +/* The fourth-to-last include */ +#ifdef __CYGWIN__ +#define WIN32_LEAN_AND_MEAN +#include +#ifdef _WIN32 +#undef _WIN32 +#endif +#endif + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* DllMain() must only be defined for Windows and Cygwin DLL builds. */ +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(CURL_STATICLIB) + +#if defined(USE_OPENSSL) && \ + !defined(OPENSSL_IS_AWSLC) && \ + !defined(OPENSSL_IS_BORINGSSL) && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + (OPENSSL_VERSION_NUMBER >= 0x10100000L) +#define PREVENT_OPENSSL_MEMLEAK +#endif + +#ifdef PREVENT_OPENSSL_MEMLEAK +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + (void)hinstDLL; + (void)lpvReserved; + + switch(fdwReason) { + case DLL_PROCESS_ATTACH: + break; + case DLL_PROCESS_DETACH: + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + /* Call OPENSSL_thread_stop to prevent a memory leak in case OpenSSL is + linked statically. + https://github.com/curl/curl/issues/12327#issuecomment-1826405944 */ + OPENSSL_thread_stop(); + break; + } + return TRUE; +} +#endif /* OpenSSL */ + +#endif /* DLL build */ diff --git a/third_party/curl/lib/doh.c b/third_party/curl/lib/doh.c new file mode 100644 index 0000000000..0dcca17ccd --- /dev/null +++ b/third_party/curl/lib/doh.c @@ -0,0 +1,1436 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_DOH + +#include "urldata.h" +#include "curl_addrinfo.h" +#include "doh.h" + +#include "sendf.h" +#include "multiif.h" +#include "url.h" +#include "share.h" +#include "curl_base64.h" +#include "connect.h" +#include "strdup.h" +#include "dynbuf.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" +#include "escape.h" + +#define DNS_CLASS_IN 0x01 + +/* local_print_buf truncates if the hex string will be more than this */ +#define LOCAL_PB_HEXMAX 400 + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static const char * const errors[]={ + "", + "Bad label", + "Out of range", + "Label loop", + "Too small", + "Out of memory", + "RDATA length", + "Malformat", + "Bad RCODE", + "Unexpected TYPE", + "Unexpected CLASS", + "No content", + "Bad ID", + "Name too long" +}; + +static const char *doh_strerror(DOHcode code) +{ + if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG)) + return errors[code]; + return "bad error code"; +} + +struct curl_trc_feat Curl_doh_trc = { + "DoH", + CURL_LOG_LVL_NONE, +}; +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + +/* @unittest 1655 + */ +UNITTEST DOHcode doh_encode(const char *host, + DNStype dnstype, + unsigned char *dnsp, /* buffer */ + size_t len, /* buffer size */ + size_t *olen) /* output length */ +{ + const size_t hostlen = strlen(host); + unsigned char *orig = dnsp; + const char *hostp = host; + + /* The expected output length is 16 bytes more than the length of + * the QNAME-encoding of the hostname. + * + * A valid DNS name may not contain a zero-length label, except at + * the end. For this reason, a name beginning with a dot, or + * containing a sequence of two or more consecutive dots, is invalid + * and cannot be encoded as a QNAME. + * + * If the hostname ends with a trailing dot, the corresponding + * QNAME-encoding is one byte longer than the hostname. If (as is + * also valid) the hostname is shortened by the omission of the + * trailing dot, then its QNAME-encoding will be two bytes longer + * than the hostname. + * + * Each [ label, dot ] pair is encoded as [ length, label ], + * preserving overall length. A final [ label ] without a dot is + * also encoded as [ length, label ], increasing overall length + * by one. The encoding is completed by appending a zero byte, + * representing the zero-length root label, again increasing + * the overall length by one. + */ + + size_t expected_len; + DEBUGASSERT(hostlen); + expected_len = 12 + 1 + hostlen + 4; + if(host[hostlen-1]!='.') + expected_len++; + + if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */ + return DOH_DNS_NAME_TOO_LONG; + + if(len < expected_len) + return DOH_TOO_SMALL_BUFFER; + + *dnsp++ = 0; /* 16 bit id */ + *dnsp++ = 0; + *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */ + *dnsp++ = '\0'; /* |RA| Z | RCODE | */ + *dnsp++ = '\0'; + *dnsp++ = 1; /* QDCOUNT (number of entries in the question section) */ + *dnsp++ = '\0'; + *dnsp++ = '\0'; /* ANCOUNT */ + *dnsp++ = '\0'; + *dnsp++ = '\0'; /* NSCOUNT */ + *dnsp++ = '\0'; + *dnsp++ = '\0'; /* ARCOUNT */ + + /* encode each label and store it in the QNAME */ + while(*hostp) { + size_t labellen; + char *dot = strchr(hostp, '.'); + if(dot) + labellen = dot - hostp; + else + labellen = strlen(hostp); + if((labellen > 63) || (!labellen)) { + /* label is too long or too short, error out */ + *olen = 0; + return DOH_DNS_BAD_LABEL; + } + /* label is non-empty, process it */ + *dnsp++ = (unsigned char)labellen; + memcpy(dnsp, hostp, labellen); + dnsp += labellen; + hostp += labellen; + /* advance past dot, but only if there is one */ + if(dot) + hostp++; + } /* next label */ + + *dnsp++ = 0; /* append zero-length label for root */ + + /* There are assigned TYPE codes beyond 255: use range [1..65535] */ + *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */ + *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */ + + *dnsp++ = '\0'; /* upper 8 bit CLASS */ + *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */ + + *olen = dnsp - orig; + + /* verify that our estimation of length is valid, since + * this has led to buffer overflows in this function */ + DEBUGASSERT(*olen == expected_len); + return DOH_OK; +} + +static size_t +doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp) +{ + size_t realsize = size * nmemb; + struct dynbuf *mem = (struct dynbuf *)userp; + + if(Curl_dyn_addn(mem, contents, realsize)) + return 0; + + return realsize; +} + +#if defined(USE_HTTPSRR) && defined(DEBUGBUILD) +static void local_print_buf(struct Curl_easy *data, + const char *prefix, + unsigned char *buf, size_t len) +{ + unsigned char hexstr[LOCAL_PB_HEXMAX]; + size_t hlen = LOCAL_PB_HEXMAX; + bool truncated = false; + + if(len > (LOCAL_PB_HEXMAX / 2)) + truncated = true; + Curl_hexencode(buf, len, hexstr, hlen); + if(!truncated) + infof(data, "%s: len=%d, val=%s", prefix, (int)len, hexstr); + else + infof(data, "%s: len=%d (truncated)val=%s", prefix, (int)len, hexstr); + return; +} +#endif + +/* called from multi.c when this DoH transfer is complete */ +static int doh_done(struct Curl_easy *doh, CURLcode result) +{ + struct Curl_easy *data = doh->set.dohfor; + struct dohdata *dohp = data->req.doh; + /* so one of the DoH request done for the 'data' transfer is now complete! */ + dohp->pending--; + infof(doh, "a DoH request is completed, %u to go", dohp->pending); + if(result) + infof(doh, "DoH request %s", curl_easy_strerror(result)); + + if(!dohp->pending) { + /* DoH completed */ + curl_slist_free_all(dohp->headers); + dohp->headers = NULL; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + return 0; +} + +#define ERROR_CHECK_SETOPT(x,y) \ +do { \ + result = curl_easy_setopt(doh, x, y); \ + if(result && \ + result != CURLE_NOT_BUILT_IN && \ + result != CURLE_UNKNOWN_OPTION) \ + goto error; \ +} while(0) + +static CURLcode dohprobe(struct Curl_easy *data, + struct dnsprobe *p, DNStype dnstype, + const char *host, + const char *url, CURLM *multi, + struct curl_slist *headers) +{ + struct Curl_easy *doh = NULL; + CURLcode result = CURLE_OK; + timediff_t timeout_ms; + DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer), + &p->dohlen); + if(d) { + failf(data, "Failed to encode DoH packet [%d]", d); + return CURLE_OUT_OF_MEMORY; + } + + p->dnstype = dnstype; + Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE); + + timeout_ms = Curl_timeleft(data, NULL, TRUE); + if(timeout_ms <= 0) { + result = CURLE_OPERATION_TIMEDOUT; + goto error; + } + /* Curl_open() is the internal version of curl_easy_init() */ + result = Curl_open(&doh); + if(!result) { + /* pass in the struct pointer via a local variable to please coverity and + the gcc typecheck helpers */ + struct dynbuf *resp = &p->serverdoh; + doh->state.internal = true; +#ifndef CURL_DISABLE_VERBOSE_STRINGS + doh->state.feat = &Curl_doh_trc; +#endif + ERROR_CHECK_SETOPT(CURLOPT_URL, url); + ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https"); + ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb); + ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp); + ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer); + ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen); + ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); +#ifdef USE_HTTP2 + ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); + ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L); +#endif +#ifndef DEBUGBUILD + /* enforce HTTPS if not debug */ + ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); +#else + /* in debug mode, also allow http */ + ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); +#endif + ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms); + ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share); + if(data->set.err && data->set.err != stderr) + ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err); + if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) + ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); + if(data->set.no_signal) + ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); + + ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, + data->set.doh_verifyhost ? 2L : 0L); + ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, + data->set.doh_verifypeer ? 1L : 0L); + ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, + data->set.doh_verifystatus ? 1L : 0L); + + /* Inherit *some* SSL options from the user's transfer. This is a + best-guess as to which options are needed for compatibility. #3661 + + Note DoH does not inherit the user's proxy server so proxy SSL settings + have no effect and are not inherited. If that changes then two new + options should be added to check doh proxy insecure separately, + CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER. + */ + if(data->set.ssl.falsestart) + ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L); + if(data->set.str[STRING_SSL_CAFILE]) { + ERROR_CHECK_SETOPT(CURLOPT_CAINFO, + data->set.str[STRING_SSL_CAFILE]); + } + if(data->set.blobs[BLOB_CAINFO]) { + ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, + data->set.blobs[BLOB_CAINFO]); + } + if(data->set.str[STRING_SSL_CAPATH]) { + ERROR_CHECK_SETOPT(CURLOPT_CAPATH, + data->set.str[STRING_SSL_CAPATH]); + } + if(data->set.str[STRING_SSL_CRLFILE]) { + ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, + data->set.str[STRING_SSL_CRLFILE]); + } + if(data->set.ssl.certinfo) + ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); + if(data->set.ssl.fsslctx) + ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx); + if(data->set.ssl.fsslctxp) + ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp); + if(data->set.fdebug) + ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug); + if(data->set.debugdata) + ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata); + if(data->set.str[STRING_SSL_EC_CURVES]) { + ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES, + data->set.str[STRING_SSL_EC_CURVES]); + } + + { + long mask = + (data->set.ssl.enable_beast ? + CURLSSLOPT_ALLOW_BEAST : 0) | + (data->set.ssl.no_revoke ? + CURLSSLOPT_NO_REVOKE : 0) | + (data->set.ssl.no_partialchain ? + CURLSSLOPT_NO_PARTIALCHAIN : 0) | + (data->set.ssl.revoke_best_effort ? + CURLSSLOPT_REVOKE_BEST_EFFORT : 0) | + (data->set.ssl.native_ca_store ? + CURLSSLOPT_NATIVE_CA : 0) | + (data->set.ssl.auto_client_cert ? + CURLSSLOPT_AUTO_CLIENT_CERT : 0); + + (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask); + } + + doh->set.fmultidone = doh_done; + doh->set.dohfor = data; /* identify for which transfer this is done */ + p->easy = doh; + + /* DoH handles must not inherit private_data. The handles may be passed to + the user via callbacks and the user will be able to identify them as + internal handles because private data is not set. The user can then set + private_data via CURLOPT_PRIVATE if they so choose. */ + DEBUGASSERT(!doh->set.private_data); + + if(curl_multi_add_handle(multi, doh)) + goto error; + } + else + goto error; + return CURLE_OK; + +error: + Curl_close(&doh); + return result; +} + +/* + * Curl_doh() resolves a name using DoH. It resolves a name and returns a + * 'Curl_addrinfo *' with the address information. + */ + +struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, + const char *hostname, + int port, + int *waitp) +{ + CURLcode result = CURLE_OK; + struct dohdata *dohp; + struct connectdata *conn = data->conn; +#ifdef USE_HTTPSRR + /* for now, this is only used when ECH is enabled */ +# ifdef USE_ECH + char *qname = NULL; +# endif +#endif + *waitp = FALSE; + (void)hostname; + (void)port; + + DEBUGASSERT(!data->req.doh); + DEBUGASSERT(conn); + + /* start clean, consider allocating this struct on demand */ + dohp = data->req.doh = calloc(1, sizeof(struct dohdata)); + if(!dohp) + return NULL; + + conn->bits.doh = TRUE; + dohp->host = hostname; + dohp->port = port; + dohp->headers = + curl_slist_append(NULL, + "Content-Type: application/dns-message"); + if(!dohp->headers) + goto error; + + /* create IPv4 DoH request */ + result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4], + DNS_TYPE_A, hostname, data->set.str[STRING_DOH], + data->multi, dohp->headers); + if(result) + goto error; + dohp->pending++; + +#ifdef USE_IPV6 + if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { + /* create IPv6 DoH request */ + result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6], + DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH], + data->multi, dohp->headers); + if(result) + goto error; + dohp->pending++; + } +#endif + +#ifdef USE_HTTPSRR + /* + * TODO: Figure out the conditions under which we want to make + * a request for an HTTPS RR when we are not doing ECH. For now, + * making this request breaks a bunch of DoH tests, e.g. test2100, + * where the additional request does not match the pre-cooked data + * files, so there is a bit of work attached to making the request + * in a non-ECH use-case. For the present, we will only make the + * request when ECH is enabled in the build and is being used for + * the curl operation. + */ +# ifdef USE_ECH + if(data->set.tls_ech & CURLECH_ENABLE + || data->set.tls_ech & CURLECH_HARD) { + if(port == 443) + qname = strdup(hostname); + else + qname = aprintf("_%d._https.%s", port, hostname); + if(!qname) + goto error; + result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_HTTPS], + DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH], + data->multi, dohp->headers); + Curl_safefree(qname); + if(result) + goto error; + dohp->pending++; + } +# endif +#endif + *waitp = TRUE; /* this never returns synchronously */ + return NULL; + +error: + Curl_doh_cleanup(data); + return NULL; +} + +static DOHcode skipqname(const unsigned char *doh, size_t dohlen, + unsigned int *indexp) +{ + unsigned char length; + do { + if(dohlen < (*indexp + 1)) + return DOH_DNS_OUT_OF_RANGE; + length = doh[*indexp]; + if((length & 0xc0) == 0xc0) { + /* name pointer, advance over it and be done */ + if(dohlen < (*indexp + 2)) + return DOH_DNS_OUT_OF_RANGE; + *indexp += 2; + break; + } + if(length & 0xc0) + return DOH_DNS_BAD_LABEL; + if(dohlen < (*indexp + 1 + length)) + return DOH_DNS_OUT_OF_RANGE; + *indexp += (unsigned int)(1 + length); + } while(length); + return DOH_OK; +} + +static unsigned short get16bit(const unsigned char *doh, unsigned int index) +{ + return (unsigned short)((doh[index] << 8) | doh[index + 1]); +} + +static unsigned int get32bit(const unsigned char *doh, unsigned int index) +{ + /* make clang and gcc optimize this to bswap by incrementing + the pointer first. */ + doh += index; + + /* avoid undefined behavior by casting to unsigned before shifting + 24 bits, possibly into the sign bit. codegen is same, but + ub sanitizer will not be upset */ + return ((unsigned)doh[0] << 24) | ((unsigned)doh[1] << 16) | + ((unsigned)doh[2] << 8) | doh[3]; +} + +static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d) +{ + /* silently ignore addresses over the limit */ + if(d->numaddr < DOH_MAX_ADDR) { + struct dohaddr *a = &d->addr[d->numaddr]; + a->type = DNS_TYPE_A; + memcpy(&a->ip.v4, &doh[index], 4); + d->numaddr++; + } + return DOH_OK; +} + +static DOHcode store_aaaa(const unsigned char *doh, + int index, + struct dohentry *d) +{ + /* silently ignore addresses over the limit */ + if(d->numaddr < DOH_MAX_ADDR) { + struct dohaddr *a = &d->addr[d->numaddr]; + a->type = DNS_TYPE_AAAA; + memcpy(&a->ip.v6, &doh[index], 16); + d->numaddr++; + } + return DOH_OK; +} + +#ifdef USE_HTTPSRR +static DOHcode store_https(const unsigned char *doh, + int index, + struct dohentry *d, + uint16_t len) +{ + /* silently ignore RRs over the limit */ + if(d->numhttps_rrs < DOH_MAX_HTTPS) { + struct dohhttps_rr *h = &d->https_rrs[d->numhttps_rrs]; + h->val = Curl_memdup(&doh[index], len); + if(!h->val) + return DOH_OUT_OF_MEM; + h->len = len; + d->numhttps_rrs++; + } + return DOH_OK; +} +#endif + +static DOHcode store_cname(const unsigned char *doh, + size_t dohlen, + unsigned int index, + struct dohentry *d) +{ + struct dynbuf *c; + unsigned int loop = 128; /* a valid DNS name can never loop this much */ + unsigned char length; + + if(d->numcname == DOH_MAX_CNAME) + return DOH_OK; /* skip! */ + + c = &d->cname[d->numcname++]; + do { + if(index >= dohlen) + return DOH_DNS_OUT_OF_RANGE; + length = doh[index]; + if((length & 0xc0) == 0xc0) { + int newpos; + /* name pointer, get the new offset (14 bits) */ + if((index + 1) >= dohlen) + return DOH_DNS_OUT_OF_RANGE; + + /* move to the new index */ + newpos = (length & 0x3f) << 8 | doh[index + 1]; + index = (unsigned int)newpos; + continue; + } + else if(length & 0xc0) + return DOH_DNS_BAD_LABEL; /* bad input */ + else + index++; + + if(length) { + if(Curl_dyn_len(c)) { + if(Curl_dyn_addn(c, STRCONST("."))) + return DOH_OUT_OF_MEM; + } + if((index + length) > dohlen) + return DOH_DNS_BAD_LABEL; + + if(Curl_dyn_addn(c, &doh[index], length)) + return DOH_OUT_OF_MEM; + index += length; + } + } while(length && --loop); + + if(!loop) + return DOH_DNS_LABEL_LOOP; + return DOH_OK; +} + +static DOHcode rdata(const unsigned char *doh, + size_t dohlen, + unsigned short rdlength, + unsigned short type, + int index, + struct dohentry *d) +{ + /* RDATA + - A (TYPE 1): 4 bytes + - AAAA (TYPE 28): 16 bytes + - NS (TYPE 2): N bytes + - HTTPS (TYPE 65): N bytes */ + DOHcode rc; + + switch(type) { + case DNS_TYPE_A: + if(rdlength != 4) + return DOH_DNS_RDATA_LEN; + rc = store_a(doh, index, d); + if(rc) + return rc; + break; + case DNS_TYPE_AAAA: + if(rdlength != 16) + return DOH_DNS_RDATA_LEN; + rc = store_aaaa(doh, index, d); + if(rc) + return rc; + break; +#ifdef USE_HTTPSRR + case DNS_TYPE_HTTPS: + rc = store_https(doh, index, d, rdlength); + if(rc) + return rc; + break; +#endif + case DNS_TYPE_CNAME: + rc = store_cname(doh, dohlen, (unsigned int)index, d); + if(rc) + return rc; + break; + case DNS_TYPE_DNAME: + /* explicit for clarity; just skip; rely on synthesized CNAME */ + break; + default: + /* unsupported type, just skip it */ + break; + } + return DOH_OK; +} + +UNITTEST void de_init(struct dohentry *de) +{ + int i; + memset(de, 0, sizeof(*de)); + de->ttl = INT_MAX; + for(i = 0; i < DOH_MAX_CNAME; i++) + Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME); +} + + +UNITTEST DOHcode doh_decode(const unsigned char *doh, + size_t dohlen, + DNStype dnstype, + struct dohentry *d) +{ + unsigned char rcode; + unsigned short qdcount; + unsigned short ancount; + unsigned short type = 0; + unsigned short rdlength; + unsigned short nscount; + unsigned short arcount; + unsigned int index = 12; + DOHcode rc; + + if(dohlen < 12) + return DOH_TOO_SMALL_BUFFER; /* too small */ + if(!doh || doh[0] || doh[1]) + return DOH_DNS_BAD_ID; /* bad ID */ + rcode = doh[3] & 0x0f; + if(rcode) + return DOH_DNS_BAD_RCODE; /* bad rcode */ + + qdcount = get16bit(doh, 4); + while(qdcount) { + rc = skipqname(doh, dohlen, &index); + if(rc) + return rc; /* bad qname */ + if(dohlen < (index + 4)) + return DOH_DNS_OUT_OF_RANGE; + index += 4; /* skip question's type and class */ + qdcount--; + } + + ancount = get16bit(doh, 6); + while(ancount) { + unsigned short class; + unsigned int ttl; + + rc = skipqname(doh, dohlen, &index); + if(rc) + return rc; /* bad qname */ + + if(dohlen < (index + 2)) + return DOH_DNS_OUT_OF_RANGE; + + type = get16bit(doh, index); + if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */ + && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */ + && (type != dnstype)) + /* Not the same type as was asked for nor CNAME nor DNAME */ + return DOH_DNS_UNEXPECTED_TYPE; + index += 2; + + if(dohlen < (index + 2)) + return DOH_DNS_OUT_OF_RANGE; + class = get16bit(doh, index); + if(DNS_CLASS_IN != class) + return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */ + index += 2; + + if(dohlen < (index + 4)) + return DOH_DNS_OUT_OF_RANGE; + + ttl = get32bit(doh, index); + if(ttl < d->ttl) + d->ttl = ttl; + index += 4; + + if(dohlen < (index + 2)) + return DOH_DNS_OUT_OF_RANGE; + + rdlength = get16bit(doh, index); + index += 2; + if(dohlen < (index + rdlength)) + return DOH_DNS_OUT_OF_RANGE; + + rc = rdata(doh, dohlen, rdlength, type, (int)index, d); + if(rc) + return rc; /* bad rdata */ + index += rdlength; + ancount--; + } + + nscount = get16bit(doh, 8); + while(nscount) { + rc = skipqname(doh, dohlen, &index); + if(rc) + return rc; /* bad qname */ + + if(dohlen < (index + 8)) + return DOH_DNS_OUT_OF_RANGE; + + index += 2 + 2 + 4; /* type, class and ttl */ + + if(dohlen < (index + 2)) + return DOH_DNS_OUT_OF_RANGE; + + rdlength = get16bit(doh, index); + index += 2; + if(dohlen < (index + rdlength)) + return DOH_DNS_OUT_OF_RANGE; + index += rdlength; + nscount--; + } + + arcount = get16bit(doh, 10); + while(arcount) { + rc = skipqname(doh, dohlen, &index); + if(rc) + return rc; /* bad qname */ + + if(dohlen < (index + 8)) + return DOH_DNS_OUT_OF_RANGE; + + index += 2 + 2 + 4; /* type, class and ttl */ + + if(dohlen < (index + 2)) + return DOH_DNS_OUT_OF_RANGE; + + rdlength = get16bit(doh, index); + index += 2; + if(dohlen < (index + rdlength)) + return DOH_DNS_OUT_OF_RANGE; + index += rdlength; + arcount--; + } + + if(index != dohlen) + return DOH_DNS_MALFORMAT; /* something is wrong */ + +#ifdef USE_HTTTPS + if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs) +#else + if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr) +#endif + /* nothing stored! */ + return DOH_NO_CONTENT; + + return DOH_OK; /* ok */ +} + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void showdoh(struct Curl_easy *data, + const struct dohentry *d) +{ + int i; + infof(data, "[DoH] TTL: %u seconds", d->ttl); + for(i = 0; i < d->numaddr; i++) { + const struct dohaddr *a = &d->addr[i]; + if(a->type == DNS_TYPE_A) { + infof(data, "[DoH] A: %u.%u.%u.%u", + a->ip.v4[0], a->ip.v4[1], + a->ip.v4[2], a->ip.v4[3]); + } + else if(a->type == DNS_TYPE_AAAA) { + int j; + char buffer[128]; + char *ptr; + size_t len; + len = msnprintf(buffer, 128, "[DoH] AAAA: "); + ptr = &buffer[len]; + len = sizeof(buffer) - len; + for(j = 0; j < 16; j += 2) { + size_t l; + msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j], + d->addr[i].ip.v6[j + 1]); + l = strlen(ptr); + len -= l; + ptr += l; + } + infof(data, "%s", buffer); + } + } +#ifdef USE_HTTPSRR + for(i = 0; i < d->numhttps_rrs; i++) { +# ifdef DEBUGBUILD + local_print_buf(data, "DoH HTTPS", + d->https_rrs[i].val, d->https_rrs[i].len); +# else + infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len); +# endif + } +#endif + for(i = 0; i < d->numcname; i++) { + infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i])); + } +} +#else +#define showdoh(x,y) +#endif + +/* + * doh2ai() + * + * This function returns a pointer to the first element of a newly allocated + * Curl_addrinfo struct linked list filled with the data from a set of DoH + * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for + * a IPv6 stack, but usable also for IPv4, all hosts and environments. + * + * The memory allocated by this function *MUST* be free'd later on calling + * Curl_freeaddrinfo(). For each successful call to this function there + * must be an associated call later to Curl_freeaddrinfo(). + */ + +static CURLcode doh2ai(const struct dohentry *de, const char *hostname, + int port, struct Curl_addrinfo **aip) +{ + struct Curl_addrinfo *ai; + struct Curl_addrinfo *prevai = NULL; + struct Curl_addrinfo *firstai = NULL; + struct sockaddr_in *addr; +#ifdef USE_IPV6 + struct sockaddr_in6 *addr6; +#endif + CURLcode result = CURLE_OK; + int i; + size_t hostlen = strlen(hostname) + 1; /* include null-terminator */ + + DEBUGASSERT(de); + + if(!de->numaddr) + return CURLE_COULDNT_RESOLVE_HOST; + + for(i = 0; i < de->numaddr; i++) { + size_t ss_size; + CURL_SA_FAMILY_T addrtype; + if(de->addr[i].type == DNS_TYPE_AAAA) { +#ifndef USE_IPV6 + /* we cannot handle IPv6 addresses */ + continue; +#else + ss_size = sizeof(struct sockaddr_in6); + addrtype = AF_INET6; +#endif + } + else { + ss_size = sizeof(struct sockaddr_in); + addrtype = AF_INET; + } + + ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen); + if(!ai) { + result = CURLE_OUT_OF_MEMORY; + break; + } + ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); + ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size); + memcpy(ai->ai_canonname, hostname, hostlen); + + if(!firstai) + /* store the pointer we want to return from this function */ + firstai = ai; + + if(prevai) + /* make the previous entry point to this */ + prevai->ai_next = ai; + + ai->ai_family = addrtype; + + /* we return all names as STREAM, so when using this address for TFTP + the type must be ignored and conn->socktype be used instead! */ + ai->ai_socktype = SOCK_STREAM; + + ai->ai_addrlen = (curl_socklen_t)ss_size; + + /* leave the rest of the struct filled with zero */ + + switch(ai->ai_family) { + case AF_INET: + addr = (void *)ai->ai_addr; /* storage area for this info */ + DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4)); + memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr)); +#ifdef __MINGW32__ + addr->sin_family = (short)addrtype; +#else + addr->sin_family = addrtype; +#endif + addr->sin_port = htons((unsigned short)port); + break; + +#ifdef USE_IPV6 + case AF_INET6: + addr6 = (void *)ai->ai_addr; /* storage area for this info */ + DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); + memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr)); +#ifdef __MINGW32__ + addr6->sin6_family = (short)addrtype; +#else + addr6->sin6_family = addrtype; +#endif + addr6->sin6_port = htons((unsigned short)port); + break; +#endif + } + + prevai = ai; + } + + if(result) { + Curl_freeaddrinfo(firstai); + firstai = NULL; + } + *aip = firstai; + + return result; +} + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static const char *type2name(DNStype dnstype) +{ + switch(dnstype) { + case DNS_TYPE_A: + return "A"; + case DNS_TYPE_AAAA: + return "AAAA"; +#ifdef USE_HTTPSRR + case DNS_TYPE_HTTPS: + return "HTTPS"; +#endif + default: + return "unknown"; + } +} +#endif + +UNITTEST void de_cleanup(struct dohentry *d) +{ + int i = 0; + for(i = 0; i < d->numcname; i++) { + Curl_dyn_free(&d->cname[i]); + } +#ifdef USE_HTTPSRR + for(i = 0; i < d->numhttps_rrs; i++) + Curl_safefree(d->https_rrs[i].val); +#endif +} + +#ifdef USE_HTTPSRR + +/* + * @brief decode the DNS name in a binary RRData + * @param buf points to the buffer (in/out) + * @param remaining points to the remaining buffer length (in/out) + * @param dnsname returns the string form name on success + * @return is 1 for success, error otherwise + * + * The encoding here is defined in + * https://tools.ietf.org/html/rfc1035#section-3.1 + * + * The input buffer pointer will be modified so it points to + * just after the end of the DNS name encoding on output. (And + * that is why it is an "unsigned char **" :-) + */ +static CURLcode local_decode_rdata_name(unsigned char **buf, size_t *remaining, + char **dnsname) +{ + unsigned char *cp = NULL; + int rem = 0; + unsigned char clen = 0; /* chunk len */ + struct dynbuf thename; + + DEBUGASSERT(buf && remaining && dnsname); + if(!buf || !remaining || !dnsname) + return CURLE_OUT_OF_MEMORY; + rem = (int)*remaining; + if(rem <= 0) { + Curl_dyn_free(&thename); + return CURLE_OUT_OF_MEMORY; + } + Curl_dyn_init(&thename, CURL_MAXLEN_host_name); + cp = *buf; + clen = *cp++; + if(clen == 0) { + /* special case - return "." as name */ + if(Curl_dyn_addn(&thename, ".", 1)) + return CURLE_OUT_OF_MEMORY; + } + while(clen) { + if(clen >= rem) { + Curl_dyn_free(&thename); + return CURLE_OUT_OF_MEMORY; + } + if(Curl_dyn_addn(&thename, cp, clen) || + Curl_dyn_addn(&thename, ".", 1)) + return CURLE_TOO_LARGE; + + cp += clen; + rem -= (clen + 1); + if(rem <= 0) { + Curl_dyn_free(&thename); + return CURLE_OUT_OF_MEMORY; + } + clen = *cp++; + } + *buf = cp; + *remaining = rem - 1; + *dnsname = Curl_dyn_ptr(&thename); + return CURLE_OK; +} + +static CURLcode local_decode_rdata_alpn(unsigned char *rrval, size_t len, + char **alpns) +{ + /* + * spec here is as per draft-ietf-dnsop-svcb-https, section-7.1.1 + * encoding is catenated list of strings each preceded by a one + * octet length + * output is comma-sep list of the strings + * implementations may or may not handle quoting of comma within + * string values, so we might see a comma within the wire format + * version of a string, in which case we will precede that by a + * backslash - same goes for a backslash character, and of course + * we need to use two backslashes in strings when we mean one;-) + */ + int remaining = (int) len; + char *oval; + size_t i; + unsigned char *cp = rrval; + struct dynbuf dval; + + if(!alpns) + return CURLE_OUT_OF_MEMORY; + Curl_dyn_init(&dval, DYN_DOH_RESPONSE); + remaining = (int)len; + cp = rrval; + while(remaining > 0) { + size_t tlen = (size_t) *cp++; + + /* if not 1st time, add comma */ + if(remaining != (int)len && Curl_dyn_addn(&dval, ",", 1)) + goto err; + remaining--; + if(tlen > (size_t)remaining) + goto err; + /* add escape char if needed, clunky but easier to read */ + for(i = 0; i != tlen; i++) { + if('\\' == *cp || ',' == *cp) { + if(Curl_dyn_addn(&dval, "\\", 1)) + goto err; + } + if(Curl_dyn_addn(&dval, cp++, 1)) + goto err; + } + remaining -= (int)tlen; + } + /* this string is always null terminated */ + oval = Curl_dyn_ptr(&dval); + if(!oval) + goto err; + *alpns = oval; + return CURLE_OK; +err: + Curl_dyn_free(&dval); + return CURLE_BAD_CONTENT_ENCODING; +} + +#ifdef DEBUGBUILD +static CURLcode test_alpn_escapes(void) +{ + /* we will use an example from draft-ietf-dnsop-svcb, figure 10 */ + static unsigned char example[] = { + 0x08, /* length 8 */ + 0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */ + 0x02, /* length 2 */ + 0x68, 0x32 /* value "h2" */ + }; + size_t example_len = sizeof(example); + char *aval = NULL; + static const char *expected = "f\\\\oo\\,bar,h2"; + + if(local_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK) + return CURLE_BAD_CONTENT_ENCODING; + if(strlen(aval) != strlen(expected)) + return CURLE_BAD_CONTENT_ENCODING; + if(memcmp(aval, expected, strlen(aval))) + return CURLE_BAD_CONTENT_ENCODING; + return CURLE_OK; +} +#endif + +static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len, + struct Curl_https_rrinfo **hrr) +{ + size_t remaining = len; + unsigned char *cp = rrval; + uint16_t pcode = 0, plen = 0; + struct Curl_https_rrinfo *lhrr = NULL; + char *dnsname = NULL; + +#ifdef DEBUGBUILD + /* a few tests of escaping, should not be here but ok for now */ + if(test_alpn_escapes() != CURLE_OK) + return CURLE_OUT_OF_MEMORY; +#endif + lhrr = calloc(1, sizeof(struct Curl_https_rrinfo)); + if(!lhrr) + return CURLE_OUT_OF_MEMORY; + lhrr->val = Curl_memdup(rrval, len); + if(!lhrr->val) + goto err; + lhrr->len = len; + if(remaining <= 2) + goto err; + lhrr->priority = (uint16_t)((cp[0] << 8) + cp[1]); + cp += 2; + remaining -= (uint16_t)2; + if(local_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK) + goto err; + lhrr->target = dnsname; + while(remaining >= 4) { + pcode = (uint16_t)((*cp << 8) + (*(cp + 1))); + cp += 2; + plen = (uint16_t)((*cp << 8) + (*(cp + 1))); + cp += 2; + remaining -= 4; + if(pcode == HTTPS_RR_CODE_ALPN) { + if(local_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK) + goto err; + } + if(pcode == HTTPS_RR_CODE_NO_DEF_ALPN) + lhrr->no_def_alpn = TRUE; + else if(pcode == HTTPS_RR_CODE_IPV4) { + if(!plen) + goto err; + lhrr->ipv4hints = Curl_memdup(cp, plen); + if(!lhrr->ipv4hints) + goto err; + lhrr->ipv4hints_len = (size_t)plen; + } + else if(pcode == HTTPS_RR_CODE_ECH) { + if(!plen) + goto err; + lhrr->echconfiglist = Curl_memdup(cp, plen); + if(!lhrr->echconfiglist) + goto err; + lhrr->echconfiglist_len = (size_t)plen; + } + else if(pcode == HTTPS_RR_CODE_IPV6) { + if(!plen) + goto err; + lhrr->ipv6hints = Curl_memdup(cp, plen); + if(!lhrr->ipv6hints) + goto err; + lhrr->ipv6hints_len = (size_t)plen; + } + if(plen > 0 && plen <= remaining) { + cp += plen; + remaining -= plen; + } + } + DEBUGASSERT(!remaining); + *hrr = lhrr; + return CURLE_OK; +err: + if(lhrr) { + Curl_safefree(lhrr->target); + Curl_safefree(lhrr->echconfiglist); + Curl_safefree(lhrr->val); + Curl_safefree(lhrr->alpns); + Curl_safefree(lhrr); + } + return CURLE_OUT_OF_MEMORY; +} + +# ifdef DEBUGBUILD +static void local_print_httpsrr(struct Curl_easy *data, + struct Curl_https_rrinfo *hrr) +{ + DEBUGASSERT(hrr); + infof(data, "HTTPS RR: priority %d, target: %s", + hrr->priority, hrr->target); + if(hrr->alpns) + infof(data, "HTTPS RR: alpns %s", hrr->alpns); + else + infof(data, "HTTPS RR: no alpns"); + if(hrr->no_def_alpn) + infof(data, "HTTPS RR: no_def_alpn set"); + else + infof(data, "HTTPS RR: no_def_alpn not set"); + if(hrr->ipv4hints) { + local_print_buf(data, "HTTPS RR: ipv4hints", + hrr->ipv4hints, hrr->ipv4hints_len); + } + else + infof(data, "HTTPS RR: no ipv4hints"); + if(hrr->echconfiglist) { + local_print_buf(data, "HTTPS RR: ECHConfigList", + hrr->echconfiglist, hrr->echconfiglist_len); + } + else + infof(data, "HTTPS RR: no ECHConfigList"); + if(hrr->ipv6hints) { + local_print_buf(data, "HTTPS RR: ipv6hint", + hrr->ipv6hints, hrr->ipv6hints_len); + } + else + infof(data, "HTTPS RR: no ipv6hints"); + return; +} +# endif +#endif + +CURLcode Curl_doh_is_resolved(struct Curl_easy *data, + struct Curl_dns_entry **dnsp) +{ + CURLcode result; + struct dohdata *dohp = data->req.doh; + *dnsp = NULL; /* defaults to no response */ + if(!dohp) + return CURLE_OUT_OF_MEMORY; + + if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy && + !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) { + failf(data, "Could not DoH-resolve: %s", data->state.async.hostname); + return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY: + CURLE_COULDNT_RESOLVE_HOST; + } + else if(!dohp->pending) { +#ifndef USE_HTTPSRR + DOHcode rc[DOH_PROBE_SLOTS] = { + DOH_OK, DOH_OK + }; +#else + DOHcode rc[DOH_PROBE_SLOTS] = { + DOH_OK, DOH_OK, DOH_OK + }; +#endif + struct dohentry de; + int slot; + /* remove DoH handles from multi handle and close them */ + Curl_doh_close(data); + /* parse the responses, create the struct and return it! */ + de_init(&de); + for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { + struct dnsprobe *p = &dohp->probe[slot]; + if(!p->dnstype) + continue; + rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh), + Curl_dyn_len(&p->serverdoh), + p->dnstype, + &de); + Curl_dyn_free(&p->serverdoh); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(rc[slot]) { + infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]), + type2name(p->dnstype), dohp->host); + } +#endif + } /* next slot */ + + result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */ + if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) { + /* we have an address, of one kind or other */ + struct Curl_dns_entry *dns; + struct Curl_addrinfo *ai; + + + if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) { + infof(data, "[DoH] hostname: %s", dohp->host); + showdoh(data, &de); + } + + result = doh2ai(&de, dohp->host, dohp->port, &ai); + if(result) { + de_cleanup(&de); + return result; + } + + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + /* we got a response, store it in the cache */ + dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + if(!dns) { + /* returned failure, bail out nicely */ + Curl_freeaddrinfo(ai); + } + else { + data->state.async.dns = dns; + *dnsp = dns; + result = CURLE_OK; /* address resolution OK */ + } + } /* address processing done */ + + /* Now process any build-specific attributes retrieved from DNS */ +#ifdef USE_HTTPSRR + if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) { + struct Curl_https_rrinfo *hrr = NULL; + result = Curl_doh_decode_httpsrr(de.https_rrs->val, de.https_rrs->len, + &hrr); + if(result) { + infof(data, "Failed to decode HTTPS RR"); + return result; + } + infof(data, "Some HTTPS RR to process"); +# ifdef DEBUGBUILD + local_print_httpsrr(data, hrr); +# endif + (*dnsp)->hinfo = hrr; + } +#endif + + /* All done */ + de_cleanup(&de); + Curl_safefree(data->req.doh); + return result; + + } /* !dohp->pending */ + + /* else wait for pending DoH transactions to complete */ + return CURLE_OK; +} + +void Curl_doh_close(struct Curl_easy *data) +{ + struct dohdata *doh = data->req.doh; + if(doh) { + size_t slot; + for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { + if(!doh->probe[slot].easy) + continue; + /* data->multi might already be reset at this time */ + if(doh->probe[slot].easy->multi) + curl_multi_remove_handle(doh->probe[slot].easy->multi, + doh->probe[slot].easy); + Curl_close(&doh->probe[slot].easy); + } + } +} + +void Curl_doh_cleanup(struct Curl_easy *data) +{ + struct dohdata *doh = data->req.doh; + if(doh) { + Curl_doh_close(data); + curl_slist_free_all(doh->headers); + data->req.doh->headers = NULL; + Curl_safefree(data->req.doh); + } +} + +#endif /* CURL_DISABLE_DOH */ diff --git a/third_party/curl/lib/doh.h b/third_party/curl/lib/doh.h new file mode 100644 index 0000000000..5e86bf44a5 --- /dev/null +++ b/third_party/curl/lib/doh.h @@ -0,0 +1,168 @@ +#ifndef HEADER_CURL_DOH_H +#define HEADER_CURL_DOH_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "urldata.h" +#include "curl_addrinfo.h" +#ifdef USE_HTTPSRR +# include +#endif + +#ifndef CURL_DISABLE_DOH + +typedef enum { + DOH_OK, + DOH_DNS_BAD_LABEL, /* 1 */ + DOH_DNS_OUT_OF_RANGE, /* 2 */ + DOH_DNS_LABEL_LOOP, /* 3 */ + DOH_TOO_SMALL_BUFFER, /* 4 */ + DOH_OUT_OF_MEM, /* 5 */ + DOH_DNS_RDATA_LEN, /* 6 */ + DOH_DNS_MALFORMAT, /* 7 */ + DOH_DNS_BAD_RCODE, /* 8 - no such name */ + DOH_DNS_UNEXPECTED_TYPE, /* 9 */ + DOH_DNS_UNEXPECTED_CLASS, /* 10 */ + DOH_NO_CONTENT, /* 11 */ + DOH_DNS_BAD_ID, /* 12 */ + DOH_DNS_NAME_TOO_LONG /* 13 */ +} DOHcode; + +typedef enum { + DNS_TYPE_A = 1, + DNS_TYPE_NS = 2, + DNS_TYPE_CNAME = 5, + DNS_TYPE_AAAA = 28, + DNS_TYPE_DNAME = 39, /* RFC6672 */ + DNS_TYPE_HTTPS = 65 +} DNStype; + +/* one of these for each DoH request */ +struct dnsprobe { + CURL *easy; + DNStype dnstype; + unsigned char dohbuffer[512]; + size_t dohlen; + struct dynbuf serverdoh; +}; + +struct dohdata { + struct curl_slist *headers; + struct dnsprobe probe[DOH_PROBE_SLOTS]; + unsigned int pending; /* still outstanding requests */ + int port; + const char *host; +}; + +/* + * Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name + * and returns a 'Curl_addrinfo *' with the address information. + */ + +struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, + const char *hostname, + int port, + int *waitp); + +CURLcode Curl_doh_is_resolved(struct Curl_easy *data, + struct Curl_dns_entry **dns); + +#define DOH_MAX_ADDR 24 +#define DOH_MAX_CNAME 4 +#define DOH_MAX_HTTPS 4 + +struct dohaddr { + int type; + union { + unsigned char v4[4]; /* network byte order */ + unsigned char v6[16]; + } ip; +}; + +#ifdef USE_HTTPSRR + +/* + * These are the code points for DNS wire format SvcParams as + * per draft-ietf-dnsop-svcb-https + * Not all are supported now, and even those that are may need + * more work in future to fully support the spec. + */ +#define HTTPS_RR_CODE_ALPN 0x01 +#define HTTPS_RR_CODE_NO_DEF_ALPN 0x02 +#define HTTPS_RR_CODE_PORT 0x03 +#define HTTPS_RR_CODE_IPV4 0x04 +#define HTTPS_RR_CODE_ECH 0x05 +#define HTTPS_RR_CODE_IPV6 0x06 + +/* + * These may need escaping when found within an alpn string + * value. + */ +#define COMMA_CHAR ',' +#define BACKSLASH_CHAR '\\' + +struct dohhttps_rr { + uint16_t len; /* raw encoded length */ + unsigned char *val; /* raw encoded octets */ +}; +#endif + +struct dohentry { + struct dynbuf cname[DOH_MAX_CNAME]; + struct dohaddr addr[DOH_MAX_ADDR]; + int numaddr; + unsigned int ttl; + int numcname; +#ifdef USE_HTTPSRR + struct dohhttps_rr https_rrs[DOH_MAX_HTTPS]; + int numhttps_rrs; +#endif +}; + +void Curl_doh_close(struct Curl_easy *data); +void Curl_doh_cleanup(struct Curl_easy *data); + +#ifdef UNITTESTS +UNITTEST DOHcode doh_encode(const char *host, + DNStype dnstype, + unsigned char *dnsp, /* buffer */ + size_t len, /* buffer size */ + size_t *olen); /* output length */ +UNITTEST DOHcode doh_decode(const unsigned char *doh, + size_t dohlen, + DNStype dnstype, + struct dohentry *d); + +UNITTEST void de_init(struct dohentry *d); +UNITTEST void de_cleanup(struct dohentry *d); +#endif + +extern struct curl_trc_feat Curl_doh_trc; + +#else /* if DoH is disabled */ +#define Curl_doh(a,b,c,d) NULL +#define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST +#endif + +#endif /* HEADER_CURL_DOH_H */ diff --git a/third_party/curl/lib/dynbuf.c b/third_party/curl/lib/dynbuf.c new file mode 100644 index 0000000000..eab07efbf0 --- /dev/null +++ b/third_party/curl/lib/dynbuf.c @@ -0,0 +1,282 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "dynbuf.h" +#include "curl_printf.h" +#ifdef BUILDING_LIBCURL +#include "curl_memory.h" +#endif +#include "memdebug.h" + +#define MIN_FIRST_ALLOC 32 + +#define DYNINIT 0xbee51da /* random pattern */ + +/* + * Init a dynbuf struct. + */ +void Curl_dyn_init(struct dynbuf *s, size_t toobig) +{ + DEBUGASSERT(s); + DEBUGASSERT(toobig); + s->bufr = NULL; + s->leng = 0; + s->allc = 0; + s->toobig = toobig; +#ifdef DEBUGBUILD + s->init = DYNINIT; +#endif +} + +/* + * free the buffer and re-init the necessary fields. It does not touch the + * 'init' field and thus this buffer can be reused to add data to again. + */ +void Curl_dyn_free(struct dynbuf *s) +{ + DEBUGASSERT(s); + Curl_safefree(s->bufr); + s->leng = s->allc = 0; +} + +/* + * Store/append an chunk of memory to the dynbuf. + */ +static CURLcode dyn_nappend(struct dynbuf *s, + const unsigned char *mem, size_t len) +{ + size_t indx = s->leng; + size_t a = s->allc; + size_t fit = len + indx + 1; /* new string + old string + zero byte */ + + /* try to detect if there is rubbish in the struct */ + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(s->toobig); + DEBUGASSERT(indx < s->toobig); + DEBUGASSERT(!s->leng || s->bufr); + DEBUGASSERT(a <= s->toobig); + DEBUGASSERT(!len || mem); + + if(fit > s->toobig) { + Curl_dyn_free(s); + return CURLE_TOO_LARGE; + } + else if(!a) { + DEBUGASSERT(!indx); + /* first invoke */ + if(MIN_FIRST_ALLOC > s->toobig) + a = s->toobig; + else if(fit < MIN_FIRST_ALLOC) + a = MIN_FIRST_ALLOC; + else + a = fit; + } + else { + while(a < fit) + a *= 2; + if(a > s->toobig) + /* no point in allocating a larger buffer than this is allowed to use */ + a = s->toobig; + } + + if(a != s->allc) { + /* this logic is not using Curl_saferealloc() to make the tool not have to + include that as well when it uses this code */ + void *p = realloc(s->bufr, a); + if(!p) { + Curl_dyn_free(s); + return CURLE_OUT_OF_MEMORY; + } + s->bufr = p; + s->allc = a; + } + + if(len) + memcpy(&s->bufr[indx], mem, len); + s->leng = indx + len; + s->bufr[s->leng] = 0; + return CURLE_OK; +} + +/* + * Clears the string, keeps the allocation. This can also be called on a + * buffer that already was freed. + */ +void Curl_dyn_reset(struct dynbuf *s) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + if(s->leng) + s->bufr[0] = 0; + s->leng = 0; +} + +/* + * Specify the size of the tail to keep (number of bytes from the end of the + * buffer). The rest will be dropped. + */ +CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + if(trail > s->leng) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(trail == s->leng) + return CURLE_OK; + else if(!trail) { + Curl_dyn_reset(s); + } + else { + memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail); + s->leng = trail; + s->bufr[s->leng] = 0; + } + return CURLE_OK; + +} + +/* + * Appends a buffer with length. + */ +CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + return dyn_nappend(s, mem, len); +} + +/* + * Append a null-terminated string at the end. + */ +CURLcode Curl_dyn_add(struct dynbuf *s, const char *str) +{ + size_t n; + DEBUGASSERT(str); + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + n = strlen(str); + return dyn_nappend(s, (unsigned char *)str, n); +} + +/* + * Append a string vprintf()-style + */ +CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) +{ +#ifdef BUILDING_LIBCURL + int rc; + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + DEBUGASSERT(fmt); + rc = Curl_dyn_vprintf(s, fmt, ap); + + if(!rc) + return CURLE_OK; + else if(rc == MERR_TOO_LARGE) + return CURLE_TOO_LARGE; + return CURLE_OUT_OF_MEMORY; +#else + char *str; + str = vaprintf(fmt, ap); /* this allocs a new string to append */ + + if(str) { + CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str)); + free(str); + return result; + } + /* If we failed, we cleanup the whole buffer and return error */ + Curl_dyn_free(s); + return CURLE_OUT_OF_MEMORY; +#endif +} + +/* + * Append a string printf()-style + */ +CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...) +{ + CURLcode result; + va_list ap; + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + va_start(ap, fmt); + result = Curl_dyn_vaddf(s, fmt, ap); + va_end(ap); + return result; +} + +/* + * Returns a pointer to the buffer. + */ +char *Curl_dyn_ptr(const struct dynbuf *s) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + return s->bufr; +} + +/* + * Returns an unsigned pointer to the buffer. + */ +unsigned char *Curl_dyn_uptr(const struct dynbuf *s) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + return (unsigned char *)s->bufr; +} + +/* + * Returns the length of the buffer. + */ +size_t Curl_dyn_len(const struct dynbuf *s) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + return s->leng; +} + +/* + * Set a new (smaller) length. + */ +CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + if(set > s->leng) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->leng = set; + s->bufr[s->leng] = 0; + return CURLE_OK; +} diff --git a/third_party/curl/lib/dynbuf.h b/third_party/curl/lib/dynbuf.h new file mode 100644 index 0000000000..7dbaab886e --- /dev/null +++ b/third_party/curl/lib/dynbuf.h @@ -0,0 +1,93 @@ +#ifndef HEADER_CURL_DYNBUF_H +#define HEADER_CURL_DYNBUF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include + +#ifndef BUILDING_LIBCURL +/* this renames the functions so that the tool code can use the same code + without getting symbol collisions */ +#define Curl_dyn_init(a,b) curlx_dyn_init(a,b) +#define Curl_dyn_add(a,b) curlx_dyn_add(a,b) +#define Curl_dyn_addn(a,b,c) curlx_dyn_addn(a,b,c) +#define Curl_dyn_addf curlx_dyn_addf +#define Curl_dyn_vaddf curlx_dyn_vaddf +#define Curl_dyn_free(a) curlx_dyn_free(a) +#define Curl_dyn_ptr(a) curlx_dyn_ptr(a) +#define Curl_dyn_uptr(a) curlx_dyn_uptr(a) +#define Curl_dyn_len(a) curlx_dyn_len(a) +#define Curl_dyn_reset(a) curlx_dyn_reset(a) +#define Curl_dyn_tail(a,b) curlx_dyn_tail(a,b) +#define Curl_dyn_setlen(a,b) curlx_dyn_setlen(a,b) +#define curlx_dynbuf dynbuf /* for the struct name */ +#endif + +struct dynbuf { + char *bufr; /* point to a null-terminated allocated buffer */ + size_t leng; /* number of bytes *EXCLUDING* the null-terminator */ + size_t allc; /* size of the current allocation */ + size_t toobig; /* size limit for the buffer */ +#ifdef DEBUGBUILD + int init; /* detect API usage mistakes */ +#endif +}; + +void Curl_dyn_init(struct dynbuf *s, size_t toobig); +void Curl_dyn_free(struct dynbuf *s); +CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len) + WARN_UNUSED_RESULT; +CURLcode Curl_dyn_add(struct dynbuf *s, const char *str) + WARN_UNUSED_RESULT; +CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...) + WARN_UNUSED_RESULT CURL_PRINTF(2, 3); +CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) + WARN_UNUSED_RESULT CURL_PRINTF(2, 0); +void Curl_dyn_reset(struct dynbuf *s); +CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail); +CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set); +char *Curl_dyn_ptr(const struct dynbuf *s); +unsigned char *Curl_dyn_uptr(const struct dynbuf *s); +size_t Curl_dyn_len(const struct dynbuf *s); + +/* returns 0 on success, -1 on error */ +/* The implementation of this function exists in mprintf.c */ +int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save); + +/* Dynamic buffer max sizes */ +#define DYN_DOH_RESPONSE 3000 +#define DYN_DOH_CNAME 256 +#define DYN_PAUSE_BUFFER (64 * 1024 * 1024) +#define DYN_HAXPROXY 2048 +#define DYN_HTTP_REQUEST (1024*1024) +#define DYN_APRINTF 8000000 +#define DYN_RTSP_REQ_HEADER (64*1024) +#define DYN_TRAILERS (64*1024) +#define DYN_PROXY_CONNECT_HEADERS 16384 +#define DYN_QLOG_NAME 1024 +#define DYN_H1_TRAILER 4096 +#define DYN_PINGPPONG_CMD (64*1024) +#define DYN_IMAP_CMD (64*1024) +#define DYN_MQTT_RECV (64*1024) +#endif diff --git a/third_party/curl/lib/dynhds.c b/third_party/curl/lib/dynhds.c new file mode 100644 index 0000000000..9153838e3a --- /dev/null +++ b/third_party/curl/lib/dynhds.c @@ -0,0 +1,396 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "dynhds.h" +#include "strcase.h" + +/* The last 3 #include files should be in this order */ +#ifdef USE_NGHTTP2 +#include +#include +#endif /* USE_NGHTTP2 */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +static struct dynhds_entry * +entry_new(const char *name, size_t namelen, + const char *value, size_t valuelen, int opts) +{ + struct dynhds_entry *e; + char *p; + + DEBUGASSERT(name); + DEBUGASSERT(value); + e = calloc(1, sizeof(*e) + namelen + valuelen + 2); + if(!e) + return NULL; + e->name = p = ((char *)e) + sizeof(*e); + memcpy(p, name, namelen); + e->namelen = namelen; + e->value = p += namelen + 1; /* leave a \0 at the end of name */ + memcpy(p, value, valuelen); + e->valuelen = valuelen; + if(opts & DYNHDS_OPT_LOWERCASE) + Curl_strntolower(e->name, e->name, e->namelen); + return e; +} + +static struct dynhds_entry * +entry_append(struct dynhds_entry *e, + const char *value, size_t valuelen) +{ + struct dynhds_entry *e2; + size_t valuelen2 = e->valuelen + 1 + valuelen; + char *p; + + DEBUGASSERT(value); + e2 = calloc(1, sizeof(*e) + e->namelen + valuelen2 + 2); + if(!e2) + return NULL; + e2->name = p = ((char *)e2) + sizeof(*e2); + memcpy(p, e->name, e->namelen); + e2->namelen = e->namelen; + e2->value = p += e->namelen + 1; /* leave a \0 at the end of name */ + memcpy(p, e->value, e->valuelen); + p += e->valuelen; + p[0] = ' '; + memcpy(p + 1, value, valuelen); + e2->valuelen = valuelen2; + return e2; +} + +static void entry_free(struct dynhds_entry *e) +{ + free(e); +} + +void Curl_dynhds_init(struct dynhds *dynhds, size_t max_entries, + size_t max_strs_size) +{ + DEBUGASSERT(dynhds); + DEBUGASSERT(max_strs_size); + dynhds->hds = NULL; + dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0; + dynhds->max_entries = max_entries; + dynhds->max_strs_size = max_strs_size; + dynhds->opts = 0; +} + +void Curl_dynhds_free(struct dynhds *dynhds) +{ + DEBUGASSERT(dynhds); + if(dynhds->hds && dynhds->hds_len) { + size_t i; + DEBUGASSERT(dynhds->hds); + for(i = 0; i < dynhds->hds_len; ++i) { + entry_free(dynhds->hds[i]); + } + } + Curl_safefree(dynhds->hds); + dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0; +} + +void Curl_dynhds_reset(struct dynhds *dynhds) +{ + DEBUGASSERT(dynhds); + if(dynhds->hds_len) { + size_t i; + DEBUGASSERT(dynhds->hds); + for(i = 0; i < dynhds->hds_len; ++i) { + entry_free(dynhds->hds[i]); + dynhds->hds[i] = NULL; + } + } + dynhds->hds_len = dynhds->strs_len = 0; +} + +size_t Curl_dynhds_count(struct dynhds *dynhds) +{ + return dynhds->hds_len; +} + +void Curl_dynhds_set_opts(struct dynhds *dynhds, int opts) +{ + dynhds->opts = opts; +} + +struct dynhds_entry *Curl_dynhds_getn(struct dynhds *dynhds, size_t n) +{ + DEBUGASSERT(dynhds); + return (n < dynhds->hds_len)? dynhds->hds[n] : NULL; +} + +struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, const char *name, + size_t namelen) +{ + size_t i; + for(i = 0; i < dynhds->hds_len; ++i) { + if(dynhds->hds[i]->namelen == namelen && + strncasecompare(dynhds->hds[i]->name, name, namelen)) { + return dynhds->hds[i]; + } + } + return NULL; +} + +struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name) +{ + return Curl_dynhds_get(dynhds, name, strlen(name)); +} + +CURLcode Curl_dynhds_add(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen) +{ + struct dynhds_entry *entry = NULL; + CURLcode result = CURLE_OUT_OF_MEMORY; + + DEBUGASSERT(dynhds); + if(dynhds->max_entries && dynhds->hds_len >= dynhds->max_entries) + return CURLE_OUT_OF_MEMORY; + if(dynhds->strs_len + namelen + valuelen > dynhds->max_strs_size) + return CURLE_OUT_OF_MEMORY; + +entry = entry_new(name, namelen, value, valuelen, dynhds->opts); + if(!entry) + goto out; + + if(dynhds->hds_len + 1 >= dynhds->hds_allc) { + size_t nallc = dynhds->hds_len + 16; + struct dynhds_entry **nhds; + + if(dynhds->max_entries && nallc > dynhds->max_entries) + nallc = dynhds->max_entries; + + nhds = calloc(nallc, sizeof(struct dynhds_entry *)); + if(!nhds) + goto out; + if(dynhds->hds) { + memcpy(nhds, dynhds->hds, + dynhds->hds_len * sizeof(struct dynhds_entry *)); + Curl_safefree(dynhds->hds); + } + dynhds->hds = nhds; + dynhds->hds_allc = nallc; + } + dynhds->hds[dynhds->hds_len++] = entry; + entry = NULL; + dynhds->strs_len += namelen + valuelen; + result = CURLE_OK; + +out: + if(entry) + entry_free(entry); + return result; +} + +CURLcode Curl_dynhds_cadd(struct dynhds *dynhds, + const char *name, const char *value) +{ + return Curl_dynhds_add(dynhds, name, strlen(name), value, strlen(value)); +} + +CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds, + const char *line, size_t line_len) +{ + const char *p; + const char *name; + size_t namelen; + const char *value; + size_t valuelen, i; + + if(!line || !line_len) + return CURLE_OK; + + if((line[0] == ' ') || (line[0] == '\t')) { + struct dynhds_entry *e, *e2; + /* header continuation, yikes! */ + if(!dynhds->hds_len) + return CURLE_BAD_FUNCTION_ARGUMENT; + + while(line_len && ISBLANK(line[0])) { + ++line; + --line_len; + } + if(!line_len) + return CURLE_BAD_FUNCTION_ARGUMENT; + e = dynhds->hds[dynhds->hds_len-1]; + e2 = entry_append(e, line, line_len); + if(!e2) + return CURLE_OUT_OF_MEMORY; + dynhds->hds[dynhds->hds_len-1] = e2; + entry_free(e); + return CURLE_OK; + } + else { + p = memchr(line, ':', line_len); + if(!p) + return CURLE_BAD_FUNCTION_ARGUMENT; + name = line; + namelen = p - line; + p++; /* move past the colon */ + for(i = namelen + 1; i < line_len; ++i, ++p) { + if(!ISBLANK(*p)) + break; + } + value = p; + valuelen = line_len - i; + + p = memchr(value, '\r', valuelen); + if(!p) + p = memchr(value, '\n', valuelen); + if(p) + valuelen = (size_t)(p - value); + + return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); + } +} + +CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line) +{ + return Curl_dynhds_h1_add_line(dynhds, line, line? strlen(line) : 0); +} + +#ifdef UNITTESTS +/* used by unit2602.c */ + +bool Curl_dynhds_contains(struct dynhds *dynhds, + const char *name, size_t namelen) +{ + return !!Curl_dynhds_get(dynhds, name, namelen); +} + +bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name) +{ + return Curl_dynhds_contains(dynhds, name, strlen(name)); +} + +size_t Curl_dynhds_count_name(struct dynhds *dynhds, + const char *name, size_t namelen) +{ + size_t n = 0; + if(dynhds->hds_len) { + size_t i; + for(i = 0; i < dynhds->hds_len; ++i) { + if((namelen == dynhds->hds[i]->namelen) && + strncasecompare(name, dynhds->hds[i]->name, namelen)) + ++n; + } + } + return n; +} + +size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name) +{ + return Curl_dynhds_count_name(dynhds, name, strlen(name)); +} + +CURLcode Curl_dynhds_set(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen) +{ + Curl_dynhds_remove(dynhds, name, namelen); + return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); +} + +size_t Curl_dynhds_remove(struct dynhds *dynhds, + const char *name, size_t namelen) +{ + size_t n = 0; + if(dynhds->hds_len) { + size_t i, len; + for(i = 0; i < dynhds->hds_len; ++i) { + if((namelen == dynhds->hds[i]->namelen) && + strncasecompare(name, dynhds->hds[i]->name, namelen)) { + ++n; + --dynhds->hds_len; + dynhds->strs_len -= (dynhds->hds[i]->namelen + + dynhds->hds[i]->valuelen); + entry_free(dynhds->hds[i]); + len = dynhds->hds_len - i; /* remaining entries */ + if(len) { + memmove(&dynhds->hds[i], &dynhds->hds[i + 1], + len * sizeof(dynhds->hds[i])); + } + --i; /* do this index again */ + } + } + } + return n; +} + +size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name) +{ + return Curl_dynhds_remove(dynhds, name, strlen(name)); +} + +#endif + +CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) +{ + CURLcode result = CURLE_OK; + size_t i; + + if(!dynhds->hds_len) + return result; + + for(i = 0; i < dynhds->hds_len; ++i) { + result = Curl_dyn_addf(dbuf, "%.*s: %.*s\r\n", + (int)dynhds->hds[i]->namelen, dynhds->hds[i]->name, + (int)dynhds->hds[i]->valuelen, dynhds->hds[i]->value); + if(result) + break; + } + + return result; +} + +#ifdef USE_NGHTTP2 + +nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount) +{ + nghttp2_nv *nva = calloc(1, sizeof(nghttp2_nv) * dynhds->hds_len); + size_t i; + + *pcount = 0; + if(!nva) + return NULL; + + for(i = 0; i < dynhds->hds_len; ++i) { + struct dynhds_entry *e = dynhds->hds[i]; + DEBUGASSERT(e); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP2_NV_FLAG_NONE; + } + *pcount = dynhds->hds_len; + return nva; +} + +#endif /* USE_NGHTTP2 */ diff --git a/third_party/curl/lib/dynhds.h b/third_party/curl/lib/dynhds.h new file mode 100644 index 0000000000..fb162a30de --- /dev/null +++ b/third_party/curl/lib/dynhds.h @@ -0,0 +1,187 @@ +#ifndef HEADER_CURL_DYNHDS_H +#define HEADER_CURL_DYNHDS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include +#include "dynbuf.h" + +struct dynbuf; + +/** + * A single header entry. + * `name` and `value` are non-NULL and always NUL terminated. + */ +struct dynhds_entry { + char *name; + char *value; + size_t namelen; + size_t valuelen; +}; + +struct dynhds { + struct dynhds_entry **hds; + size_t hds_len; /* number of entries in hds */ + size_t hds_allc; /* size of hds allocation */ + size_t max_entries; /* size limit number of entries */ + size_t strs_len; /* length of all strings */ + size_t max_strs_size; /* max length of all strings */ + int opts; +}; + +#define DYNHDS_OPT_NONE (0) +#define DYNHDS_OPT_LOWERCASE (1 << 0) + +/** + * Init for use on first time or after a reset. + * Allow `max_entries` headers to be added, 0 for unlimited. + * Allow size of all name and values added to not exceed `max_strs_size`` + */ +void Curl_dynhds_init(struct dynhds *dynhds, size_t max_entries, + size_t max_strs_size); +/** + * Frees all data held in `dynhds`, but not the struct itself. + */ +void Curl_dynhds_free(struct dynhds *dynhds); + +/** + * Reset `dyndns` to the initial init state. May keep allocations + * around. + */ +void Curl_dynhds_reset(struct dynhds *dynhds); + +/** + * Return the number of header entries. + */ +size_t Curl_dynhds_count(struct dynhds *dynhds); + +/** + * Set the options to use, replacing any existing ones. + * This will not have an effect on already existing headers. + */ +void Curl_dynhds_set_opts(struct dynhds *dynhds, int opts); + +/** + * Return the n-th header entry or NULL if it does not exist. + */ +struct dynhds_entry *Curl_dynhds_getn(struct dynhds *dynhds, size_t n); + +/** + * Return the 1st header entry of the name or NULL if none exists. + */ +struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, + const char *name, size_t namelen); +struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name); + +#ifdef UNITTESTS +/* used by unit2602.c */ + +/** + * Return TRUE iff one or more headers with the given name exist. + */ +bool Curl_dynhds_contains(struct dynhds *dynhds, + const char *name, size_t namelen); +bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name); + +/** + * Return how often the given name appears in `dynhds`. + * Names are case-insensitive. + */ +size_t Curl_dynhds_count_name(struct dynhds *dynhds, + const char *name, size_t namelen); + +/** + * Return how often the given 0-terminated name appears in `dynhds`. + * Names are case-insensitive. + */ +size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name); + +/** + * Remove all entries with the given name. + * Returns number of entries removed. + */ +size_t Curl_dynhds_remove(struct dynhds *dynhds, + const char *name, size_t namelen); +size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name); + + +/** + * Set the give header name and value, replacing any entries with + * the same name. The header is added at the end of all (remaining) + * entries. + */ +CURLcode Curl_dynhds_set(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen); +#endif + +CURLcode Curl_dynhds_cset(struct dynhds *dynhds, + const char *name, const char *value); + +/** + * Add a header, name + value, to `dynhds` at the end. Does *not* + * check for duplicate names. + */ +CURLcode Curl_dynhds_add(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen); + +/** + * Add a header, c-string name + value, to `dynhds` at the end. + */ +CURLcode Curl_dynhds_cadd(struct dynhds *dynhds, + const char *name, const char *value); + +/** + * Add a single header from an HTTP/1.1 formatted line at the end. Line + * may contain a delimiting \r\n or just \n. Any characters after + * that will be ignored. + */ +CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line); + +/** + * Add a single header from an HTTP/1.1 formatted line at the end. Line + * may contain a delimiting \r\n or just \n. Any characters after + * that will be ignored. + */ +CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds, + const char *line, size_t line_len); + +/** + * Add the headers to the given `dynbuf` in HTTP/1.1 format with + * cr+lf line endings. Will NOT output a last empty line. + */ +CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf); + +#ifdef USE_NGHTTP2 + +#include +#include + +nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount); + +#endif /* USE_NGHTTP2 */ + +#endif /* HEADER_CURL_DYNHDS_H */ diff --git a/third_party/curl/lib/easy.c b/third_party/curl/lib/easy.c new file mode 100644 index 0000000000..2ba9af0b3a --- /dev/null +++ b/third_party/curl/lib/easy.c @@ -0,0 +1,1350 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include "urldata.h" +#include +#include "transfer.h" +#include "vtls/vtls.h" +#include "url.h" +#include "getinfo.h" +#include "hostip.h" +#include "share.h" +#include "strdup.h" +#include "progress.h" +#include "easyif.h" +#include "multiif.h" +#include "select.h" +#include "cfilters.h" +#include "sendf.h" /* for failf function prototype */ +#include "connect.h" /* for Curl_getconnectinfo */ +#include "slist.h" +#include "mime.h" +#include "amigaos.h" +#include "macos.h" +#include "warnless.h" +#include "sigpipe.h" +#include "vssh/ssh.h" +#include "setopt.h" +#include "http_digest.h" +#include "system_win32.h" +#include "http2.h" +#include "dynbuf.h" +#include "altsvc.h" +#include "hsts.h" + +#include "easy_lock.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* true globals -- for curl_global_init() and curl_global_cleanup() */ +static unsigned int initialized; +static long easy_init_flags; + +#ifdef GLOBAL_INIT_IS_THREADSAFE + +static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; +#define global_init_lock() curl_simple_lock_lock(&s_lock) +#define global_init_unlock() curl_simple_lock_unlock(&s_lock) + +#else + +#define global_init_lock() +#define global_init_unlock() + +#endif + +/* + * strdup (and other memory functions) is redefined in complicated + * ways, but at this point it must be defined as the system-supplied strdup + * so the callback pointer is initialized correctly. + */ +#if defined(_WIN32_WCE) +#define system_strdup _strdup +#elif !defined(HAVE_STRDUP) +#define system_strdup Curl_strdup +#else +#define system_strdup strdup +#endif + +#if defined(_MSC_VER) && defined(_DLL) +# pragma warning(disable:4232) /* MSVC extension, dllimport identity */ +#endif + +/* + * If a memory-using function (like curl_getenv) is used before + * curl_global_init() is called, we need to have these pointers set already. + */ +curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; +curl_free_callback Curl_cfree = (curl_free_callback)free; +curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; +curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; +curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; +#if defined(_WIN32) && defined(UNICODE) +curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup; +#endif + +#if defined(_MSC_VER) && defined(_DLL) +# pragma warning(default:4232) /* MSVC extension, dllimport identity */ +#endif + +#ifdef DEBUGBUILD +static char *leakpointer; +#endif + +/** + * curl_global_init() globally initializes curl given a bitwise set of the + * different features of what to initialize. + */ +static CURLcode global_init(long flags, bool memoryfuncs) +{ + if(initialized++) + return CURLE_OK; + + if(memoryfuncs) { + /* Setup the default memory functions here (again) */ + Curl_cmalloc = (curl_malloc_callback)malloc; + Curl_cfree = (curl_free_callback)free; + Curl_crealloc = (curl_realloc_callback)realloc; + Curl_cstrdup = (curl_strdup_callback)system_strdup; + Curl_ccalloc = (curl_calloc_callback)calloc; +#if defined(_WIN32) && defined(UNICODE) + Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; +#endif + } + + if(Curl_trc_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_trc_init failed\n")); + goto fail; + } + + if(!Curl_ssl_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); + goto fail; + } + + if(Curl_win32_init(flags)) { + DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); + goto fail; + } + + if(Curl_amiga_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n")); + goto fail; + } + + if(Curl_macos_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_macos_init failed\n")); + goto fail; + } + + if(Curl_resolver_global_init()) { + DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); + goto fail; + } + + if(Curl_ssh_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_ssh_init failed\n")); + goto fail; + } + + easy_init_flags = flags; + +#ifdef DEBUGBUILD + if(getenv("CURL_GLOBAL_INIT")) + /* alloc data that will leak if *cleanup() is not called! */ + leakpointer = malloc(1); +#endif + + return CURLE_OK; + +fail: + initialized--; /* undo the increase */ + return CURLE_FAILED_INIT; +} + + +/** + * curl_global_init() globally initializes curl given a bitwise set of the + * different features of what to initialize. + */ +CURLcode curl_global_init(long flags) +{ + CURLcode result; + global_init_lock(); + + result = global_init(flags, TRUE); + + global_init_unlock(); + + return result; +} + +/* + * curl_global_init_mem() globally initializes curl and also registers the + * user provided callback routines. + */ +CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, + curl_free_callback f, curl_realloc_callback r, + curl_strdup_callback s, curl_calloc_callback c) +{ + CURLcode result; + + /* Invalid input, return immediately */ + if(!m || !f || !r || !s || !c) + return CURLE_FAILED_INIT; + + global_init_lock(); + + if(initialized) { + /* Already initialized, do not do it again, but bump the variable anyway to + work like curl_global_init() and require the same amount of cleanup + calls. */ + initialized++; + global_init_unlock(); + return CURLE_OK; + } + + /* set memory functions before global_init() in case it wants memory + functions */ + Curl_cmalloc = m; + Curl_cfree = f; + Curl_cstrdup = s; + Curl_crealloc = r; + Curl_ccalloc = c; + + /* Call the actual init function, but without setting */ + result = global_init(flags, FALSE); + + global_init_unlock(); + + return result; +} + +/** + * curl_global_cleanup() globally cleanups curl, uses the value of + * "easy_init_flags" to determine what needs to be cleaned up and what does + * not. + */ +void curl_global_cleanup(void) +{ + global_init_lock(); + + if(!initialized) { + global_init_unlock(); + return; + } + + if(--initialized) { + global_init_unlock(); + return; + } + + Curl_ssl_cleanup(); + Curl_resolver_global_cleanup(); + +#ifdef _WIN32 + Curl_win32_cleanup(easy_init_flags); +#endif + + Curl_amiga_cleanup(); + + Curl_ssh_cleanup(); + +#ifdef DEBUGBUILD + free(leakpointer); +#endif + + easy_init_flags = 0; + + global_init_unlock(); +} + +/** + * curl_global_trace() globally initializes curl logging. + */ +CURLcode curl_global_trace(const char *config) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + CURLcode result; + global_init_lock(); + + result = Curl_trc_opt(config); + + global_init_unlock(); + + return result; +#else + (void)config; + return CURLE_OK; +#endif +} + +/* + * curl_global_sslset() globally initializes the SSL backend to use. + */ +CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, + const curl_ssl_backend ***avail) +{ + CURLsslset rc; + + global_init_lock(); + + rc = Curl_init_sslset_nolock(id, name, avail); + + global_init_unlock(); + + return rc; +} + +/* + * curl_easy_init() is the external interface to alloc, setup and init an + * easy handle that is returned. If anything goes wrong, NULL is returned. + */ +struct Curl_easy *curl_easy_init(void) +{ + CURLcode result; + struct Curl_easy *data; + + /* Make sure we inited the global SSL stuff */ + global_init_lock(); + + if(!initialized) { + result = global_init(CURL_GLOBAL_DEFAULT, TRUE); + if(result) { + /* something in the global init failed, return nothing */ + DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n")); + global_init_unlock(); + return NULL; + } + } + global_init_unlock(); + + /* We use curl_open() with undefined URL so far */ + result = Curl_open(&data); + if(result) { + DEBUGF(fprintf(stderr, "Error: Curl_open failed\n")); + return NULL; + } + + return data; +} + +#ifdef DEBUGBUILD + +struct socketmonitor { + struct socketmonitor *next; /* the next node in the list or NULL */ + struct pollfd socket; /* socket info of what to monitor */ +}; + +struct events { + long ms; /* timeout, run the timeout function when reached */ + bool msbump; /* set TRUE when timeout is set by callback */ + int num_sockets; /* number of nodes in the monitor list */ + struct socketmonitor *list; /* list of sockets to monitor */ + int running_handles; /* store the returned number */ +}; + +/* events_timer + * + * Callback that gets called with a new value when the timeout should be + * updated. + */ + +static int events_timer(struct Curl_multi *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp) /* private callback pointer */ +{ + struct events *ev = userp; + (void)multi; + if(timeout_ms == -1) + /* timeout removed */ + timeout_ms = 0; + else if(timeout_ms == 0) + /* timeout is already reached! */ + timeout_ms = 1; /* trigger asap */ + + ev->ms = timeout_ms; + ev->msbump = TRUE; + return 0; +} + + +/* poll2cselect + * + * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones + */ +static int poll2cselect(int pollmask) +{ + int omask = 0; + if(pollmask & POLLIN) + omask |= CURL_CSELECT_IN; + if(pollmask & POLLOUT) + omask |= CURL_CSELECT_OUT; + if(pollmask & POLLERR) + omask |= CURL_CSELECT_ERR; + return omask; +} + + +/* socketcb2poll + * + * convert from libcurl' CURL_POLL_* bit definitions to poll()'s + */ +static short socketcb2poll(int pollmask) +{ + short omask = 0; + if(pollmask & CURL_POLL_IN) + omask |= POLLIN; + if(pollmask & CURL_POLL_OUT) + omask |= POLLOUT; + return omask; +} + +/* events_socket + * + * Callback that gets called with information about socket activity to + * monitor. + */ +static int events_socket(struct Curl_easy *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp) /* private socket + pointer */ +{ + struct events *ev = userp; + struct socketmonitor *m; + struct socketmonitor *prev = NULL; + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) easy; +#endif + (void)socketp; + + m = ev->list; + while(m) { + if(m->socket.fd == s) { + + if(what == CURL_POLL_REMOVE) { + struct socketmonitor *nxt = m->next; + /* remove this node from the list of monitored sockets */ + if(prev) + prev->next = nxt; + else + ev->list = nxt; + free(m); + m = nxt; + infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T + " REMOVED", s); + } + else { + /* The socket 's' is already being monitored, update the activity + mask. Convert from libcurl bitmask to the poll one. */ + m->socket.events = socketcb2poll(what); + infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T + " UPDATED as %s%s", s, + (what&CURL_POLL_IN)?"IN":"", + (what&CURL_POLL_OUT)?"OUT":""); + } + break; + } + prev = m; + m = m->next; /* move to next node */ + } + if(!m) { + if(what == CURL_POLL_REMOVE) { + /* this happens a bit too often, libcurl fix perhaps? */ + /* fprintf(stderr, + "%s: socket %d asked to be REMOVED but not present!\n", + __func__, s); */ + } + else { + m = malloc(sizeof(struct socketmonitor)); + if(m) { + m->next = ev->list; + m->socket.fd = s; + m->socket.events = socketcb2poll(what); + m->socket.revents = 0; + ev->list = m; + infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T + " ADDED as %s%s", s, + (what&CURL_POLL_IN)?"IN":"", + (what&CURL_POLL_OUT)?"OUT":""); + } + else + return CURLE_OUT_OF_MEMORY; + } + } + + return 0; +} + + +/* + * events_setup() + * + * Do the multi handle setups that only event-based transfers need. + */ +static void events_setup(struct Curl_multi *multi, struct events *ev) +{ + /* timer callback */ + curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, events_timer); + curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ev); + + /* socket callback */ + curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, events_socket); + curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev); +} + + +/* wait_or_timeout() + * + * waits for activity on any of the given sockets, or the timeout to trigger. + */ + +static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) +{ + bool done = FALSE; + CURLMcode mcode = CURLM_OK; + CURLcode result = CURLE_OK; + + while(!done) { + CURLMsg *msg; + struct socketmonitor *m; + struct pollfd *f; + struct pollfd fds[4]; + int numfds = 0; + int pollrc; + int i; + struct curltime before; + struct curltime after; + + /* populate the fds[] array */ + for(m = ev->list, f = &fds[0]; m; m = m->next) { + f->fd = m->socket.fd; + f->events = m->socket.events; + f->revents = 0; + /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */ + f++; + numfds++; + } + + /* get the time stamp to use to figure out how long poll takes */ + before = Curl_now(); + + /* wait for activity or timeout */ + pollrc = Curl_poll(fds, (unsigned int)numfds, ev->ms); + if(pollrc < 0) + return CURLE_UNRECOVERABLE_POLL; + + after = Curl_now(); + + ev->msbump = FALSE; /* reset here */ + + if(!pollrc) { + /* timeout! */ + ev->ms = 0; + /* fprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */ + mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, + &ev->running_handles); + } + else { + /* here pollrc is > 0 */ + + /* loop over the monitored sockets to see which ones had activity */ + for(i = 0; i< numfds; i++) { + if(fds[i].revents) { + /* socket activity, tell libcurl */ + int act = poll2cselect(fds[i].revents); /* convert */ + infof(multi->easyp, + "call curl_multi_socket_action(socket " + "%" CURL_FORMAT_SOCKET_T ")", fds[i].fd); + mcode = curl_multi_socket_action(multi, fds[i].fd, act, + &ev->running_handles); + } + } + + if(!ev->msbump) { + /* If nothing updated the timeout, we decrease it by the spent time. + * If it was updated, it has the new timeout time stored already. + */ + timediff_t timediff = Curl_timediff(after, before); + if(timediff > 0) { + if(timediff > ev->ms) + ev->ms = 0; + else + ev->ms -= (long)timediff; + } + } + } + + if(mcode) + return CURLE_URL_MALFORMAT; + + /* we do not really care about the "msgs_in_queue" value returned in the + second argument */ + msg = curl_multi_info_read(multi, &pollrc); + if(msg) { + result = msg->data.result; + done = TRUE; + } + } + + return result; +} + + +/* easy_events() + * + * Runs a transfer in a blocking manner using the events-based API + */ +static CURLcode easy_events(struct Curl_multi *multi) +{ + /* this struct is made static to allow it to be used after this function + returns and curl_multi_remove_handle() is called */ + static struct events evs = {2, FALSE, 0, NULL, 0}; + + /* if running event-based, do some further multi inits */ + events_setup(multi, &evs); + + return wait_or_timeout(multi, &evs); +} +#else /* DEBUGBUILD */ +/* when not built with debug, this function does not exist */ +#define easy_events(x) CURLE_NOT_BUILT_IN +#endif + +static CURLcode easy_transfer(struct Curl_multi *multi) +{ + bool done = FALSE; + CURLMcode mcode = CURLM_OK; + CURLcode result = CURLE_OK; + + while(!done && !mcode) { + int still_running = 0; + + mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL); + + if(!mcode) + mcode = curl_multi_perform(multi, &still_running); + + /* only read 'still_running' if curl_multi_perform() return OK */ + if(!mcode && !still_running) { + int rc; + CURLMsg *msg = curl_multi_info_read(multi, &rc); + if(msg) { + result = msg->data.result; + done = TRUE; + } + } + } + + /* Make sure to return some kind of error if there was a multi problem */ + if(mcode) { + result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY : + /* The other multi errors should never happen, so return + something suitably generic */ + CURLE_BAD_FUNCTION_ARGUMENT; + } + + return result; +} + + +/* + * easy_perform() is the external interface that performs a blocking + * transfer as previously setup. + * + * CONCEPT: This function creates a multi handle, adds the easy handle to it, + * runs curl_multi_perform() until the transfer is done, then detaches the + * easy handle, destroys the multi handle and returns the easy handle's return + * code. + * + * REALITY: it cannot just create and destroy the multi handle that easily. It + * needs to keep it around since if this easy handle is used again by this + * function, the same multi handle must be reused so that the same pools and + * caches can be used. + * + * DEBUG: if 'events' is set TRUE, this function will use a replacement engine + * instead of curl_multi_perform() and use curl_multi_socket_action(). + */ +static CURLcode easy_perform(struct Curl_easy *data, bool events) +{ + struct Curl_multi *multi; + CURLMcode mcode; + CURLcode result = CURLE_OK; + SIGPIPE_VARIABLE(pipe_st); + + if(!data) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(data->set.errorbuffer) + /* clear this as early as possible */ + data->set.errorbuffer[0] = 0; + + data->state.os_errno = 0; + + if(data->multi) { + failf(data, "easy handle already used in multi handle"); + return CURLE_FAILED_INIT; + } + + if(data->multi_easy) + multi = data->multi_easy; + else { + /* this multi handle will only ever have a single easy handled attached + to it, so make it use minimal hashes */ + multi = Curl_multi_handle(1, 3, 7); + if(!multi) + return CURLE_OUT_OF_MEMORY; + } + + if(multi->in_callback) + return CURLE_RECURSIVE_API_CALL; + + /* Copy the MAXCONNECTS option to the multi handle */ + curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects); + + data->multi_easy = NULL; /* pretend it does not exist */ + mcode = curl_multi_add_handle(multi, data); + if(mcode) { + curl_multi_cleanup(multi); + if(mcode == CURLM_OUT_OF_MEMORY) + return CURLE_OUT_OF_MEMORY; + return CURLE_FAILED_INIT; + } + + /* assign this after curl_multi_add_handle() */ + data->multi_easy = multi; + + sigpipe_init(&pipe_st); + sigpipe_apply(data, &pipe_st); + + /* run the transfer */ + result = events ? easy_events(multi) : easy_transfer(multi); + + /* ignoring the return code is not nice, but atm we cannot really handle + a failure here, room for future improvement! */ + (void)curl_multi_remove_handle(multi, data); + + sigpipe_restore(&pipe_st); + + /* The multi handle is kept alive, owned by the easy handle */ + return result; +} + + +/* + * curl_easy_perform() is the external interface that performs a blocking + * transfer as previously setup. + */ +CURLcode curl_easy_perform(struct Curl_easy *data) +{ + return easy_perform(data, FALSE); +} + +#ifdef DEBUGBUILD +/* + * curl_easy_perform_ev() is the external interface that performs a blocking + * transfer using the event-based API internally. + */ +CURLcode curl_easy_perform_ev(struct Curl_easy *data) +{ + return easy_perform(data, TRUE); +} + +#endif + +/* + * curl_easy_cleanup() is the external interface to cleaning/freeing the given + * easy handle. + */ +void curl_easy_cleanup(struct Curl_easy *data) +{ + if(GOOD_EASY_HANDLE(data)) { + SIGPIPE_VARIABLE(pipe_st); + sigpipe_ignore(data, &pipe_st); + Curl_close(&data); + sigpipe_restore(&pipe_st); + } +} + +/* + * curl_easy_getinfo() is an external interface that allows an app to retrieve + * information from a performed transfer and similar. + */ +#undef curl_easy_getinfo +CURLcode curl_easy_getinfo(struct Curl_easy *data, CURLINFO info, ...) +{ + va_list arg; + void *paramp; + CURLcode result; + + va_start(arg, info); + paramp = va_arg(arg, void *); + + result = Curl_getinfo(data, info, paramp); + + va_end(arg); + return result; +} + +static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) +{ + CURLcode result = CURLE_OK; + enum dupstring i; + enum dupblob j; + + /* Copy src->set into dst->set first, then deal with the strings + afterwards */ + dst->set = src->set; + Curl_mime_initpart(&dst->set.mimepost); + + /* clear all dest string and blob pointers first, in case we error out + mid-function */ + memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); + memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); + + /* duplicate all strings */ + for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { + result = Curl_setstropt(&dst->set.str[i], src->set.str[i]); + if(result) + return result; + } + + /* duplicate all blobs */ + for(j = (enum dupblob)0; j < BLOB_LAST; j++) { + result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]); + if(result) + return result; + } + + /* duplicate memory areas pointed to */ + i = STRING_COPYPOSTFIELDS; + if(src->set.str[i]) { + if(src->set.postfieldsize == -1) + dst->set.str[i] = strdup(src->set.str[i]); + else + /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ + dst->set.str[i] = Curl_memdup(src->set.str[i], + curlx_sotouz(src->set.postfieldsize)); + if(!dst->set.str[i]) + return CURLE_OUT_OF_MEMORY; + /* point to the new copy */ + dst->set.postfields = dst->set.str[i]; + } + + /* Duplicate mime data. */ + result = Curl_mime_duppart(dst, &dst->set.mimepost, &src->set.mimepost); + + if(src->set.resolve) + dst->state.resolve = dst->set.resolve; + + return result; +} + +/* + * curl_easy_duphandle() is an external interface to allow duplication of a + * given input easy handle. The returned handle will be a new working handle + * with all options set exactly as the input source handle. + */ +struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) +{ + struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy)); + if(!outcurl) + goto fail; + + /* + * We setup a few buffers we need. We should probably make them + * get setup on-demand in the code, as that would probably decrease + * the likeliness of us forgetting to init a buffer here in the future. + */ + outcurl->set.buffer_size = data->set.buffer_size; + + /* copy all userdefined values */ + if(dupset(outcurl, data)) + goto fail; + + Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); + + /* the connection cache is setup on demand */ + outcurl->state.conn_cache = NULL; + outcurl->state.lastconnect_id = -1; + outcurl->state.recent_conn_id = -1; + outcurl->id = -1; + + outcurl->progress.flags = data->progress.flags; + outcurl->progress.callback = data->progress.callback; + +#ifndef CURL_DISABLE_COOKIES + outcurl->state.cookielist = NULL; + if(data->cookies && data->state.cookie_engine) { + /* If cookies are enabled in the parent handle, we enable them + in the clone as well! */ + outcurl->cookies = Curl_cookie_init(outcurl, NULL, outcurl->cookies, + data->set.cookiesession); + if(!outcurl->cookies) + goto fail; + } + + if(data->state.cookielist) { + outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); + if(!outcurl->state.cookielist) + goto fail; + } +#endif + + if(data->state.url) { + outcurl->state.url = strdup(data->state.url); + if(!outcurl->state.url) + goto fail; + outcurl->state.url_alloc = TRUE; + } + + if(data->state.referer) { + outcurl->state.referer = strdup(data->state.referer); + if(!outcurl->state.referer) + goto fail; + outcurl->state.referer_alloc = TRUE; + } + + /* Reinitialize an SSL engine for the new handle + * note: the engine name has already been copied by dupset */ + if(outcurl->set.str[STRING_SSL_ENGINE]) { + if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE])) + goto fail; + } + +#ifndef CURL_DISABLE_ALTSVC + if(data->asi) { + outcurl->asi = Curl_altsvc_init(); + if(!outcurl->asi) + goto fail; + if(outcurl->set.str[STRING_ALTSVC]) + (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]); + } +#endif +#ifndef CURL_DISABLE_HSTS + if(data->hsts) { + outcurl->hsts = Curl_hsts_init(); + if(!outcurl->hsts) + goto fail; + if(outcurl->set.str[STRING_HSTS]) + (void)Curl_hsts_loadfile(outcurl, + outcurl->hsts, outcurl->set.str[STRING_HSTS]); + (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); + } +#endif + +#ifdef CURLRES_ASYNCH + /* Clone the resolver handle, if present, for the new handle */ + if(Curl_resolver_duphandle(outcurl, + &outcurl->state.async.resolver, + data->state.async.resolver)) + goto fail; +#endif + +#ifdef USE_ARES + { + CURLcode rc; + + rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]); + if(rc && rc != CURLE_NOT_BUILT_IN) + goto fail; + + rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]); + if(rc && rc != CURLE_NOT_BUILT_IN) + goto fail; + + rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]); + if(rc && rc != CURLE_NOT_BUILT_IN) + goto fail; + + rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]); + if(rc && rc != CURLE_NOT_BUILT_IN) + goto fail; + } +#endif /* USE_ARES */ + + Curl_initinfo(outcurl); + + outcurl->magic = CURLEASY_MAGIC_NUMBER; + + /* we reach this point and thus we are OK */ + + return outcurl; + +fail: + + if(outcurl) { +#ifndef CURL_DISABLE_COOKIES + free(outcurl->cookies); +#endif + Curl_dyn_free(&outcurl->state.headerb); + Curl_altsvc_cleanup(&outcurl->asi); + Curl_hsts_cleanup(&outcurl->hsts); + Curl_freeset(outcurl); + free(outcurl); + } + + return NULL; +} + +/* + * curl_easy_reset() is an external interface that allows an app to re- + * initialize a session handle to the default values. + */ +void curl_easy_reset(struct Curl_easy *data) +{ + Curl_req_hard_reset(&data->req, data); + + /* zero out UserDefined data: */ + Curl_freeset(data); + memset(&data->set, 0, sizeof(struct UserDefined)); + (void)Curl_init_userdefined(data); + + /* zero out Progress data: */ + memset(&data->progress, 0, sizeof(struct Progress)); + + /* zero out PureInfo data: */ + Curl_initinfo(data); + + data->progress.flags |= PGRS_HIDE; + data->state.current_speed = -1; /* init to negative == impossible */ + data->state.retrycount = 0; /* reset the retry counter */ + + /* zero out authentication data: */ + memset(&data->state.authhost, 0, sizeof(struct auth)); + memset(&data->state.authproxy, 0, sizeof(struct auth)); + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) + Curl_http_auth_cleanup_digest(data); +#endif +} + +/* + * curl_easy_pause() allows an application to pause or unpause a specific + * transfer and direction. This function sets the full new state for the + * current connection this easy handle operates on. + * + * NOTE: if you have the receiving paused and you call this function to remove + * the pausing, you may get your write callback called at this point. + * + * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h + * + * NOTE: This is one of few API functions that are allowed to be called from + * within a callback. + */ +CURLcode curl_easy_pause(struct Curl_easy *data, int action) +{ + struct SingleRequest *k; + CURLcode result = CURLE_OK; + int oldstate; + int newstate; + bool recursive = FALSE; + bool keep_changed, unpause_read, not_all_paused; + + if(!GOOD_EASY_HANDLE(data) || !data->conn) + /* crazy input, do not continue */ + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(Curl_is_in_callback(data)) + recursive = TRUE; + k = &data->req; + oldstate = k->keepon & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE); + + /* first switch off both pause bits then set the new pause bits */ + newstate = (k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) | + ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) | + ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0); + + keep_changed = ((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) != oldstate); + not_all_paused = (newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != + (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE); + unpause_read = ((k->keepon & ~newstate & KEEP_SEND_PAUSE) && + (data->mstate == MSTATE_PERFORMING || + data->mstate == MSTATE_RATELIMITING)); + /* Unpausing writes is detected on the next run in + * transfer.c:Curl_readwrite(). This is because this may result + * in a transfer error if the application's callbacks fail */ + + /* Set the new keepon state, so it takes effect no matter what error + * may happen afterwards. */ + k->keepon = newstate; + + /* If not completely pausing both directions now, run again in any case. */ + if(not_all_paused) { + Curl_expire(data, 0, EXPIRE_RUN_NOW); + /* reset the too-slow time keeper */ + data->state.keeps_speed.tv_sec = 0; + /* Simulate socket events on next run for unpaused directions */ + if(!(newstate & KEEP_SEND_PAUSE)) + data->state.select_bits |= CURL_CSELECT_OUT; + if(!(newstate & KEEP_RECV_PAUSE)) + data->state.select_bits |= CURL_CSELECT_IN; + /* On changes, tell application to update its timers. */ + if(keep_changed && data->multi) { + if(Curl_update_timer(data->multi)) { + result = CURLE_ABORTED_BY_CALLBACK; + goto out; + } + } + } + + if(unpause_read) { + result = Curl_creader_unpause(data); + if(result) + goto out; + } + + if(!(k->keepon & KEEP_RECV_PAUSE) && Curl_cwriter_is_paused(data)) { + Curl_conn_ev_data_pause(data, FALSE); + result = Curl_cwriter_unpause(data); + } + +out: + if(!result && !data->state.done && keep_changed) + /* This transfer may have been moved in or out of the bundle, update the + corresponding socket callback, if used */ + result = Curl_updatesocket(data); + + if(recursive) + /* this might have called a callback recursively which might have set this + to false again on exit */ + Curl_set_in_callback(data, TRUE); + + return result; +} + + +static CURLcode easy_connection(struct Curl_easy *data, + struct connectdata **connp) +{ + curl_socket_t sfd; + + if(!data) + return CURLE_BAD_FUNCTION_ARGUMENT; + + /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */ + if(!data->set.connect_only) { + failf(data, "CONNECT_ONLY is required"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + + sfd = Curl_getconnectinfo(data, connp); + + if(sfd == CURL_SOCKET_BAD) { + failf(data, "Failed to get recent socket"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + + return CURLE_OK; +} + +/* + * Receives data from the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + * Returns CURLE_OK on success, error code on error. + */ +CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, + size_t *n) +{ + CURLcode result; + ssize_t n1; + struct connectdata *c; + + if(Curl_is_in_callback(data)) + return CURLE_RECURSIVE_API_CALL; + + result = easy_connection(data, &c); + if(result) + return result; + + if(!data->conn) + /* on first invoke, the transfer has been detached from the connection and + needs to be reattached */ + Curl_attach_connection(data, c); + + *n = 0; + result = Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, &n1); + + if(result) + return result; + + *n = (size_t)n1; + return CURLE_OK; +} + +#ifdef USE_WEBSOCKETS +CURLcode Curl_connect_only_attach(struct Curl_easy *data) +{ + CURLcode result; + struct connectdata *c = NULL; + + result = easy_connection(data, &c); + if(result) + return result; + + if(!data->conn) + /* on first invoke, the transfer has been detached from the connection and + needs to be reattached */ + Curl_attach_connection(data, c); + + return CURLE_OK; +} +#endif /* USE_WEBSOCKETS */ + +/* + * Sends data over the connected socket. + * + * This is the private internal version of curl_easy_send() + */ +CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, + size_t buflen, size_t *n) +{ + CURLcode result; + struct connectdata *c = NULL; + SIGPIPE_VARIABLE(pipe_st); + + *n = 0; + result = easy_connection(data, &c); + if(result) + return result; + + if(!data->conn) + /* on first invoke, the transfer has been detached from the connection and + needs to be reattached */ + Curl_attach_connection(data, c); + + sigpipe_ignore(data, &pipe_st); + result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, n); + sigpipe_restore(&pipe_st); + + if(result && result != CURLE_AGAIN) + return CURLE_SEND_ERROR; + return result; +} + +/* + * Sends data over the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, + size_t buflen, size_t *n) +{ + size_t written = 0; + CURLcode result; + if(Curl_is_in_callback(data)) + return CURLE_RECURSIVE_API_CALL; + + result = Curl_senddata(data, buffer, buflen, &written); + *n = written; + return result; +} + +/* + * Wrapper to call functions in Curl_conncache_foreach() + * + * Returns always 0. + */ +static int conn_upkeep(struct Curl_easy *data, + struct connectdata *conn, + void *param) +{ + struct curltime *now = param; + + if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms) + return 0; + + /* briefly attach for action */ + Curl_attach_connection(data, conn); + if(conn->handler->connection_check) { + /* Do a protocol-specific keepalive check on the connection. */ + conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); + } + else { + /* Do the generic action on the FIRSTSOCKET filter chain */ + Curl_conn_keep_alive(data, conn, FIRSTSOCKET); + } + Curl_detach_connection(data); + + conn->keepalive = *now; + return 0; /* continue iteration */ +} + +static CURLcode upkeep(struct conncache *conn_cache, void *data) +{ + struct curltime now = Curl_now(); + /* Loop over every connection and make connection alive. */ + Curl_conncache_foreach(data, + conn_cache, + &now, + conn_upkeep); + return CURLE_OK; +} + +/* + * Performs connection upkeep for the given session handle. + */ +CURLcode curl_easy_upkeep(struct Curl_easy *data) +{ + /* Verify that we got an easy handle we can work with. */ + if(!GOOD_EASY_HANDLE(data)) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(data->multi_easy) { + /* Use the common function to keep connections alive. */ + return upkeep(&data->multi_easy->conn_cache, data); + } + else { + /* No connections, so just return success */ + return CURLE_OK; + } +} diff --git a/third_party/curl/lib/easy_lock.h b/third_party/curl/lib/easy_lock.h new file mode 100644 index 0000000000..4f6764d427 --- /dev/null +++ b/third_party/curl/lib/easy_lock.h @@ -0,0 +1,111 @@ +#ifndef HEADER_CURL_EASY_LOCK_H +#define HEADER_CURL_EASY_LOCK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#define GLOBAL_INIT_IS_THREADSAFE + +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 + +#ifdef __MINGW32__ +#ifndef SRWLOCK_INIT +#define SRWLOCK_INIT NULL +#endif +#endif /* __MINGW32__ */ + +#define curl_simple_lock SRWLOCK +#define CURL_SIMPLE_LOCK_INIT SRWLOCK_INIT + +#define curl_simple_lock_lock(m) AcquireSRWLockExclusive(m) +#define curl_simple_lock_unlock(m) ReleaseSRWLockExclusive(m) + +#elif defined(HAVE_ATOMIC) && defined(HAVE_STDATOMIC_H) +#include +#if defined(HAVE_SCHED_YIELD) +#include +#endif + +#define curl_simple_lock atomic_int +#define CURL_SIMPLE_LOCK_INIT 0 + +/* a clang-thing */ +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifndef __INTEL_COMPILER +/* The Intel compiler tries to look like GCC *and* clang *and* lies in its + __has_builtin() function, so override it. */ + +/* if GCC on i386/x86_64 or if the built-in is present */ +#if ( (defined(__GNUC__) && !defined(__clang__)) && \ + (defined(__i386__) || defined(__x86_64__))) || \ + __has_builtin(__builtin_ia32_pause) +#define HAVE_BUILTIN_IA32_PAUSE +#endif + +#endif + +static inline void curl_simple_lock_lock(curl_simple_lock *lock) +{ + for(;;) { + if(!atomic_exchange_explicit(lock, true, memory_order_acquire)) + break; + /* Reduce cache coherency traffic */ + while(atomic_load_explicit(lock, memory_order_relaxed)) { + /* Reduce load (not mandatory) */ +#ifdef HAVE_BUILTIN_IA32_PAUSE + __builtin_ia32_pause(); +#elif defined(__aarch64__) + __asm__ volatile("yield" ::: "memory"); +#elif defined(HAVE_SCHED_YIELD) + sched_yield(); +#endif + } + } +} + +static inline void curl_simple_lock_unlock(curl_simple_lock *lock) +{ + atomic_store_explicit(lock, false, memory_order_release); +} + +#elif defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) + +#include + +#define curl_simple_lock pthread_mutex_t +#define CURL_SIMPLE_LOCK_INIT PTHREAD_MUTEX_INITIALIZER +#define curl_simple_lock_lock(m) pthread_mutex_lock(m) +#define curl_simple_lock_unlock(m) pthread_mutex_unlock(m) + +#else + +#undef GLOBAL_INIT_IS_THREADSAFE + +#endif + +#endif /* HEADER_CURL_EASY_LOCK_H */ diff --git a/third_party/curl/lib/easygetopt.c b/third_party/curl/lib/easygetopt.c new file mode 100644 index 0000000000..86833bf6b9 --- /dev/null +++ b/third_party/curl/lib/easygetopt.c @@ -0,0 +1,98 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ | | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * ___|___/|_| ______| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "strcase.h" +#include "easyoptions.h" + +#ifndef CURL_DISABLE_GETOPTIONS + +/* Lookups easy options at runtime */ +static struct curl_easyoption *lookup(const char *name, CURLoption id) +{ + DEBUGASSERT(name || id); + DEBUGASSERT(!Curl_easyopts_check()); + if(name || id) { + struct curl_easyoption *o = &Curl_easyopts[0]; + do { + if(name) { + if(strcasecompare(o->name, name)) + return o; + } + else { + if((o->id == id) && !(o->flags & CURLOT_FLAG_ALIAS)) + /* do not match alias options */ + return o; + } + o++; + } while(o->name); + } + return NULL; +} + +const struct curl_easyoption *curl_easy_option_by_name(const char *name) +{ + /* when name is used, the id argument is ignored */ + return lookup(name, CURLOPT_LASTENTRY); +} + +const struct curl_easyoption *curl_easy_option_by_id(CURLoption id) +{ + return lookup(NULL, id); +} + +/* Iterates over available options */ +const struct curl_easyoption * +curl_easy_option_next(const struct curl_easyoption *prev) +{ + if(prev && prev->name) { + prev++; + if(prev->name) + return prev; + } + else if(!prev) + return &Curl_easyopts[0]; + return NULL; +} + +#else +const struct curl_easyoption *curl_easy_option_by_name(const char *name) +{ + (void)name; + return NULL; +} + +const struct curl_easyoption *curl_easy_option_by_id (CURLoption id) +{ + (void)id; + return NULL; +} + +const struct curl_easyoption * +curl_easy_option_next(const struct curl_easyoption *prev) +{ + (void)prev; + return NULL; +} +#endif diff --git a/third_party/curl/lib/easyif.h b/third_party/curl/lib/easyif.h new file mode 100644 index 0000000000..d77bb98f92 --- /dev/null +++ b/third_party/curl/lib/easyif.h @@ -0,0 +1,41 @@ +#ifndef HEADER_CURL_EASYIF_H +#define HEADER_CURL_EASYIF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Prototypes for library-wide functions provided by easy.c + */ +CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, + size_t buflen, size_t *n); + +#ifdef USE_WEBSOCKETS +CURLcode Curl_connect_only_attach(struct Curl_easy *data); +#endif + +#ifdef DEBUGBUILD +CURL_EXTERN CURLcode curl_easy_perform_ev(struct Curl_easy *easy); +#endif + +#endif /* HEADER_CURL_EASYIF_H */ diff --git a/third_party/curl/lib/easyoptions.c b/third_party/curl/lib/easyoptions.c new file mode 100644 index 0000000000..81091c405a --- /dev/null +++ b/third_party/curl/lib/easyoptions.c @@ -0,0 +1,382 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* This source code is generated by optiontable.pl - DO NOT EDIT BY HAND */ + +#include "curl_setup.h" +#include "easyoptions.h" + +/* all easy setopt options listed in alphabetical order */ +struct curl_easyoption Curl_easyopts[] = { + {"ABSTRACT_UNIX_SOCKET", CURLOPT_ABSTRACT_UNIX_SOCKET, CURLOT_STRING, 0}, + {"ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS, CURLOT_LONG, 0}, + {"ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, 0}, + {"ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE, CURLOT_LONG, 0}, + {"ALTSVC", CURLOPT_ALTSVC, CURLOT_STRING, 0}, + {"ALTSVC_CTRL", CURLOPT_ALTSVC_CTRL, CURLOT_LONG, 0}, + {"APPEND", CURLOPT_APPEND, CURLOT_LONG, 0}, + {"AUTOREFERER", CURLOPT_AUTOREFERER, CURLOT_LONG, 0}, + {"AWS_SIGV4", CURLOPT_AWS_SIGV4, CURLOT_STRING, 0}, + {"BUFFERSIZE", CURLOPT_BUFFERSIZE, CURLOT_LONG, 0}, + {"CAINFO", CURLOPT_CAINFO, CURLOT_STRING, 0}, + {"CAINFO_BLOB", CURLOPT_CAINFO_BLOB, CURLOT_BLOB, 0}, + {"CAPATH", CURLOPT_CAPATH, CURLOT_STRING, 0}, + {"CA_CACHE_TIMEOUT", CURLOPT_CA_CACHE_TIMEOUT, CURLOT_LONG, 0}, + {"CERTINFO", CURLOPT_CERTINFO, CURLOT_LONG, 0}, + {"CHUNK_BGN_FUNCTION", CURLOPT_CHUNK_BGN_FUNCTION, CURLOT_FUNCTION, 0}, + {"CHUNK_DATA", CURLOPT_CHUNK_DATA, CURLOT_CBPTR, 0}, + {"CHUNK_END_FUNCTION", CURLOPT_CHUNK_END_FUNCTION, CURLOT_FUNCTION, 0}, + {"CLOSESOCKETDATA", CURLOPT_CLOSESOCKETDATA, CURLOT_CBPTR, 0}, + {"CLOSESOCKETFUNCTION", CURLOPT_CLOSESOCKETFUNCTION, CURLOT_FUNCTION, 0}, + {"CONNECTTIMEOUT", CURLOPT_CONNECTTIMEOUT, CURLOT_LONG, 0}, + {"CONNECTTIMEOUT_MS", CURLOPT_CONNECTTIMEOUT_MS, CURLOT_LONG, 0}, + {"CONNECT_ONLY", CURLOPT_CONNECT_ONLY, CURLOT_LONG, 0}, + {"CONNECT_TO", CURLOPT_CONNECT_TO, CURLOT_SLIST, 0}, + {"CONV_FROM_NETWORK_FUNCTION", CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOT_FUNCTION, 0}, + {"CONV_FROM_UTF8_FUNCTION", CURLOPT_CONV_FROM_UTF8_FUNCTION, + CURLOT_FUNCTION, 0}, + {"CONV_TO_NETWORK_FUNCTION", CURLOPT_CONV_TO_NETWORK_FUNCTION, + CURLOT_FUNCTION, 0}, + {"COOKIE", CURLOPT_COOKIE, CURLOT_STRING, 0}, + {"COOKIEFILE", CURLOPT_COOKIEFILE, CURLOT_STRING, 0}, + {"COOKIEJAR", CURLOPT_COOKIEJAR, CURLOT_STRING, 0}, + {"COOKIELIST", CURLOPT_COOKIELIST, CURLOT_STRING, 0}, + {"COOKIESESSION", CURLOPT_COOKIESESSION, CURLOT_LONG, 0}, + {"COPYPOSTFIELDS", CURLOPT_COPYPOSTFIELDS, CURLOT_OBJECT, 0}, + {"CRLF", CURLOPT_CRLF, CURLOT_LONG, 0}, + {"CRLFILE", CURLOPT_CRLFILE, CURLOT_STRING, 0}, + {"CURLU", CURLOPT_CURLU, CURLOT_OBJECT, 0}, + {"CUSTOMREQUEST", CURLOPT_CUSTOMREQUEST, CURLOT_STRING, 0}, + {"DEBUGDATA", CURLOPT_DEBUGDATA, CURLOT_CBPTR, 0}, + {"DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION, CURLOT_FUNCTION, 0}, + {"DEFAULT_PROTOCOL", CURLOPT_DEFAULT_PROTOCOL, CURLOT_STRING, 0}, + {"DIRLISTONLY", CURLOPT_DIRLISTONLY, CURLOT_LONG, 0}, + {"DISALLOW_USERNAME_IN_URL", CURLOPT_DISALLOW_USERNAME_IN_URL, + CURLOT_LONG, 0}, + {"DNS_CACHE_TIMEOUT", CURLOPT_DNS_CACHE_TIMEOUT, CURLOT_LONG, 0}, + {"DNS_INTERFACE", CURLOPT_DNS_INTERFACE, CURLOT_STRING, 0}, + {"DNS_LOCAL_IP4", CURLOPT_DNS_LOCAL_IP4, CURLOT_STRING, 0}, + {"DNS_LOCAL_IP6", CURLOPT_DNS_LOCAL_IP6, CURLOT_STRING, 0}, + {"DNS_SERVERS", CURLOPT_DNS_SERVERS, CURLOT_STRING, 0}, + {"DNS_SHUFFLE_ADDRESSES", CURLOPT_DNS_SHUFFLE_ADDRESSES, CURLOT_LONG, 0}, + {"DNS_USE_GLOBAL_CACHE", CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOT_LONG, 0}, + {"DOH_SSL_VERIFYHOST", CURLOPT_DOH_SSL_VERIFYHOST, CURLOT_LONG, 0}, + {"DOH_SSL_VERIFYPEER", CURLOPT_DOH_SSL_VERIFYPEER, CURLOT_LONG, 0}, + {"DOH_SSL_VERIFYSTATUS", CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOT_LONG, 0}, + {"DOH_URL", CURLOPT_DOH_URL, CURLOT_STRING, 0}, + {"ECH", CURLOPT_ECH, CURLOT_STRING, 0}, + {"EGDSOCKET", CURLOPT_EGDSOCKET, CURLOT_STRING, 0}, + {"ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, CURLOT_FLAG_ALIAS}, + {"ERRORBUFFER", CURLOPT_ERRORBUFFER, CURLOT_OBJECT, 0}, + {"EXPECT_100_TIMEOUT_MS", CURLOPT_EXPECT_100_TIMEOUT_MS, CURLOT_LONG, 0}, + {"FAILONERROR", CURLOPT_FAILONERROR, CURLOT_LONG, 0}, + {"FILE", CURLOPT_WRITEDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, + {"FILETIME", CURLOPT_FILETIME, CURLOT_LONG, 0}, + {"FNMATCH_DATA", CURLOPT_FNMATCH_DATA, CURLOT_CBPTR, 0}, + {"FNMATCH_FUNCTION", CURLOPT_FNMATCH_FUNCTION, CURLOT_FUNCTION, 0}, + {"FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION, CURLOT_LONG, 0}, + {"FORBID_REUSE", CURLOPT_FORBID_REUSE, CURLOT_LONG, 0}, + {"FRESH_CONNECT", CURLOPT_FRESH_CONNECT, CURLOT_LONG, 0}, + {"FTPAPPEND", CURLOPT_APPEND, CURLOT_LONG, CURLOT_FLAG_ALIAS}, + {"FTPLISTONLY", CURLOPT_DIRLISTONLY, CURLOT_LONG, CURLOT_FLAG_ALIAS}, + {"FTPPORT", CURLOPT_FTPPORT, CURLOT_STRING, 0}, + {"FTPSSLAUTH", CURLOPT_FTPSSLAUTH, CURLOT_VALUES, 0}, + {"FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT, CURLOT_STRING, 0}, + {"FTP_ALTERNATIVE_TO_USER", CURLOPT_FTP_ALTERNATIVE_TO_USER, + CURLOT_STRING, 0}, + {"FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS, + CURLOT_LONG, 0}, + {"FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD, CURLOT_VALUES, 0}, + {"FTP_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, + CURLOT_LONG, CURLOT_FLAG_ALIAS}, + {"FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP, CURLOT_LONG, 0}, + {"FTP_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, CURLOT_FLAG_ALIAS}, + {"FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC, CURLOT_LONG, 0}, + {"FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT, CURLOT_LONG, 0}, + {"FTP_USE_EPSV", CURLOPT_FTP_USE_EPSV, CURLOT_LONG, 0}, + {"FTP_USE_PRET", CURLOPT_FTP_USE_PRET, CURLOT_LONG, 0}, + {"GSSAPI_DELEGATION", CURLOPT_GSSAPI_DELEGATION, CURLOT_VALUES, 0}, + {"HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, + CURLOT_LONG, 0}, + {"HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CURLOT_LONG, 0}, + {"HAPROXY_CLIENT_IP", CURLOPT_HAPROXY_CLIENT_IP, CURLOT_STRING, 0}, + {"HEADER", CURLOPT_HEADER, CURLOT_LONG, 0}, + {"HEADERDATA", CURLOPT_HEADERDATA, CURLOT_CBPTR, 0}, + {"HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0}, + {"HEADEROPT", CURLOPT_HEADEROPT, CURLOT_VALUES, 0}, + {"HSTS", CURLOPT_HSTS, CURLOT_STRING, 0}, + {"HSTSREADDATA", CURLOPT_HSTSREADDATA, CURLOT_CBPTR, 0}, + {"HSTSREADFUNCTION", CURLOPT_HSTSREADFUNCTION, CURLOT_FUNCTION, 0}, + {"HSTSWRITEDATA", CURLOPT_HSTSWRITEDATA, CURLOT_CBPTR, 0}, + {"HSTSWRITEFUNCTION", CURLOPT_HSTSWRITEFUNCTION, CURLOT_FUNCTION, 0}, + {"HSTS_CTRL", CURLOPT_HSTS_CTRL, CURLOT_LONG, 0}, + {"HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED, CURLOT_LONG, 0}, + {"HTTP200ALIASES", CURLOPT_HTTP200ALIASES, CURLOT_SLIST, 0}, + {"HTTPAUTH", CURLOPT_HTTPAUTH, CURLOT_VALUES, 0}, + {"HTTPGET", CURLOPT_HTTPGET, CURLOT_LONG, 0}, + {"HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0}, + {"HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0}, + {"HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0}, + {"HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING, CURLOT_LONG, 0}, + {"HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING, CURLOT_LONG, 0}, + {"HTTP_VERSION", CURLOPT_HTTP_VERSION, CURLOT_VALUES, 0}, + {"IGNORE_CONTENT_LENGTH", CURLOPT_IGNORE_CONTENT_LENGTH, CURLOT_LONG, 0}, + {"INFILE", CURLOPT_READDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, + {"INFILESIZE", CURLOPT_INFILESIZE, CURLOT_LONG, 0}, + {"INFILESIZE_LARGE", CURLOPT_INFILESIZE_LARGE, CURLOT_OFF_T, 0}, + {"INTERFACE", CURLOPT_INTERFACE, CURLOT_STRING, 0}, + {"INTERLEAVEDATA", CURLOPT_INTERLEAVEDATA, CURLOT_CBPTR, 0}, + {"INTERLEAVEFUNCTION", CURLOPT_INTERLEAVEFUNCTION, CURLOT_FUNCTION, 0}, + {"IOCTLDATA", CURLOPT_IOCTLDATA, CURLOT_CBPTR, 0}, + {"IOCTLFUNCTION", CURLOPT_IOCTLFUNCTION, CURLOT_FUNCTION, 0}, + {"IPRESOLVE", CURLOPT_IPRESOLVE, CURLOT_VALUES, 0}, + {"ISSUERCERT", CURLOPT_ISSUERCERT, CURLOT_STRING, 0}, + {"ISSUERCERT_BLOB", CURLOPT_ISSUERCERT_BLOB, CURLOT_BLOB, 0}, + {"KEEP_SENDING_ON_ERROR", CURLOPT_KEEP_SENDING_ON_ERROR, CURLOT_LONG, 0}, + {"KEYPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, 0}, + {"KRB4LEVEL", CURLOPT_KRBLEVEL, CURLOT_STRING, CURLOT_FLAG_ALIAS}, + {"KRBLEVEL", CURLOPT_KRBLEVEL, CURLOT_STRING, 0}, + {"LOCALPORT", CURLOPT_LOCALPORT, CURLOT_LONG, 0}, + {"LOCALPORTRANGE", CURLOPT_LOCALPORTRANGE, CURLOT_LONG, 0}, + {"LOGIN_OPTIONS", CURLOPT_LOGIN_OPTIONS, CURLOT_STRING, 0}, + {"LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT, CURLOT_LONG, 0}, + {"LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME, CURLOT_LONG, 0}, + {"MAIL_AUTH", CURLOPT_MAIL_AUTH, CURLOT_STRING, 0}, + {"MAIL_FROM", CURLOPT_MAIL_FROM, CURLOT_STRING, 0}, + {"MAIL_RCPT", CURLOPT_MAIL_RCPT, CURLOT_SLIST, 0}, + {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, + CURLOT_LONG, CURLOT_FLAG_ALIAS}, + {"MAIL_RCPT_ALLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOT_LONG, 0}, + {"MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CURLOT_LONG, 0}, + {"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0}, + {"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0}, + {"MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE, CURLOT_OFF_T, 0}, + {"MAXLIFETIME_CONN", CURLOPT_MAXLIFETIME_CONN, CURLOT_LONG, 0}, + {"MAXREDIRS", CURLOPT_MAXREDIRS, CURLOT_LONG, 0}, + {"MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE, CURLOT_OFF_T, 0}, + {"MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CURLOT_OFF_T, 0}, + {"MIMEPOST", CURLOPT_MIMEPOST, CURLOT_OBJECT, 0}, + {"MIME_OPTIONS", CURLOPT_MIME_OPTIONS, CURLOT_LONG, 0}, + {"NETRC", CURLOPT_NETRC, CURLOT_VALUES, 0}, + {"NETRC_FILE", CURLOPT_NETRC_FILE, CURLOT_STRING, 0}, + {"NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS, CURLOT_LONG, 0}, + {"NEW_FILE_PERMS", CURLOPT_NEW_FILE_PERMS, CURLOT_LONG, 0}, + {"NOBODY", CURLOPT_NOBODY, CURLOT_LONG, 0}, + {"NOPROGRESS", CURLOPT_NOPROGRESS, CURLOT_LONG, 0}, + {"NOPROXY", CURLOPT_NOPROXY, CURLOT_STRING, 0}, + {"NOSIGNAL", CURLOPT_NOSIGNAL, CURLOT_LONG, 0}, + {"OPENSOCKETDATA", CURLOPT_OPENSOCKETDATA, CURLOT_CBPTR, 0}, + {"OPENSOCKETFUNCTION", CURLOPT_OPENSOCKETFUNCTION, CURLOT_FUNCTION, 0}, + {"PASSWORD", CURLOPT_PASSWORD, CURLOT_STRING, 0}, + {"PATH_AS_IS", CURLOPT_PATH_AS_IS, CURLOT_LONG, 0}, + {"PINNEDPUBLICKEY", CURLOPT_PINNEDPUBLICKEY, CURLOT_STRING, 0}, + {"PIPEWAIT", CURLOPT_PIPEWAIT, CURLOT_LONG, 0}, + {"PORT", CURLOPT_PORT, CURLOT_LONG, 0}, + {"POST", CURLOPT_POST, CURLOT_LONG, 0}, + {"POST301", CURLOPT_POSTREDIR, CURLOT_VALUES, CURLOT_FLAG_ALIAS}, + {"POSTFIELDS", CURLOPT_POSTFIELDS, CURLOT_OBJECT, 0}, + {"POSTFIELDSIZE", CURLOPT_POSTFIELDSIZE, CURLOT_LONG, 0}, + {"POSTFIELDSIZE_LARGE", CURLOPT_POSTFIELDSIZE_LARGE, CURLOT_OFF_T, 0}, + {"POSTQUOTE", CURLOPT_POSTQUOTE, CURLOT_SLIST, 0}, + {"POSTREDIR", CURLOPT_POSTREDIR, CURLOT_VALUES, 0}, + {"PREQUOTE", CURLOPT_PREQUOTE, CURLOT_SLIST, 0}, + {"PREREQDATA", CURLOPT_PREREQDATA, CURLOT_CBPTR, 0}, + {"PREREQFUNCTION", CURLOPT_PREREQFUNCTION, CURLOT_FUNCTION, 0}, + {"PRE_PROXY", CURLOPT_PRE_PROXY, CURLOT_STRING, 0}, + {"PRIVATE", CURLOPT_PRIVATE, CURLOT_OBJECT, 0}, + {"PROGRESSDATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, + {"PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION, CURLOT_FUNCTION, 0}, + {"PROTOCOLS", CURLOPT_PROTOCOLS, CURLOT_LONG, 0}, + {"PROTOCOLS_STR", CURLOPT_PROTOCOLS_STR, CURLOT_STRING, 0}, + {"PROXY", CURLOPT_PROXY, CURLOT_STRING, 0}, + {"PROXYAUTH", CURLOPT_PROXYAUTH, CURLOT_VALUES, 0}, + {"PROXYHEADER", CURLOPT_PROXYHEADER, CURLOT_SLIST, 0}, + {"PROXYPASSWORD", CURLOPT_PROXYPASSWORD, CURLOT_STRING, 0}, + {"PROXYPORT", CURLOPT_PROXYPORT, CURLOT_LONG, 0}, + {"PROXYTYPE", CURLOPT_PROXYTYPE, CURLOT_VALUES, 0}, + {"PROXYUSERNAME", CURLOPT_PROXYUSERNAME, CURLOT_STRING, 0}, + {"PROXYUSERPWD", CURLOPT_PROXYUSERPWD, CURLOT_STRING, 0}, + {"PROXY_CAINFO", CURLOPT_PROXY_CAINFO, CURLOT_STRING, 0}, + {"PROXY_CAINFO_BLOB", CURLOPT_PROXY_CAINFO_BLOB, CURLOT_BLOB, 0}, + {"PROXY_CAPATH", CURLOPT_PROXY_CAPATH, CURLOT_STRING, 0}, + {"PROXY_CRLFILE", CURLOPT_PROXY_CRLFILE, CURLOT_STRING, 0}, + {"PROXY_ISSUERCERT", CURLOPT_PROXY_ISSUERCERT, CURLOT_STRING, 0}, + {"PROXY_ISSUERCERT_BLOB", CURLOPT_PROXY_ISSUERCERT_BLOB, CURLOT_BLOB, 0}, + {"PROXY_KEYPASSWD", CURLOPT_PROXY_KEYPASSWD, CURLOT_STRING, 0}, + {"PROXY_PINNEDPUBLICKEY", CURLOPT_PROXY_PINNEDPUBLICKEY, CURLOT_STRING, 0}, + {"PROXY_SERVICE_NAME", CURLOPT_PROXY_SERVICE_NAME, CURLOT_STRING, 0}, + {"PROXY_SSLCERT", CURLOPT_PROXY_SSLCERT, CURLOT_STRING, 0}, + {"PROXY_SSLCERTTYPE", CURLOPT_PROXY_SSLCERTTYPE, CURLOT_STRING, 0}, + {"PROXY_SSLCERT_BLOB", CURLOPT_PROXY_SSLCERT_BLOB, CURLOT_BLOB, 0}, + {"PROXY_SSLKEY", CURLOPT_PROXY_SSLKEY, CURLOT_STRING, 0}, + {"PROXY_SSLKEYTYPE", CURLOPT_PROXY_SSLKEYTYPE, CURLOT_STRING, 0}, + {"PROXY_SSLKEY_BLOB", CURLOPT_PROXY_SSLKEY_BLOB, CURLOT_BLOB, 0}, + {"PROXY_SSLVERSION", CURLOPT_PROXY_SSLVERSION, CURLOT_VALUES, 0}, + {"PROXY_SSL_CIPHER_LIST", CURLOPT_PROXY_SSL_CIPHER_LIST, CURLOT_STRING, 0}, + {"PROXY_SSL_OPTIONS", CURLOPT_PROXY_SSL_OPTIONS, CURLOT_LONG, 0}, + {"PROXY_SSL_VERIFYHOST", CURLOPT_PROXY_SSL_VERIFYHOST, CURLOT_LONG, 0}, + {"PROXY_SSL_VERIFYPEER", CURLOPT_PROXY_SSL_VERIFYPEER, CURLOT_LONG, 0}, + {"PROXY_TLS13_CIPHERS", CURLOPT_PROXY_TLS13_CIPHERS, CURLOT_STRING, 0}, + {"PROXY_TLSAUTH_PASSWORD", CURLOPT_PROXY_TLSAUTH_PASSWORD, + CURLOT_STRING, 0}, + {"PROXY_TLSAUTH_TYPE", CURLOPT_PROXY_TLSAUTH_TYPE, CURLOT_STRING, 0}, + {"PROXY_TLSAUTH_USERNAME", CURLOPT_PROXY_TLSAUTH_USERNAME, + CURLOT_STRING, 0}, + {"PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE, CURLOT_LONG, 0}, + {"PUT", CURLOPT_PUT, CURLOT_LONG, 0}, + {"QUICK_EXIT", CURLOPT_QUICK_EXIT, CURLOT_LONG, 0}, + {"QUOTE", CURLOPT_QUOTE, CURLOT_SLIST, 0}, + {"RANDOM_FILE", CURLOPT_RANDOM_FILE, CURLOT_STRING, 0}, + {"RANGE", CURLOPT_RANGE, CURLOT_STRING, 0}, + {"READDATA", CURLOPT_READDATA, CURLOT_CBPTR, 0}, + {"READFUNCTION", CURLOPT_READFUNCTION, CURLOT_FUNCTION, 0}, + {"REDIR_PROTOCOLS", CURLOPT_REDIR_PROTOCOLS, CURLOT_LONG, 0}, + {"REDIR_PROTOCOLS_STR", CURLOPT_REDIR_PROTOCOLS_STR, CURLOT_STRING, 0}, + {"REFERER", CURLOPT_REFERER, CURLOT_STRING, 0}, + {"REQUEST_TARGET", CURLOPT_REQUEST_TARGET, CURLOT_STRING, 0}, + {"RESOLVE", CURLOPT_RESOLVE, CURLOT_SLIST, 0}, + {"RESOLVER_START_DATA", CURLOPT_RESOLVER_START_DATA, CURLOT_CBPTR, 0}, + {"RESOLVER_START_FUNCTION", CURLOPT_RESOLVER_START_FUNCTION, + CURLOT_FUNCTION, 0}, + {"RESUME_FROM", CURLOPT_RESUME_FROM, CURLOT_LONG, 0}, + {"RESUME_FROM_LARGE", CURLOPT_RESUME_FROM_LARGE, CURLOT_OFF_T, 0}, + {"RTSPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, CURLOT_FLAG_ALIAS}, + {"RTSP_CLIENT_CSEQ", CURLOPT_RTSP_CLIENT_CSEQ, CURLOT_LONG, 0}, + {"RTSP_REQUEST", CURLOPT_RTSP_REQUEST, CURLOT_VALUES, 0}, + {"RTSP_SERVER_CSEQ", CURLOPT_RTSP_SERVER_CSEQ, CURLOT_LONG, 0}, + {"RTSP_SESSION_ID", CURLOPT_RTSP_SESSION_ID, CURLOT_STRING, 0}, + {"RTSP_STREAM_URI", CURLOPT_RTSP_STREAM_URI, CURLOT_STRING, 0}, + {"RTSP_TRANSPORT", CURLOPT_RTSP_TRANSPORT, CURLOT_STRING, 0}, + {"SASL_AUTHZID", CURLOPT_SASL_AUTHZID, CURLOT_STRING, 0}, + {"SASL_IR", CURLOPT_SASL_IR, CURLOT_LONG, 0}, + {"SEEKDATA", CURLOPT_SEEKDATA, CURLOT_CBPTR, 0}, + {"SEEKFUNCTION", CURLOPT_SEEKFUNCTION, CURLOT_FUNCTION, 0}, + {"SERVER_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, + CURLOT_LONG, 0}, + {"SERVER_RESPONSE_TIMEOUT_MS", CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, + CURLOT_LONG, 0}, + {"SERVICE_NAME", CURLOPT_SERVICE_NAME, CURLOT_STRING, 0}, + {"SHARE", CURLOPT_SHARE, CURLOT_OBJECT, 0}, + {"SOCKOPTDATA", CURLOPT_SOCKOPTDATA, CURLOT_CBPTR, 0}, + {"SOCKOPTFUNCTION", CURLOPT_SOCKOPTFUNCTION, CURLOT_FUNCTION, 0}, + {"SOCKS5_AUTH", CURLOPT_SOCKS5_AUTH, CURLOT_LONG, 0}, + {"SOCKS5_GSSAPI_NEC", CURLOPT_SOCKS5_GSSAPI_NEC, CURLOT_LONG, 0}, + {"SOCKS5_GSSAPI_SERVICE", CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOT_STRING, 0}, + {"SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES, CURLOT_VALUES, 0}, + {"SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CURLOT_LONG, 0}, + {"SSH_HOSTKEYDATA", CURLOPT_SSH_HOSTKEYDATA, CURLOT_CBPTR, 0}, + {"SSH_HOSTKEYFUNCTION", CURLOPT_SSH_HOSTKEYFUNCTION, CURLOT_FUNCTION, 0}, + {"SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, + CURLOT_STRING, 0}, + {"SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, + CURLOT_STRING, 0}, + {"SSH_KEYDATA", CURLOPT_SSH_KEYDATA, CURLOT_CBPTR, 0}, + {"SSH_KEYFUNCTION", CURLOPT_SSH_KEYFUNCTION, CURLOT_FUNCTION, 0}, + {"SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CURLOT_STRING, 0}, + {"SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE, CURLOT_STRING, 0}, + {"SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE, CURLOT_STRING, 0}, + {"SSLCERT", CURLOPT_SSLCERT, CURLOT_STRING, 0}, + {"SSLCERTPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, CURLOT_FLAG_ALIAS}, + {"SSLCERTTYPE", CURLOPT_SSLCERTTYPE, CURLOT_STRING, 0}, + {"SSLCERT_BLOB", CURLOPT_SSLCERT_BLOB, CURLOT_BLOB, 0}, + {"SSLENGINE", CURLOPT_SSLENGINE, CURLOT_STRING, 0}, + {"SSLENGINE_DEFAULT", CURLOPT_SSLENGINE_DEFAULT, CURLOT_LONG, 0}, + {"SSLKEY", CURLOPT_SSLKEY, CURLOT_STRING, 0}, + {"SSLKEYPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, CURLOT_FLAG_ALIAS}, + {"SSLKEYTYPE", CURLOPT_SSLKEYTYPE, CURLOT_STRING, 0}, + {"SSLKEY_BLOB", CURLOPT_SSLKEY_BLOB, CURLOT_BLOB, 0}, + {"SSLVERSION", CURLOPT_SSLVERSION, CURLOT_VALUES, 0}, + {"SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST, CURLOT_STRING, 0}, + {"SSL_CTX_DATA", CURLOPT_SSL_CTX_DATA, CURLOT_CBPTR, 0}, + {"SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, CURLOT_FUNCTION, 0}, + {"SSL_EC_CURVES", CURLOPT_SSL_EC_CURVES, CURLOT_STRING, 0}, + {"SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CURLOT_LONG, 0}, + {"SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CURLOT_LONG, 0}, + {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0}, + {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0}, + {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0}, + {"SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CURLOT_LONG, 0}, + {"SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CURLOT_LONG, 0}, + {"SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CURLOT_LONG, 0}, + {"STDERR", CURLOPT_STDERR, CURLOT_OBJECT, 0}, + {"STREAM_DEPENDS", CURLOPT_STREAM_DEPENDS, CURLOT_OBJECT, 0}, + {"STREAM_DEPENDS_E", CURLOPT_STREAM_DEPENDS_E, CURLOT_OBJECT, 0}, + {"STREAM_WEIGHT", CURLOPT_STREAM_WEIGHT, CURLOT_LONG, 0}, + {"SUPPRESS_CONNECT_HEADERS", CURLOPT_SUPPRESS_CONNECT_HEADERS, + CURLOT_LONG, 0}, + {"TCP_FASTOPEN", CURLOPT_TCP_FASTOPEN, CURLOT_LONG, 0}, + {"TCP_KEEPALIVE", CURLOPT_TCP_KEEPALIVE, CURLOT_LONG, 0}, + {"TCP_KEEPCNT", CURLOPT_TCP_KEEPCNT, CURLOT_LONG, 0}, + {"TCP_KEEPIDLE", CURLOPT_TCP_KEEPIDLE, CURLOT_LONG, 0}, + {"TCP_KEEPINTVL", CURLOPT_TCP_KEEPINTVL, CURLOT_LONG, 0}, + {"TCP_NODELAY", CURLOPT_TCP_NODELAY, CURLOT_LONG, 0}, + {"TELNETOPTIONS", CURLOPT_TELNETOPTIONS, CURLOT_SLIST, 0}, + {"TFTP_BLKSIZE", CURLOPT_TFTP_BLKSIZE, CURLOT_LONG, 0}, + {"TFTP_NO_OPTIONS", CURLOPT_TFTP_NO_OPTIONS, CURLOT_LONG, 0}, + {"TIMECONDITION", CURLOPT_TIMECONDITION, CURLOT_VALUES, 0}, + {"TIMEOUT", CURLOPT_TIMEOUT, CURLOT_LONG, 0}, + {"TIMEOUT_MS", CURLOPT_TIMEOUT_MS, CURLOT_LONG, 0}, + {"TIMEVALUE", CURLOPT_TIMEVALUE, CURLOT_LONG, 0}, + {"TIMEVALUE_LARGE", CURLOPT_TIMEVALUE_LARGE, CURLOT_OFF_T, 0}, + {"TLS13_CIPHERS", CURLOPT_TLS13_CIPHERS, CURLOT_STRING, 0}, + {"TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD, CURLOT_STRING, 0}, + {"TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE, CURLOT_STRING, 0}, + {"TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME, CURLOT_STRING, 0}, + {"TRAILERDATA", CURLOPT_TRAILERDATA, CURLOT_CBPTR, 0}, + {"TRAILERFUNCTION", CURLOPT_TRAILERFUNCTION, CURLOT_FUNCTION, 0}, + {"TRANSFERTEXT", CURLOPT_TRANSFERTEXT, CURLOT_LONG, 0}, + {"TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING, CURLOT_LONG, 0}, + {"UNIX_SOCKET_PATH", CURLOPT_UNIX_SOCKET_PATH, CURLOT_STRING, 0}, + {"UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH, CURLOT_LONG, 0}, + {"UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CURLOT_LONG, 0}, + {"UPLOAD", CURLOPT_UPLOAD, CURLOT_LONG, 0}, + {"UPLOAD_BUFFERSIZE", CURLOPT_UPLOAD_BUFFERSIZE, CURLOT_LONG, 0}, + {"URL", CURLOPT_URL, CURLOT_STRING, 0}, + {"USERAGENT", CURLOPT_USERAGENT, CURLOT_STRING, 0}, + {"USERNAME", CURLOPT_USERNAME, CURLOT_STRING, 0}, + {"USERPWD", CURLOPT_USERPWD, CURLOT_STRING, 0}, + {"USE_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, 0}, + {"VERBOSE", CURLOPT_VERBOSE, CURLOT_LONG, 0}, + {"WILDCARDMATCH", CURLOPT_WILDCARDMATCH, CURLOT_LONG, 0}, + {"WRITEDATA", CURLOPT_WRITEDATA, CURLOT_CBPTR, 0}, + {"WRITEFUNCTION", CURLOPT_WRITEFUNCTION, CURLOT_FUNCTION, 0}, + {"WRITEHEADER", CURLOPT_HEADERDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, + {"WS_OPTIONS", CURLOPT_WS_OPTIONS, CURLOT_LONG, 0}, + {"XFERINFODATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, 0}, + {"XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION, CURLOT_FUNCTION, 0}, + {"XOAUTH2_BEARER", CURLOPT_XOAUTH2_BEARER, CURLOT_STRING, 0}, + {NULL, CURLOPT_LASTENTRY, CURLOT_LONG, 0} /* end of table */ +}; + +#ifdef DEBUGBUILD +/* + * Curl_easyopts_check() is a debug-only function that returns non-zero + * if this source file is not in sync with the options listed in curl/curl.h + */ +int Curl_easyopts_check(void) +{ + return ((CURLOPT_LASTENTRY%10000) != (326 + 1)); +} +#endif diff --git a/third_party/curl/lib/easyoptions.h b/third_party/curl/lib/easyoptions.h new file mode 100644 index 0000000000..24b4cd93ed --- /dev/null +++ b/third_party/curl/lib/easyoptions.h @@ -0,0 +1,37 @@ +#ifndef HEADER_CURL_EASYOPTIONS_H +#define HEADER_CURL_EASYOPTIONS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* should probably go into the public header */ + +#include + +/* generated table with all easy options */ +extern struct curl_easyoption Curl_easyopts[]; + +#ifdef DEBUGBUILD +int Curl_easyopts_check(void); +#endif +#endif diff --git a/third_party/curl/lib/escape.c b/third_party/curl/lib/escape.c new file mode 100644 index 0000000000..1633c2da2b --- /dev/null +++ b/third_party/curl/lib/escape.c @@ -0,0 +1,235 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* Escape and unescape URL encoding in strings. The functions return a new + * allocated string or NULL if an error occurred. */ + +#include "curl_setup.h" + +#include + +#include "urldata.h" +#include "warnless.h" +#include "escape.h" +#include "strdup.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* for ABI-compatibility with previous versions */ +char *curl_escape(const char *string, int inlength) +{ + return curl_easy_escape(NULL, string, inlength); +} + +/* for ABI-compatibility with previous versions */ +char *curl_unescape(const char *string, int length) +{ + return curl_easy_unescape(NULL, string, length, NULL); +} + +/* Escapes for URL the given unescaped string of given length. + * 'data' is ignored since 7.82.0. + */ +char *curl_easy_escape(struct Curl_easy *data, const char *string, + int inlength) +{ + size_t length; + struct dynbuf d; + (void)data; + + if(!string || (inlength < 0)) + return NULL; + + Curl_dyn_init(&d, CURL_MAX_INPUT_LENGTH * 3); + + length = (inlength?(size_t)inlength:strlen(string)); + if(!length) + return strdup(""); + + while(length--) { + /* treat the characters unsigned */ + unsigned char in = (unsigned char)*string++; + + if(ISUNRESERVED(in)) { + /* append this */ + if(Curl_dyn_addn(&d, &in, 1)) + return NULL; + } + else { + /* encode it */ + const char hex[] = "0123456789ABCDEF"; + char out[3]={'%'}; + out[1] = hex[in>>4]; + out[2] = hex[in & 0xf]; + if(Curl_dyn_addn(&d, out, 3)) + return NULL; + } + } + + return Curl_dyn_ptr(&d); +} + +static const unsigned char hextable[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ + 0, 10, 11, 12, 13, 14, 15 /* 0x60 - 0x66 */ +}; + +/* the input is a single hex digit */ +#define onehex2dec(x) hextable[x - '0'] + +/* + * Curl_urldecode() URL decodes the given string. + * + * Returns a pointer to a malloced string in *ostring with length given in + * *olen. If length == 0, the length is assumed to be strlen(string). + * + * ctrl options: + * - REJECT_NADA: accept everything + * - REJECT_CTRL: rejects control characters (byte codes lower than 32) in + * the data + * - REJECT_ZERO: rejects decoded zero bytes + * + * The values for the enum starts at 2, to make the assert detect legacy + * invokes that used TRUE/FALSE (0 and 1). + */ + +CURLcode Curl_urldecode(const char *string, size_t length, + char **ostring, size_t *olen, + enum urlreject ctrl) +{ + size_t alloc; + char *ns; + + DEBUGASSERT(string); + DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */ + + alloc = (length?length:strlen(string)); + ns = malloc(alloc + 1); + + if(!ns) + return CURLE_OUT_OF_MEMORY; + + /* store output string */ + *ostring = ns; + + while(alloc) { + unsigned char in = (unsigned char)*string; + if(('%' == in) && (alloc > 2) && + ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { + /* this is two hexadecimal digits following a '%' */ + in = (unsigned char)(onehex2dec(string[1]) << 4) | onehex2dec(string[2]); + + string += 3; + alloc -= 3; + } + else { + string++; + alloc--; + } + + if(((ctrl == REJECT_CTRL) && (in < 0x20)) || + ((ctrl == REJECT_ZERO) && (in == 0))) { + Curl_safefree(*ostring); + return CURLE_URL_MALFORMAT; + } + + *ns++ = (char)in; + } + *ns = 0; /* terminate it */ + + if(olen) + /* store output size */ + *olen = ns - *ostring; + + return CURLE_OK; +} + +/* + * Unescapes the given URL escaped string of given length. Returns a + * pointer to a malloced string with length given in *olen. + * If length == 0, the length is assumed to be strlen(string). + * If olen == NULL, no output length is stored. + * 'data' is ignored since 7.82.0. + */ +char *curl_easy_unescape(struct Curl_easy *data, const char *string, + int length, int *olen) +{ + char *str = NULL; + (void)data; + if(string && (length >= 0)) { + size_t inputlen = (size_t)length; + size_t outputlen; + CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen, + REJECT_NADA); + if(res) + return NULL; + + if(olen) { + if(outputlen <= (size_t) INT_MAX) + *olen = curlx_uztosi(outputlen); + else + /* too large to return in an int, fail! */ + Curl_safefree(str); + } + } + return str; +} + +/* For operating systems/environments that use different malloc/free + systems for the app and for this library, we provide a free that uses + the library's memory system */ +void curl_free(void *p) +{ + free(p); +} + +/* + * Curl_hexencode() + * + * Converts binary input to lowercase hex-encoded ASCII output. + * Null-terminated. + */ +void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ + unsigned char *out, size_t olen) /* output buffer size */ +{ + const char *hex = "0123456789abcdef"; + DEBUGASSERT(src && len && (olen >= 3)); + if(src && len && (olen >= 3)) { + while(len-- && (olen >= 3)) { + /* clang-tidy warns on this line without this comment: */ + /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */ + *out++ = (unsigned char)hex[(*src & 0xF0)>>4]; + *out++ = (unsigned char)hex[*src & 0x0F]; + ++src; + olen -= 2; + } + *out = 0; + } + else if(olen) + *out = 0; +} diff --git a/third_party/curl/lib/escape.h b/third_party/curl/lib/escape.h new file mode 100644 index 0000000000..690e417879 --- /dev/null +++ b/third_party/curl/lib/escape.h @@ -0,0 +1,44 @@ +#ifndef HEADER_CURL_ESCAPE_H +#define HEADER_CURL_ESCAPE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* Escape and unescape URL encoding in strings. The functions return a new + * allocated string or NULL if an error occurred. */ + +#include "curl_ctype.h" + +enum urlreject { + REJECT_NADA = 2, + REJECT_CTRL, + REJECT_ZERO +}; + +CURLcode Curl_urldecode(const char *string, size_t length, + char **ostring, size_t *olen, + enum urlreject ctrl); + +void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ + unsigned char *out, size_t olen); /* output buffer size */ + +#endif /* HEADER_CURL_ESCAPE_H */ diff --git a/third_party/curl/lib/file.c b/third_party/curl/lib/file.c new file mode 100644 index 0000000000..82ff151b6f --- /dev/null +++ b/third_party/curl/lib/file.c @@ -0,0 +1,643 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_FILE + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_DIRENT_H +#include +#endif + +#include "strtoofft.h" +#include "urldata.h" +#include +#include "progress.h" +#include "sendf.h" +#include "escape.h" +#include "file.h" +#include "speedcheck.h" +#include "getinfo.h" +#include "multiif.h" +#include "transfer.h" +#include "url.h" +#include "parsedate.h" /* for the week day and month names */ +#include "warnless.h" +#include "curl_range.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if defined(_WIN32) || defined(MSDOS) || defined(__EMX__) +#define DOS_FILESYSTEM 1 +#elif defined(__amigaos4__) +#define AMIGA_FILESYSTEM 1 +#endif + +#ifdef OPEN_NEEDS_ARG3 +# define open_readonly(p,f) open((p),(f),(0)) +#else +# define open_readonly(p,f) open((p),(f)) +#endif + +/* + * Forward declarations. + */ + +static CURLcode file_do(struct Curl_easy *data, bool *done); +static CURLcode file_done(struct Curl_easy *data, + CURLcode status, bool premature); +static CURLcode file_connect(struct Curl_easy *data, bool *done); +static CURLcode file_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection); +static CURLcode file_setup_connection(struct Curl_easy *data, + struct connectdata *conn); + +/* + * FILE scheme handler. + */ + +const struct Curl_handler Curl_handler_file = { + "file", /* scheme */ + file_setup_connection, /* setup_connection */ + file_do, /* do_it */ + file_done, /* done */ + ZERO_NULL, /* do_more */ + file_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + file_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + 0, /* defport */ + CURLPROTO_FILE, /* protocol */ + CURLPROTO_FILE, /* family */ + PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */ +}; + + +static CURLcode file_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + (void)conn; + /* allocate the FILE specific struct */ + data->req.p.file = calloc(1, sizeof(struct FILEPROTO)); + if(!data->req.p.file) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +/* + * file_connect() gets called from Curl_protocol_connect() to allow us to + * do protocol-specific actions at connect-time. We emulate a + * connect-then-transfer protocol and "connect" to the file here + */ +static CURLcode file_connect(struct Curl_easy *data, bool *done) +{ + char *real_path; + struct FILEPROTO *file = data->req.p.file; + int fd; +#ifdef DOS_FILESYSTEM + size_t i; + 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; + } + + result = Curl_urldecode(data->state.up.path, 0, &real_path, + &real_path_len, REJECT_ZERO); + if(result) + return result; + +#ifdef DOS_FILESYSTEM + /* If the first character is a slash, and there is + something that looks like a drive at the beginning of + the path, skip the slash. If we remove the initial + slash in all cases, paths without drive letters end up + relative to the current directory which is not how + browsers work. + + Some browsers accept | instead of : as the drive letter + separator, so we do too. + + On other platforms, we need the slash to indicate an + absolute pathname. On Windows, absolute paths start + with a drive letter. + */ + actual_path = real_path; + if((actual_path[0] == '/') && + actual_path[1] && + (actual_path[2] == ':' || actual_path[2] == '|')) { + actual_path[2] = ':'; + actual_path++; + real_path_len--; + } + + /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */ + for(i = 0; i < real_path_len; ++i) + if(actual_path[i] == '/') + actual_path[i] = '\\'; + else if(!actual_path[i]) { /* binary zero */ + Curl_safefree(real_path); + return CURLE_URL_MALFORMAT; + } + + fd = open_readonly(actual_path, O_RDONLY|O_BINARY); + file->path = actual_path; +#else + if(memchr(real_path, 0, real_path_len)) { + /* binary zeroes indicate foul play */ + Curl_safefree(real_path); + return CURLE_URL_MALFORMAT; + } + + #ifdef AMIGA_FILESYSTEM + /* + * A leading slash in an AmigaDOS path denotes the parent + * directory, and hence we block this as it is relative. + * Absolute paths start with 'volumename:', so we check for + * this first. Failing that, we treat the path as a real unix + * path, but only if the application was compiled with -lunix. + */ + fd = -1; + file->path = real_path; + + if(real_path[0] == '/') { + extern int __unix_path_semantics; + if(strchr(real_path + 1, ':')) { + /* Amiga absolute path */ + fd = open_readonly(real_path + 1, O_RDONLY); + file->path++; + } + else if(__unix_path_semantics) { + /* -lunix fallback */ + fd = open_readonly(real_path, O_RDONLY); + } + } + #else + fd = open_readonly(real_path, O_RDONLY); + file->path = real_path; + #endif +#endif + Curl_safefree(file->freepath); + file->freepath = real_path; /* free this when done */ + + file->fd = fd; + if(!data->state.upload && (fd == -1)) { + failf(data, "Couldn't open file %s", data->state.up.path); + file_done(data, CURLE_FILE_COULDNT_READ_FILE, FALSE); + return CURLE_FILE_COULDNT_READ_FILE; + } + *done = TRUE; + + return CURLE_OK; +} + +static CURLcode file_done(struct Curl_easy *data, + CURLcode status, bool premature) +{ + struct FILEPROTO *file = data->req.p.file; + (void)status; /* not used */ + (void)premature; /* not used */ + + if(file) { + Curl_safefree(file->freepath); + file->path = NULL; + if(file->fd != -1) + close(file->fd); + file->fd = -1; + } + + return CURLE_OK; +} + +static CURLcode file_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + (void)dead_connection; /* not used */ + (void)conn; + return file_done(data, CURLE_OK, FALSE); +} + +#ifdef DOS_FILESYSTEM +#define DIRSEP '\\' +#else +#define DIRSEP '/' +#endif + +static CURLcode file_upload(struct Curl_easy *data) +{ + struct FILEPROTO *file = data->req.p.file; + const char *dir = strchr(file->path, DIRSEP); + int fd; + int mode; + CURLcode result = CURLE_OK; + char *xfer_ulbuf; + size_t xfer_ulblen; + curl_off_t bytecount = 0; + struct_stat file_stat; + const char *sendbuf; + bool eos = FALSE; + + /* + * Since FILE: does not do the full init, we need to provide some extra + * assignments here. + */ + + if(!dir) + return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ + + if(!dir[1]) + return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ + +#ifdef O_BINARY +#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY +#else +#define MODE_DEFAULT O_WRONLY|O_CREAT +#endif + + if(data->state.resume_from) + mode = MODE_DEFAULT|O_APPEND; + else + mode = MODE_DEFAULT|O_TRUNC; + + fd = open(file->path, mode, data->set.new_file_perms); + if(fd < 0) { + failf(data, "cannot open %s for writing", file->path); + return CURLE_WRITE_ERROR; + } + + if(-1 != data->state.infilesize) + /* known size of data to "upload" */ + Curl_pgrsSetUploadSize(data, data->state.infilesize); + + /* treat the negative resume offset value as the case of "-" */ + if(data->state.resume_from < 0) { + if(fstat(fd, &file_stat)) { + close(fd); + failf(data, "cannot get the size of %s", file->path); + return CURLE_WRITE_ERROR; + } + data->state.resume_from = (curl_off_t)file_stat.st_size; + } + + result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen); + if(result) + goto out; + + while(!result && !eos) { + size_t nread; + ssize_t nwrite; + size_t readcount; + + result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &readcount, &eos); + if(result) + break; + + if(!readcount) + break; + + nread = readcount; + + /* skip bytes before resume point */ + if(data->state.resume_from) { + if((curl_off_t)nread <= data->state.resume_from) { + data->state.resume_from -= nread; + nread = 0; + sendbuf = xfer_ulbuf; + } + else { + sendbuf = xfer_ulbuf + data->state.resume_from; + nread -= (size_t)data->state.resume_from; + data->state.resume_from = 0; + } + } + else + sendbuf = xfer_ulbuf; + + /* write the data to the target */ + nwrite = write(fd, sendbuf, nread); + if((size_t)nwrite != nread) { + result = CURLE_SEND_ERROR; + break; + } + + bytecount += nread; + + Curl_pgrsSetUploadCounter(data, bytecount); + + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, Curl_now()); + } + if(!result && Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + +out: + close(fd); + Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf); + + return result; +} + +/* + * file_do() is the protocol-specific function for the do-phase, separated + * from the connect-phase above. Other protocols merely setup the transfer in + * the do-phase, to have it done in the main transfer loop but since some + * platforms we support do not allow select()ing etc on file handles (as + * opposed to sockets) we instead perform the whole do-operation in this + * function. + */ +static CURLcode file_do(struct Curl_easy *data, bool *done) +{ + /* This implementation ignores the hostname in conformance with + RFC 1738. Only local files (reachable via the standard file system) + are supported. This means that files on remotely mounted directories + (via NFS, Samba, NT sharing) can be accessed through a file:// URL + */ + CURLcode result = CURLE_OK; + struct_stat statbuf; /* struct_stat instead of struct stat just to allow the + Windows version to have a different struct without + having to redefine the simple word 'stat' */ + curl_off_t expected_size = -1; + bool size_known; + bool fstated = FALSE; + int fd; + struct FILEPROTO *file; + char *xfer_buf; + size_t xfer_blen; + + *done = TRUE; /* unconditionally */ + + if(data->state.upload) + return file_upload(data); + + file = data->req.p.file; + + /* get the fd from the connection phase */ + fd = file->fd; + + /* VMS: This only works reliable for STREAMLF files */ + if(-1 != fstat(fd, &statbuf)) { + if(!S_ISDIR(statbuf.st_mode)) + expected_size = statbuf.st_size; + /* and store the modification time */ + data->info.filetime = statbuf.st_mtime; + fstated = TRUE; + } + + if(fstated && !data->state.range && data->set.timecondition && + !Curl_meets_timecondition(data, data->info.filetime)) + return CURLE_OK; + + if(fstated) { + time_t filetime; + struct tm buffer; + const struct tm *tm = &buffer; + char header[80]; + int headerlen; + static const char accept_ranges[]= { "Accept-ranges: bytes\r\n" }; + if(expected_size >= 0) { + headerlen = + msnprintf(header, sizeof(header), + "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", + expected_size); + result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); + if(result) + return result; + + result = Curl_client_write(data, CLIENTWRITE_HEADER, + accept_ranges, sizeof(accept_ranges) - 1); + if(result != CURLE_OK) + return result; + } + + filetime = (time_t)statbuf.st_mtime; + result = Curl_gmtime(filetime, &buffer); + if(result) + return result; + + /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ + headerlen = + msnprintf(header, sizeof(header), + "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", + Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], + tm->tm_mday, + Curl_month[tm->tm_mon], + tm->tm_year + 1900, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); + if(!result) + /* end of headers */ + result = Curl_client_write(data, CLIENTWRITE_HEADER, "\r\n", 2); + if(result) + return result; + /* set the file size to make it available post transfer */ + Curl_pgrsSetDownloadSize(data, expected_size); + if(data->req.no_body) + return CURLE_OK; + } + + /* Check whether file range has been specified */ + result = Curl_range(data); + if(result) + return result; + + /* Adjust the start offset in case we want to get the N last bytes + * of the stream if the filesize could be determined */ + if(data->state.resume_from < 0) { + if(!fstated) { + failf(data, "cannot get the size of file."); + return CURLE_READ_ERROR; + } + data->state.resume_from += (curl_off_t)statbuf.st_size; + } + + if(data->state.resume_from > 0) { + /* We check explicitly if we have a start offset, because + * expected_size may be -1 if we do not know how large the file is, + * in which case we should not adjust it. */ + if(data->state.resume_from <= expected_size) + expected_size -= data->state.resume_from; + else { + failf(data, "failed to resume file:// transfer"); + return CURLE_BAD_DOWNLOAD_RESUME; + } + } + + /* A high water mark has been specified so we obey... */ + if(data->req.maxdownload > 0) + expected_size = data->req.maxdownload; + + if(!fstated || (expected_size <= 0)) + size_known = FALSE; + else + size_known = TRUE; + + /* The following is a shortcut implementation of file reading + this is both more efficient than the former call to download() and + it avoids problems with select() and recv() on file descriptors + in Winsock */ + if(size_known) + Curl_pgrsSetDownloadSize(data, expected_size); + + if(data->state.resume_from) { + if(!S_ISDIR(statbuf.st_mode)) { + if(data->state.resume_from != + lseek(fd, data->state.resume_from, SEEK_SET)) + return CURLE_BAD_DOWNLOAD_RESUME; + } + else { + return CURLE_BAD_DOWNLOAD_RESUME; + } + } + + result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen); + if(result) + goto out; + + if(!S_ISDIR(statbuf.st_mode)) { + while(!result) { + ssize_t nread; + /* Do not fill a whole buffer if we want less than all data */ + size_t bytestoread; + + if(size_known) { + bytestoread = (expected_size < (curl_off_t)(xfer_blen-1)) ? + curlx_sotouz(expected_size) : (xfer_blen-1); + } + else + bytestoread = xfer_blen-1; + + nread = read(fd, xfer_buf, bytestoread); + + if(nread > 0) + xfer_buf[nread] = 0; + + if(nread <= 0 || (size_known && (expected_size == 0))) + break; + + if(size_known) + expected_size -= nread; + + result = Curl_client_write(data, CLIENTWRITE_BODY, xfer_buf, nread); + if(result) + goto out; + + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, Curl_now()); + if(result) + goto out; + } + } + else { +#ifdef HAVE_OPENDIR + DIR *dir = opendir(file->path); + struct dirent *entry; + + if(!dir) { + result = CURLE_READ_ERROR; + goto out; + } + else { + while((entry = readdir(dir))) { + if(entry->d_name[0] != '.') { + result = Curl_client_write(data, CLIENTWRITE_BODY, + entry->d_name, strlen(entry->d_name)); + if(result) + break; + result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1); + if(result) + break; + } + } + closedir(dir); + } +#else + failf(data, "Directory listing not yet implemented on this platform."); + result = CURLE_READ_ERROR; +#endif + } + + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + +out: + Curl_multi_xfer_buf_release(data, xfer_buf); + return result; +} + +#endif diff --git a/third_party/curl/lib/file.h b/third_party/curl/lib/file.h new file mode 100644 index 0000000000..4565525592 --- /dev/null +++ b/third_party/curl/lib/file.h @@ -0,0 +1,42 @@ +#ifndef HEADER_CURL_FILE_H +#define HEADER_CURL_FILE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + + +/**************************************************************************** + * FILE unique setup + ***************************************************************************/ +struct FILEPROTO { + char *path; /* the path we operate on */ + char *freepath; /* pointer to the allocated block we must free, this might + differ from the 'path' pointer */ + int fd; /* open file descriptor to read from! */ +}; + +#ifndef CURL_DISABLE_FILE +extern const struct Curl_handler Curl_handler_file; +#endif + +#endif /* HEADER_CURL_FILE_H */ diff --git a/third_party/curl/lib/fileinfo.c b/third_party/curl/lib/fileinfo.c new file mode 100644 index 0000000000..2be3b3239b --- /dev/null +++ b/third_party/curl/lib/fileinfo.c @@ -0,0 +1,46 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#ifndef CURL_DISABLE_FTP +#include "strdup.h" +#include "fileinfo.h" +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +struct fileinfo *Curl_fileinfo_alloc(void) +{ + return calloc(1, sizeof(struct fileinfo)); +} + +void Curl_fileinfo_cleanup(struct fileinfo *finfo) +{ + if(!finfo) + return; + + Curl_dyn_free(&finfo->buf); + free(finfo); +} +#endif diff --git a/third_party/curl/lib/fileinfo.h b/third_party/curl/lib/fileinfo.h new file mode 100644 index 0000000000..ce009da06d --- /dev/null +++ b/third_party/curl/lib/fileinfo.h @@ -0,0 +1,40 @@ +#ifndef HEADER_CURL_FILEINFO_H +#define HEADER_CURL_FILEINFO_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include +#include "llist.h" +#include "dynbuf.h" + +struct fileinfo { + struct curl_fileinfo info; + struct Curl_llist_element list; + struct dynbuf buf; +}; + +struct fileinfo *Curl_fileinfo_alloc(void); +void Curl_fileinfo_cleanup(struct fileinfo *finfo); + +#endif /* HEADER_CURL_FILEINFO_H */ diff --git a/third_party/curl/lib/fopen.c b/third_party/curl/lib/fopen.c new file mode 100644 index 0000000000..7373e08831 --- /dev/null +++ b/third_party/curl/lib/fopen.c @@ -0,0 +1,158 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ + !defined(CURL_DISABLE_HSTS) + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include "urldata.h" +#include "rand.h" +#include "fopen.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + The dirslash() function breaks a null-terminated pathname string into + directory and filename components then returns the directory component up + to, *AND INCLUDING*, a final '/'. If there is no directory in the path, + this instead returns a "" string. + + This function returns a pointer to malloc'ed memory. + + The input path to this function is expected to have a filename part. +*/ + +#ifdef _WIN32 +#define PATHSEP "\\" +#define IS_SEP(x) (((x) == '/') || ((x) == '\\')) +#elif defined(MSDOS) || defined(__EMX__) || defined(OS2) +#define PATHSEP "\\" +#define IS_SEP(x) ((x) == '\\') +#else +#define PATHSEP "/" +#define IS_SEP(x) ((x) == '/') +#endif + +static char *dirslash(const char *path) +{ + size_t n; + struct dynbuf out; + DEBUGASSERT(path); + Curl_dyn_init(&out, CURL_MAX_INPUT_LENGTH); + n = strlen(path); + if(n) { + /* find the rightmost path separator, if any */ + while(n && !IS_SEP(path[n-1])) + --n; + /* skip over all the path separators, if any */ + while(n && IS_SEP(path[n-1])) + --n; + } + if(Curl_dyn_addn(&out, path, n)) + return NULL; + /* if there was a directory, append a single trailing slash */ + if(n && Curl_dyn_addn(&out, PATHSEP, 1)) + return NULL; + return Curl_dyn_ptr(&out); +} + +/* + * Curl_fopen() opens a file for writing with a temp name, to be renamed + * to the final name when completed. If there is an existing file using this + * name at the time of the open, this function will clone the mode from that + * file. if 'tempname' is non-NULL, it needs a rename after the file is + * written. + */ +CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, + FILE **fh, char **tempname) +{ + CURLcode result = CURLE_WRITE_ERROR; + unsigned char randbuf[41]; + char *tempstore = NULL; + struct_stat sb; + int fd = -1; + char *dir = NULL; + *tempname = NULL; + + *fh = fopen(filename, FOPEN_WRITETEXT); + if(!*fh) + goto fail; + if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { + return CURLE_OK; + } + fclose(*fh); + *fh = NULL; + + result = Curl_rand_alnum(data, randbuf, sizeof(randbuf)); + if(result) + goto fail; + + dir = dirslash(filename); + if(dir) { + /* The temp filename should not end up too long for the target file + system */ + tempstore = aprintf("%s%s.tmp", dir, randbuf); + free(dir); + } + + if(!tempstore) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + result = CURLE_WRITE_ERROR; +#if (defined(ANDROID) || defined(__ANDROID__)) && \ + (defined(__i386__) || defined(__arm__)) + fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, (mode_t)(0600|sb.st_mode)); +#else + fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode); +#endif + if(fd == -1) + goto fail; + + *fh = fdopen(fd, FOPEN_WRITETEXT); + if(!*fh) + goto fail; + + *tempname = tempstore; + return CURLE_OK; + +fail: + if(fd != -1) { + close(fd); + unlink(tempstore); + } + + free(tempstore); + return result; +} + +#endif /* ! disabled */ diff --git a/third_party/curl/lib/fopen.h b/third_party/curl/lib/fopen.h new file mode 100644 index 0000000000..e3a919d073 --- /dev/null +++ b/third_party/curl/lib/fopen.h @@ -0,0 +1,30 @@ +#ifndef HEADER_CURL_FOPEN_H +#define HEADER_CURL_FOPEN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, + FILE **fh, char **tempname); + +#endif diff --git a/third_party/curl/lib/formdata.c b/third_party/curl/lib/formdata.c new file mode 100644 index 0000000000..7a282bdd3d --- /dev/null +++ b/third_party/curl/lib/formdata.c @@ -0,0 +1,958 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "formdata.h" +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) + +#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) +#include +#endif + +#include "urldata.h" /* for struct Curl_easy */ +#include "mime.h" +#include "vtls/vtls.h" +#include "strcase.h" +#include "sendf.h" +#include "strdup.h" +#include "rand.h" +#include "warnless.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +#define HTTPPOST_PTRNAME CURL_HTTPPOST_PTRNAME +#define HTTPPOST_FILENAME CURL_HTTPPOST_FILENAME +#define HTTPPOST_PTRCONTENTS CURL_HTTPPOST_PTRCONTENTS +#define HTTPPOST_READFILE CURL_HTTPPOST_READFILE +#define HTTPPOST_PTRBUFFER CURL_HTTPPOST_PTRBUFFER +#define HTTPPOST_CALLBACK CURL_HTTPPOST_CALLBACK +#define HTTPPOST_BUFFER CURL_HTTPPOST_BUFFER + +/*************************************************************************** + * + * AddHttpPost() + * + * Adds an HttpPost structure to the list, if parent_post is given becomes + * a subpost of parent_post instead of a direct list element. + * + * Returns newly allocated HttpPost on success and NULL if malloc failed. + * + ***************************************************************************/ +static struct curl_httppost * +AddHttpPost(char *name, size_t namelength, + char *value, curl_off_t contentslength, + char *buffer, size_t bufferlength, + char *contenttype, + long flags, + struct curl_slist *contentHeader, + char *showfilename, char *userp, + struct curl_httppost *parent_post, + struct curl_httppost **httppost, + struct curl_httppost **last_post) +{ + struct curl_httppost *post; + if(!namelength && name) + namelength = strlen(name); + if((bufferlength > LONG_MAX) || (namelength > LONG_MAX)) + /* avoid overflow in typecasts below */ + return NULL; + post = calloc(1, sizeof(struct curl_httppost)); + if(post) { + post->name = name; + post->namelength = (long)namelength; + post->contents = value; + post->contentlen = contentslength; + post->buffer = buffer; + post->bufferlength = (long)bufferlength; + post->contenttype = contenttype; + post->contentheader = contentHeader; + post->showfilename = showfilename; + post->userp = userp; + post->flags = flags | CURL_HTTPPOST_LARGE; + } + else + return NULL; + + if(parent_post) { + /* now, point our 'more' to the original 'more' */ + post->more = parent_post->more; + + /* then move the original 'more' to point to ourselves */ + parent_post->more = post; + } + else { + /* make the previous point to this */ + if(*last_post) + (*last_post)->next = post; + else + (*httppost) = post; + + (*last_post) = post; + } + return post; +} + +/*************************************************************************** + * + * AddFormInfo() + * + * Adds a FormInfo structure to the list presented by parent_form_info. + * + * Returns newly allocated FormInfo on success and NULL if malloc failed/ + * parent_form_info is NULL. + * + ***************************************************************************/ +static struct FormInfo *AddFormInfo(char *value, + char *contenttype, + struct FormInfo *parent_form_info) +{ + struct FormInfo *form_info; + form_info = calloc(1, sizeof(struct FormInfo)); + if(!form_info) + return NULL; + if(value) + form_info->value = value; + if(contenttype) + form_info->contenttype = contenttype; + form_info->flags = HTTPPOST_FILENAME; + + if(parent_form_info) { + /* now, point our 'more' to the original 'more' */ + form_info->more = parent_form_info->more; + + /* then move the original 'more' to point to ourselves */ + parent_form_info->more = form_info; + } + + return form_info; +} + +/*************************************************************************** + * + * FormAdd() + * + * Stores a formpost parameter and builds the appropriate linked list. + * + * Has two principal functionalities: using files and byte arrays as + * post parts. Byte arrays are either copied or just the pointer is stored + * (as the user requests) while for files only the filename and not the + * content is stored. + * + * While you may have only one byte array for each name, multiple filenames + * are allowed (and because of this feature CURLFORM_END is needed after + * using CURLFORM_FILE). + * + * Examples: + * + * Simple name/value pair with copied contents: + * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", + * CURLFORM_COPYCONTENTS, "value", CURLFORM_END); + * + * name/value pair where only the content pointer is remembered: + * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", + * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END); + * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used) + * + * storing a filename (CONTENTTYPE is optional!): + * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", + * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text", + * CURLFORM_END); + * + * storing multiple filenames: + * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", + * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END); + * + * Returns: + * CURL_FORMADD_OK on success + * CURL_FORMADD_MEMORY if the FormInfo allocation fails + * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form + * CURL_FORMADD_NULL if a null pointer was given for a char + * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed + * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used + * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) + * CURL_FORMADD_MEMORY if an HttpPost struct cannot be allocated + * CURL_FORMADD_MEMORY if some allocation for string copying failed. + * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + ***************************************************************************/ + +static +CURLFORMcode FormAdd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + va_list params) +{ + struct FormInfo *first_form, *current_form, *form = NULL; + CURLFORMcode return_value = CURL_FORMADD_OK; + const char *prevtype = NULL; + struct curl_httppost *post = NULL; + CURLformoption option; + struct curl_forms *forms = NULL; + char *array_value = NULL; /* value read from an array */ + + /* This is a state variable, that if TRUE means that we are parsing an + array that we got passed to us. If FALSE we are parsing the input + va_list arguments. */ + bool array_state = FALSE; + + /* + * We need to allocate the first struct to fill in. + */ + first_form = calloc(1, sizeof(struct FormInfo)); + if(!first_form) + return CURL_FORMADD_MEMORY; + + current_form = first_form; + + /* + * Loop through all the options set. Break if we have an error to report. + */ + while(return_value == CURL_FORMADD_OK) { + + /* first see if we have more parts of the array param */ + if(array_state && forms) { + /* get the upcoming option from the given array */ + option = forms->option; + array_value = (char *)forms->value; + + forms++; /* advance this to next entry */ + if(CURLFORM_END == option) { + /* end of array state */ + array_state = FALSE; + continue; + } + } + else { + /* This is not array-state, get next option. This gets an 'int' with + va_arg() because CURLformoption might be a smaller type than int and + might cause compiler warnings and wrong behavior. */ + option = (CURLformoption)va_arg(params, int); + if(CURLFORM_END == option) + break; + } + + switch(option) { + case CURLFORM_ARRAY: + if(array_state) + /* we do not support an array from within an array */ + return_value = CURL_FORMADD_ILLEGAL_ARRAY; + else { + forms = va_arg(params, struct curl_forms *); + if(forms) + array_state = TRUE; + else + return_value = CURL_FORMADD_NULL; + } + break; + + /* + * Set the Name property. + */ + case CURLFORM_PTRNAME: + current_form->flags |= HTTPPOST_PTRNAME; /* fall through */ + + FALLTHROUGH(); + case CURLFORM_COPYNAME: + if(current_form->name) + return_value = CURL_FORMADD_OPTION_TWICE; + else { + char *name = array_state? + array_value:va_arg(params, char *); + if(name) + current_form->name = name; /* store for the moment */ + else + return_value = CURL_FORMADD_NULL; + } + break; + case CURLFORM_NAMELENGTH: + if(current_form->namelength) + return_value = CURL_FORMADD_OPTION_TWICE; + else + current_form->namelength = + array_state?(size_t)array_value:(size_t)va_arg(params, long); + break; + + /* + * Set the contents property. + */ + case CURLFORM_PTRCONTENTS: + current_form->flags |= HTTPPOST_PTRCONTENTS; + FALLTHROUGH(); + case CURLFORM_COPYCONTENTS: + if(current_form->value) + return_value = CURL_FORMADD_OPTION_TWICE; + else { + char *value = + array_state?array_value:va_arg(params, char *); + if(value) + current_form->value = value; /* store for the moment */ + else + return_value = CURL_FORMADD_NULL; + } + break; + case CURLFORM_CONTENTSLENGTH: + current_form->contentslength = + array_state?(size_t)array_value:(size_t)va_arg(params, long); + break; + + case CURLFORM_CONTENTLEN: + current_form->flags |= CURL_HTTPPOST_LARGE; + current_form->contentslength = + array_state?(curl_off_t)(size_t)array_value:va_arg(params, curl_off_t); + break; + + /* Get contents from a given filename */ + case CURLFORM_FILECONTENT: + if(current_form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE)) + return_value = CURL_FORMADD_OPTION_TWICE; + else { + const char *filename = array_state? + array_value:va_arg(params, char *); + if(filename) { + current_form->value = strdup(filename); + if(!current_form->value) + return_value = CURL_FORMADD_MEMORY; + else { + current_form->flags |= HTTPPOST_READFILE; + current_form->value_alloc = TRUE; + } + } + else + return_value = CURL_FORMADD_NULL; + } + break; + + /* We upload a file */ + case CURLFORM_FILE: + { + const char *filename = array_state?array_value: + va_arg(params, char *); + + if(current_form->value) { + if(current_form->flags & HTTPPOST_FILENAME) { + if(filename) { + char *fname = strdup(filename); + if(!fname) + return_value = CURL_FORMADD_MEMORY; + else { + form = AddFormInfo(fname, NULL, current_form); + if(!form) { + free(fname); + return_value = CURL_FORMADD_MEMORY; + } + else { + form->value_alloc = TRUE; + current_form = form; + form = NULL; + } + } + } + else + return_value = CURL_FORMADD_NULL; + } + else + return_value = CURL_FORMADD_OPTION_TWICE; + } + else { + if(filename) { + current_form->value = strdup(filename); + if(!current_form->value) + return_value = CURL_FORMADD_MEMORY; + else { + current_form->flags |= HTTPPOST_FILENAME; + current_form->value_alloc = TRUE; + } + } + else + return_value = CURL_FORMADD_NULL; + } + break; + } + + case CURLFORM_BUFFERPTR: + current_form->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER; + if(current_form->buffer) + return_value = CURL_FORMADD_OPTION_TWICE; + else { + char *buffer = + array_state?array_value:va_arg(params, char *); + if(buffer) { + current_form->buffer = buffer; /* store for the moment */ + current_form->value = buffer; /* make it non-NULL to be accepted + as fine */ + } + else + return_value = CURL_FORMADD_NULL; + } + break; + + case CURLFORM_BUFFERLENGTH: + if(current_form->bufferlength) + return_value = CURL_FORMADD_OPTION_TWICE; + else + current_form->bufferlength = + array_state?(size_t)array_value:(size_t)va_arg(params, long); + break; + + case CURLFORM_STREAM: + current_form->flags |= HTTPPOST_CALLBACK; + if(current_form->userp) + return_value = CURL_FORMADD_OPTION_TWICE; + else { + char *userp = + array_state?array_value:va_arg(params, char *); + if(userp) { + current_form->userp = userp; + current_form->value = userp; /* this is not strictly true but we + derive a value from this later on + and we need this non-NULL to be + accepted as a fine form part */ + } + else + return_value = CURL_FORMADD_NULL; + } + break; + + case CURLFORM_CONTENTTYPE: + { + const char *contenttype = + array_state?array_value:va_arg(params, char *); + if(current_form->contenttype) { + if(current_form->flags & HTTPPOST_FILENAME) { + if(contenttype) { + char *type = strdup(contenttype); + if(!type) + return_value = CURL_FORMADD_MEMORY; + else { + form = AddFormInfo(NULL, type, current_form); + if(!form) { + free(type); + return_value = CURL_FORMADD_MEMORY; + } + else { + form->contenttype_alloc = TRUE; + current_form = form; + form = NULL; + } + } + } + else + return_value = CURL_FORMADD_NULL; + } + else + return_value = CURL_FORMADD_OPTION_TWICE; + } + else { + if(contenttype) { + current_form->contenttype = strdup(contenttype); + if(!current_form->contenttype) + return_value = CURL_FORMADD_MEMORY; + else + current_form->contenttype_alloc = TRUE; + } + else + return_value = CURL_FORMADD_NULL; + } + break; + } + case CURLFORM_CONTENTHEADER: + { + /* this "cast increases required alignment of target type" but + we consider it OK anyway */ + struct curl_slist *list = array_state? + (struct curl_slist *)(void *)array_value: + va_arg(params, struct curl_slist *); + + if(current_form->contentheader) + return_value = CURL_FORMADD_OPTION_TWICE; + else + current_form->contentheader = list; + + break; + } + case CURLFORM_FILENAME: + case CURLFORM_BUFFER: + { + const char *filename = array_state?array_value: + va_arg(params, char *); + if(current_form->showfilename) + return_value = CURL_FORMADD_OPTION_TWICE; + else { + current_form->showfilename = strdup(filename); + if(!current_form->showfilename) + return_value = CURL_FORMADD_MEMORY; + else + current_form->showfilename_alloc = TRUE; + } + break; + } + default: + return_value = CURL_FORMADD_UNKNOWN_OPTION; + break; + } + } + + if(CURL_FORMADD_OK != return_value) { + /* On error, free allocated fields for all nodes of the FormInfo linked + list without deallocating nodes. List nodes are deallocated later on */ + struct FormInfo *ptr; + for(ptr = first_form; ptr != NULL; ptr = ptr->more) { + if(ptr->name_alloc) { + Curl_safefree(ptr->name); + ptr->name_alloc = FALSE; + } + if(ptr->value_alloc) { + Curl_safefree(ptr->value); + ptr->value_alloc = FALSE; + } + if(ptr->contenttype_alloc) { + Curl_safefree(ptr->contenttype); + ptr->contenttype_alloc = FALSE; + } + if(ptr->showfilename_alloc) { + Curl_safefree(ptr->showfilename); + ptr->showfilename_alloc = FALSE; + } + } + } + + if(CURL_FORMADD_OK == return_value) { + /* go through the list, check for completeness and if everything is + * alright add the HttpPost item otherwise set return_value accordingly */ + + post = NULL; + for(form = first_form; + form != NULL; + form = form->more) { + if(((!form->name || !form->value) && !post) || + ( (form->contentslength) && + (form->flags & HTTPPOST_FILENAME) ) || + ( (form->flags & HTTPPOST_FILENAME) && + (form->flags & HTTPPOST_PTRCONTENTS) ) || + + ( (!form->buffer) && + (form->flags & HTTPPOST_BUFFER) && + (form->flags & HTTPPOST_PTRBUFFER) ) || + + ( (form->flags & HTTPPOST_READFILE) && + (form->flags & HTTPPOST_PTRCONTENTS) ) + ) { + return_value = CURL_FORMADD_INCOMPLETE; + break; + } + if(((form->flags & HTTPPOST_FILENAME) || + (form->flags & HTTPPOST_BUFFER)) && + !form->contenttype) { + char *f = (form->flags & HTTPPOST_BUFFER)? + form->showfilename : form->value; + char const *type; + type = Curl_mime_contenttype(f); + if(!type) + type = prevtype; + if(!type) + type = FILE_CONTENTTYPE_DEFAULT; + + /* our contenttype is missing */ + form->contenttype = strdup(type); + if(!form->contenttype) { + return_value = CURL_FORMADD_MEMORY; + break; + } + form->contenttype_alloc = TRUE; + } + if(form->name && form->namelength) { + /* Name should not contain nul bytes. */ + size_t i; + for(i = 0; i < form->namelength; i++) + if(!form->name[i]) { + return_value = CURL_FORMADD_NULL; + break; + } + if(return_value != CURL_FORMADD_OK) + break; + } + if(!(form->flags & HTTPPOST_PTRNAME) && + (form == first_form) ) { + /* Note that there is small risk that form->name is NULL here if the + app passed in a bad combo, so we better check for that first. */ + if(form->name) { + /* copy name (without strdup; possibly not null-terminated) */ + form->name = Curl_memdup0(form->name, form->namelength? + form->namelength: + strlen(form->name)); + } + if(!form->name) { + return_value = CURL_FORMADD_MEMORY; + break; + } + form->name_alloc = TRUE; + } + if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE | + HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER | + HTTPPOST_CALLBACK)) && form->value) { + /* copy value (without strdup; possibly contains null characters) */ + size_t clen = (size_t) form->contentslength; + if(!clen) + clen = strlen(form->value) + 1; + + form->value = Curl_memdup(form->value, clen); + + if(!form->value) { + return_value = CURL_FORMADD_MEMORY; + break; + } + form->value_alloc = TRUE; + } + post = AddHttpPost(form->name, form->namelength, + form->value, form->contentslength, + form->buffer, form->bufferlength, + form->contenttype, form->flags, + form->contentheader, form->showfilename, + form->userp, + post, httppost, + last_post); + + if(!post) { + return_value = CURL_FORMADD_MEMORY; + break; + } + + if(form->contenttype) + prevtype = form->contenttype; + } + if(CURL_FORMADD_OK != return_value) { + /* On error, free allocated fields for nodes of the FormInfo linked + list which are not already owned by the httppost linked list + without deallocating nodes. List nodes are deallocated later on */ + struct FormInfo *ptr; + for(ptr = form; ptr != NULL; ptr = ptr->more) { + if(ptr->name_alloc) { + Curl_safefree(ptr->name); + ptr->name_alloc = FALSE; + } + if(ptr->value_alloc) { + Curl_safefree(ptr->value); + ptr->value_alloc = FALSE; + } + if(ptr->contenttype_alloc) { + Curl_safefree(ptr->contenttype); + ptr->contenttype_alloc = FALSE; + } + if(ptr->showfilename_alloc) { + Curl_safefree(ptr->showfilename); + ptr->showfilename_alloc = FALSE; + } + } + } + } + + /* Always deallocate FormInfo linked list nodes without touching node + fields given that these have either been deallocated or are owned + now by the httppost linked list */ + while(first_form) { + struct FormInfo *ptr = first_form->more; + free(first_form); + first_form = ptr; + } + + return return_value; +} + +/* + * curl_formadd() is a public API to add a section to the multipart formpost. + * + * @unittest: 1308 + */ + +CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...) +{ + va_list arg; + CURLFORMcode result; + va_start(arg, last_post); + result = FormAdd(httppost, last_post, arg); + va_end(arg); + return result; +} + +/* + * curl_formget() + * Serialize a curl_httppost struct. + * Returns 0 on success. + * + * @unittest: 1308 + */ +int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append) +{ + CURLcode result; + curl_mimepart toppart; + + Curl_mime_initpart(&toppart); /* default form is empty */ + result = Curl_getformdata(NULL, &toppart, form, NULL); + if(!result) + result = Curl_mime_prepare_headers(NULL, &toppart, "multipart/form-data", + NULL, MIMESTRATEGY_FORM); + + while(!result) { + char buffer[8192]; + size_t nread = Curl_mime_read(buffer, 1, sizeof(buffer), &toppart); + + if(!nread) + break; + + if(nread > sizeof(buffer) || append(arg, buffer, nread) != nread) { + result = CURLE_READ_ERROR; + if(nread == CURL_READFUNC_ABORT) + result = CURLE_ABORTED_BY_CALLBACK; + } + } + + Curl_mime_cleanpart(&toppart); + return (int) result; +} + +/* + * curl_formfree() is an external function to free up a whole form post + * chain + */ +void curl_formfree(struct curl_httppost *form) +{ + struct curl_httppost *next; + + if(!form) + /* no form to free, just get out of this */ + return; + + do { + next = form->next; /* the following form line */ + + /* recurse to sub-contents */ + curl_formfree(form->more); + + if(!(form->flags & HTTPPOST_PTRNAME)) + free(form->name); /* free the name */ + if(!(form->flags & + (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK)) + ) + free(form->contents); /* free the contents */ + free(form->contenttype); /* free the content type */ + free(form->showfilename); /* free the faked filename */ + free(form); /* free the struct */ + form = next; + } while(form); /* continue */ +} + + +/* Set mime part name, taking care of non null-terminated name string. */ +static CURLcode setname(curl_mimepart *part, const char *name, size_t len) +{ + char *zname; + CURLcode res; + + if(!name || !len) + return curl_mime_name(part, name); + zname = Curl_memdup0(name, len); + if(!zname) + return CURLE_OUT_OF_MEMORY; + res = curl_mime_name(part, zname); + free(zname); + return res; +} + +/* wrap call to fseeko so it matches the calling convention of callback */ +static int fseeko_wrapper(void *stream, curl_off_t offset, int whence) +{ +#if defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO) + return fseeko(stream, (off_t)offset, whence); +#elif defined(HAVE__FSEEKI64) + return _fseeki64(stream, (__int64)offset, whence); +#else + if(offset > LONG_MAX) + return -1; + return fseek(stream, (long)offset, whence); +#endif +} + +/* + * Curl_getformdata() converts a linked list of "meta data" into a mime + * structure. The input list is in 'post', while the output is stored in + * mime part at '*finalform'. + * + * This function will not do a failf() for the potential memory failures but + * should for all other errors it spots. Just note that this function MAY get + * a NULL pointer in the 'data' argument. + */ + +CURLcode Curl_getformdata(struct Curl_easy *data, + curl_mimepart *finalform, + struct curl_httppost *post, + curl_read_callback fread_func) +{ + CURLcode result = CURLE_OK; + curl_mime *form = NULL; + curl_mimepart *part; + struct curl_httppost *file; + + Curl_mime_cleanpart(finalform); /* default form is empty */ + + if(!post) + return result; /* no input => no output! */ + + form = curl_mime_init(data); + if(!form) + result = CURLE_OUT_OF_MEMORY; + + if(!result) + result = curl_mime_subparts(finalform, form); + + /* Process each top part. */ + for(; !result && post; post = post->next) { + /* If we have more than a file here, create a mime subpart and fill it. */ + curl_mime *multipart = form; + if(post->more) { + part = curl_mime_addpart(form); + if(!part) + result = CURLE_OUT_OF_MEMORY; + if(!result) + result = setname(part, post->name, post->namelength); + if(!result) { + multipart = curl_mime_init(data); + if(!multipart) + result = CURLE_OUT_OF_MEMORY; + } + if(!result) + result = curl_mime_subparts(part, multipart); + } + + /* Generate all the part contents. */ + for(file = post; !result && file; file = file->more) { + /* Create the part. */ + part = curl_mime_addpart(multipart); + if(!part) + result = CURLE_OUT_OF_MEMORY; + + /* Set the headers. */ + if(!result) + result = curl_mime_headers(part, file->contentheader, 0); + + /* Set the content type. */ + if(!result && file->contenttype) + result = curl_mime_type(part, file->contenttype); + + /* Set field name. */ + if(!result && !post->more) + result = setname(part, post->name, post->namelength); + + /* Process contents. */ + if(!result) { + curl_off_t clen = post->contentslength; + + if(post->flags & CURL_HTTPPOST_LARGE) + clen = post->contentlen; + + if(post->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE)) { + if(!strcmp(file->contents, "-")) { + /* There are a few cases where the code below will not work; in + particular, freopen(stdin) by the caller is not guaranteed + to result as expected. This feature has been kept for backward + compatibility: use of "-" pseudo filename should be avoided. */ + result = curl_mime_data_cb(part, (curl_off_t) -1, + (curl_read_callback) fread, + fseeko_wrapper, + NULL, (void *) stdin); + } + else + result = curl_mime_filedata(part, file->contents); + if(!result && (post->flags & HTTPPOST_READFILE)) + result = curl_mime_filename(part, NULL); + } + else if(post->flags & HTTPPOST_BUFFER) + result = curl_mime_data(part, post->buffer, + post->bufferlength? post->bufferlength: -1); + else if(post->flags & HTTPPOST_CALLBACK) { + /* the contents should be read with the callback and the size is set + with the contentslength */ + if(!clen) + clen = -1; + result = curl_mime_data_cb(part, clen, + fread_func, NULL, NULL, post->userp); + } + else { + size_t uclen; + if(!clen) + uclen = CURL_ZERO_TERMINATED; + else + uclen = (size_t)clen; + result = curl_mime_data(part, post->contents, uclen); + } + } + + /* Set fake filename. */ + if(!result && post->showfilename) + if(post->more || (post->flags & (HTTPPOST_FILENAME | HTTPPOST_BUFFER | + HTTPPOST_CALLBACK))) + result = curl_mime_filename(part, post->showfilename); + } + } + + if(result) + Curl_mime_cleanpart(finalform); + + return result; +} + +#else +/* if disabled */ +CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...) +{ + (void)httppost; + (void)last_post; + return CURL_FORMADD_DISABLED; +} + +int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append) +{ + (void) form; + (void) arg; + (void) append; + return CURL_FORMADD_DISABLED; +} + +void curl_formfree(struct curl_httppost *form) +{ + (void)form; + /* Nothing to do. */ +} + +#endif /* if disabled */ diff --git a/third_party/curl/lib/formdata.h b/third_party/curl/lib/formdata.h new file mode 100644 index 0000000000..2ed96ffcf6 --- /dev/null +++ b/third_party/curl/lib/formdata.h @@ -0,0 +1,59 @@ +#ifndef HEADER_CURL_FORMDATA_H +#define HEADER_CURL_FORMDATA_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_FORM_API + +/* used by FormAdd for temporary storage */ +struct FormInfo { + char *name; + size_t namelength; + char *value; + curl_off_t contentslength; + char *contenttype; + long flags; + char *buffer; /* pointer to existing buffer used for file upload */ + size_t bufferlength; + char *showfilename; /* The filename to show. If not set, the actual + filename will be used */ + char *userp; /* pointer for the read callback */ + struct curl_slist *contentheader; + struct FormInfo *more; + bool name_alloc; + bool value_alloc; + bool contenttype_alloc; + bool showfilename_alloc; +}; + +CURLcode Curl_getformdata(struct Curl_easy *data, + curl_mimepart *, + struct curl_httppost *post, + curl_read_callback fread_func); +#endif /* CURL_DISABLE_FORM_API */ + + +#endif /* HEADER_CURL_FORMDATA_H */ diff --git a/third_party/curl/lib/ftp.c b/third_party/curl/lib/ftp.c new file mode 100644 index 0000000000..ae340c44af --- /dev/null +++ b/third_party/curl/lib/ftp.c @@ -0,0 +1,4590 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_FTP + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include +#include "urldata.h" +#include "sendf.h" +#include "if2ip.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "escape.h" +#include "http.h" /* for HTTP proxy tunnel stuff */ +#include "ftp.h" +#include "fileinfo.h" +#include "ftplistparser.h" +#include "curl_range.h" +#include "curl_krb5.h" +#include "strtoofft.h" +#include "strcase.h" +#include "vtls/vtls.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "connect.h" +#include "strerror.h" +#include "inet_ntop.h" +#include "inet_pton.h" +#include "select.h" +#include "parsedate.h" /* for the week day and month names */ +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "multiif.h" +#include "url.h" +#include "speedcheck.h" +#include "warnless.h" +#include "http_proxy.h" +#include "socks.h" +#include "strdup.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN 16 +#endif + +/* macro to check for a three-digit ftp status code at the start of the + given string */ +#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \ + ISDIGIT(line[2])) + +/* macro to check for the last line in an FTP server response */ +#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3])) + +#ifdef CURL_DISABLE_VERBOSE_STRINGS +#define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt +#define FTP_CSTATE(c) "" +#define FTP_DSTATE(d) "" +#else /* CURL_DISABLE_VERBOSE_STRINGS */ + /* for tracing purposes */ +static const char * const ftp_state_names[]={ + "STOP", + "WAIT220", + "AUTH", + "USER", + "PASS", + "ACCT", + "PBSZ", + "PROT", + "CCC", + "PWD", + "SYST", + "NAMEFMT", + "QUOTE", + "RETR_PREQUOTE", + "STOR_PREQUOTE", + "POSTQUOTE", + "CWD", + "MKD", + "MDTM", + "TYPE", + "LIST_TYPE", + "RETR_TYPE", + "STOR_TYPE", + "SIZE", + "RETR_SIZE", + "STOR_SIZE", + "REST", + "RETR_REST", + "PORT", + "PRET", + "PASV", + "LIST", + "RETR", + "STOR", + "QUIT" +}; +#define FTP_CSTATE(c) ((c)? ftp_state_names[(c)->proto.ftpc.state] : "???") +#define FTP_DSTATE(d) (((d) && (d)->conn)? \ + ftp_state_names[(d)->conn->proto.ftpc.state] : "???") + +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + +/* This is the ONLY way to change FTP state! */ +static void _ftp_state(struct Curl_easy *data, + ftpstate newstate +#ifdef DEBUGBUILD + , int lineno +#endif + ) +{ + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) +#ifdef DEBUGBUILD + (void)lineno; +#endif +#else /* CURL_DISABLE_VERBOSE_STRINGS */ + if(ftpc->state != newstate) +#ifdef DEBUGBUILD + CURL_TRC_FTP(data, "[%s] -> [%s] (line %d)", FTP_DSTATE(data), + ftp_state_names[newstate], lineno); +#else + CURL_TRC_FTP(data, "[%s] -> [%s]", FTP_DSTATE(data), + ftp_state_names[newstate]); +#endif +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + + ftpc->state = newstate; +} + + +/* Local API functions */ +#ifndef DEBUGBUILD +#define ftp_state(x,y) _ftp_state(x,y) +#else /* !DEBUGBUILD */ +#define ftp_state(x,y) _ftp_state(x,y,__LINE__) +#endif /* DEBUGBUILD */ + +static CURLcode ftp_sendquote(struct Curl_easy *data, + struct connectdata *conn, + struct curl_slist *quote); +static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn); +static CURLcode ftp_parse_url_path(struct Curl_easy *data); +static CURLcode ftp_regular_transfer(struct Curl_easy *data, bool *done); +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void ftp_pasv_verbose(struct Curl_easy *data, + struct Curl_addrinfo *ai, + char *newhost, /* ascii version */ + int port); +#endif +static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data); +static CURLcode ftp_state_mdtm(struct Curl_easy *data); +static CURLcode ftp_state_quote(struct Curl_easy *data, + bool init, ftpstate instate); +static CURLcode ftp_nb_type(struct Curl_easy *data, + struct connectdata *conn, + bool ascii, ftpstate newstate); +static int ftp_need_type(struct connectdata *conn, + bool ascii); +static CURLcode ftp_do(struct Curl_easy *data, bool *done); +static CURLcode ftp_done(struct Curl_easy *data, + CURLcode, bool premature); +static CURLcode ftp_connect(struct Curl_easy *data, bool *done); +static CURLcode ftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection); +static CURLcode ftp_do_more(struct Curl_easy *data, int *completed); +static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done); +static int ftp_getsock(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t *socks); +static int ftp_domore_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); +static CURLcode ftp_doing(struct Curl_easy *data, + bool *dophase_done); +static CURLcode ftp_setup_connection(struct Curl_easy *data, + struct connectdata *conn); +static CURLcode init_wc_data(struct Curl_easy *data); +static CURLcode wc_statemach(struct Curl_easy *data); +static void wc_data_dtor(void *ptr); +static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize); +static CURLcode ftp_readresp(struct Curl_easy *data, + int sockindex, + struct pingpong *pp, + int *ftpcode, + size_t *size); +static CURLcode ftp_dophase_done(struct Curl_easy *data, + bool connected); + +/* + * FTP protocol handler. + */ + +const struct Curl_handler Curl_handler_ftp = { + "ftp", /* scheme */ + ftp_setup_connection, /* setup_connection */ + ftp_do, /* do_it */ + ftp_done, /* done */ + ftp_do_more, /* do_more */ + ftp_connect, /* connect_it */ + ftp_multi_statemach, /* connecting */ + ftp_doing, /* doing */ + ftp_getsock, /* proto_getsock */ + ftp_getsock, /* doing_getsock */ + ftp_domore_getsock, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_FTP, /* defport */ + CURLPROTO_FTP, /* protocol */ + CURLPROTO_FTP, /* family */ + PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD | + PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP | + PROTOPT_WILDCARD /* flags */ +}; + + +#ifdef USE_SSL +/* + * FTPS protocol handler. + */ + +const struct Curl_handler Curl_handler_ftps = { + "ftps", /* scheme */ + ftp_setup_connection, /* setup_connection */ + ftp_do, /* do_it */ + ftp_done, /* done */ + ftp_do_more, /* do_more */ + ftp_connect, /* connect_it */ + ftp_multi_statemach, /* connecting */ + ftp_doing, /* doing */ + ftp_getsock, /* proto_getsock */ + ftp_getsock, /* doing_getsock */ + ftp_domore_getsock, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_FTPS, /* defport */ + CURLPROTO_FTPS, /* protocol */ + CURLPROTO_FTP, /* family */ + PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION | + PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD /* flags */ +}; +#endif + +static void close_secondarysocket(struct Curl_easy *data) +{ + CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_DSTATE(data)); + Curl_conn_close(data, SECONDARYSOCKET); + Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET); +} + +/* + * NOTE: back in the old days, we added code in the FTP code that made NOBODY + * requests on files respond with headers passed to the client/stdout that + * looked like HTTP ones. + * + * This approach is not very elegant, it causes confusion and is error-prone. + * It is subject for removal at the next (or at least a future) soname bump. + * Until then you can test the effects of the removal by undefining the + * following define named CURL_FTP_HTTPSTYLE_HEAD. + */ +#define CURL_FTP_HTTPSTYLE_HEAD 1 + +static void freedirs(struct ftp_conn *ftpc) +{ + if(ftpc->dirs) { + int i; + for(i = 0; i < ftpc->dirdepth; i++) { + free(ftpc->dirs[i]); + ftpc->dirs[i] = NULL; + } + free(ftpc->dirs); + ftpc->dirs = NULL; + ftpc->dirdepth = 0; + } + Curl_safefree(ftpc->file); + + /* no longer of any use */ + Curl_safefree(ftpc->newhost); +} + +#ifdef CURL_DO_LINEEND_CONV +/*********************************************************************** + * + * Lineend Conversions + * On ASCII transfers, e.g. directory listings, we might get lines + * ending in '\r\n' and we prefer just '\n'. + * We might also get a lonely '\r' which we convert into a '\n'. + */ +struct ftp_cw_lc_ctx { + struct Curl_cwriter super; + bool newline_pending; +}; + +static CURLcode ftp_cw_lc_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t blen) +{ + static const char nl = '\n'; + struct ftp_cw_lc_ctx *ctx = writer->ctx; + + if(!(type & CLIENTWRITE_BODY) || + data->conn->proto.ftpc.transfertype != 'A') + return Curl_cwriter_write(data, writer->next, type, buf, blen); + + /* ASCII mode BODY data, convert lineends */ + while(blen) { + /* do not pass EOS when writing parts */ + int chunk_type = (type & ~CLIENTWRITE_EOS); + const char *cp; + size_t chunk_len; + CURLcode result; + + if(ctx->newline_pending) { + if(buf[0] != '\n') { + /* previous chunk ended in '\r' and we do not see a '\n' in this one, + * need to write a newline. */ + result = Curl_cwriter_write(data, writer->next, chunk_type, &nl, 1); + if(result) + return result; + } + /* either we just wrote the newline or it is part of the next + * chunk of bytes we write. */ + data->state.crlf_conversions++; + ctx->newline_pending = FALSE; + } + + cp = memchr(buf, '\r', blen); + if(!cp) + break; + + /* write the bytes before the '\r', excluding the '\r' */ + chunk_len = cp - buf; + if(chunk_len) { + result = Curl_cwriter_write(data, writer->next, chunk_type, + buf, chunk_len); + if(result) + return result; + } + /* skip the '\r', we now have a newline pending */ + buf = cp + 1; + blen = blen - chunk_len - 1; + ctx->newline_pending = TRUE; + } + + /* Any remaining data does not contain a '\r' */ + if(blen) { + DEBUGASSERT(!ctx->newline_pending); + return Curl_cwriter_write(data, writer->next, type, buf, blen); + } + else if(type & CLIENTWRITE_EOS) { + /* EndOfStream, if we have a trailing cr, now is the time to write it */ + if(ctx->newline_pending) { + ctx->newline_pending = FALSE; + data->state.crlf_conversions++; + return Curl_cwriter_write(data, writer->next, type, &nl, 1); + } + /* Always pass on the EOS type indicator */ + return Curl_cwriter_write(data, writer->next, type, buf, 0); + } + return CURLE_OK; +} + +static const struct Curl_cwtype ftp_cw_lc = { + "ftp-lineconv", + NULL, + Curl_cwriter_def_init, + ftp_cw_lc_write, + Curl_cwriter_def_close, + sizeof(struct ftp_cw_lc_ctx) +}; + +#endif /* CURL_DO_LINEEND_CONV */ +/*********************************************************************** + * + * AcceptServerConnect() + * + * After connection request is received from the server this function is + * called to accept the connection and close the listening socket + * + */ +static CURLcode AcceptServerConnect(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + curl_socket_t sock = conn->sock[SECONDARYSOCKET]; + curl_socket_t s = CURL_SOCKET_BAD; +#ifdef USE_IPV6 + struct Curl_sockaddr_storage add; +#else + 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); + } + + if(CURL_SOCKET_BAD == s) { + failf(data, "Error accept()ing server connect"); + return CURLE_FTP_PORT_FAILED; + } + infof(data, "Connection accepted from server"); + /* when this happens within the DO state it is important that we mark us as + not needing DO_MORE anymore */ + conn->bits.do_more = FALSE; + + (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ + /* Replace any filter on SECONDARY with one listening on this socket */ + result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s); + if(result) { + sclose(s); + return result; + } + + if(data->set.fsockopt) { + int error = 0; + + /* activate callback for setting socket options */ + Curl_set_in_callback(data, true); + error = data->set.fsockopt(data->set.sockopt_client, + s, + CURLSOCKTYPE_ACCEPT); + Curl_set_in_callback(data, false); + + if(error) { + close_secondarysocket(data); + return CURLE_ABORTED_BY_CALLBACK; + } + } + + return CURLE_OK; + +} + +/* + * ftp_timeleft_accept() returns the amount of milliseconds left allowed for + * waiting server to connect. If the value is negative, the timeout time has + * already elapsed. + * + * The start time is stored in progress.t_acceptdata - as set with + * Curl_pgrsTime(..., TIMER_STARTACCEPT); + * + */ +static timediff_t ftp_timeleft_accept(struct Curl_easy *data) +{ + timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT; + timediff_t other; + struct curltime now; + + if(data->set.accepttimeout > 0) + timeout_ms = data->set.accepttimeout; + + now = Curl_now(); + + /* check if the generic timeout possibly is set shorter */ + other = Curl_timeleft(data, &now, FALSE); + if(other && (other < timeout_ms)) + /* note that this also works fine for when other happens to be negative + due to it already having elapsed */ + timeout_ms = other; + else { + /* subtract elapsed time */ + timeout_ms -= Curl_timediff(now, data->progress.t_acceptdata); + if(!timeout_ms) + /* avoid returning 0 as that means no timeout! */ + return -1; + } + + return timeout_ms; +} + + +/*********************************************************************** + * + * ReceivedServerConnect() + * + * After allowing server to connect to us from data port, this function + * checks both data connection for connection establishment and ctrl + * connection for a negative response regarding a failure in connecting + * + */ +static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received) +{ + struct connectdata *conn = data->conn; + curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET]; + curl_socket_t data_sock = conn->sock[SECONDARYSOCKET]; + struct ftp_conn *ftpc = &conn->proto.ftpc; + struct pingpong *pp = &ftpc->pp; + int socketstate = 0; + timediff_t timeout_ms; + ssize_t nread; + int ftpcode; + bool response = FALSE; + + *received = FALSE; + + timeout_ms = ftp_timeleft_accept(data); + infof(data, "Checking for server connect"); + 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; + } + + /* First check whether there is a cached response from server */ + if(Curl_dyn_len(&pp->recvbuf) && (*Curl_dyn_ptr(&pp->recvbuf) > '3')) { + /* Data connection could not be established, let's return */ + infof(data, "There is negative response in cache while serv connect"); + (void)Curl_GetFTPResponse(data, &nread, &ftpcode); + return CURLE_FTP_ACCEPT_FAILED; + } + + if(pp->overflow) + /* there is pending control data still in the buffer to read */ + response = TRUE; + else + socketstate = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0); + + /* see if the connection request is already here */ + switch(socketstate) { + case -1: /* error */ + /* let's die here */ + failf(data, "Error while waiting for server connect"); + return CURLE_FTP_ACCEPT_FAILED; + case 0: /* Server connect is not received yet */ + break; /* loop */ + default: + if(socketstate & CURL_CSELECT_IN2) { + infof(data, "Ready to accept data connection from server"); + *received = TRUE; + } + else if(socketstate & CURL_CSELECT_IN) + response = TRUE; + break; + } + if(response) { + infof(data, "Ctrl conn has data while waiting for data conn"); + if(pp->overflow > 3) { + char *r = Curl_dyn_ptr(&pp->recvbuf); + + DEBUGASSERT((pp->overflow + pp->nfinal) <= + Curl_dyn_len(&pp->recvbuf)); + /* move over the most recently handled response line */ + r += pp->nfinal; + + if(LASTLINE(r)) { + int status = curlx_sltosi(strtol(r, NULL, 10)); + if(status == 226) { + /* funny timing situation where we get the final message on the + control connection before traffic on the data connection has been + noticed. Leave the 226 in there and use this as a trigger to read + the data socket. */ + infof(data, "Got 226 before data activity"); + *received = TRUE; + return CURLE_OK; + } + } + } + + (void)Curl_GetFTPResponse(data, &nread, &ftpcode); + + infof(data, "FTP code: %03d", ftpcode); + + if(ftpcode/100 > 3) + return CURLE_FTP_ACCEPT_FAILED; + + return CURLE_WEIRD_SERVER_REPLY; + } + + return CURLE_OK; +} + + +/*********************************************************************** + * + * InitiateTransfer() + * + * After connection from server is accepted this function is called to + * setup transfer parameters and initiate the data transfer. + * + */ +static CURLcode InitiateTransfer(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + bool connected; + + CURL_TRC_FTP(data, "InitiateTransfer()"); + if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port && + !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) { + result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET); + if(result) + return result; + } + result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected); + if(result || !connected) + return result; + + if(conn->proto.ftpc.state_saved == FTP_STOR) { + /* When we know we are uploading a specified file, we can get the file + size prior to the actual upload. */ + Curl_pgrsSetUploadSize(data, data->state.infilesize); + + /* set the SO_SNDBUF for the secondary socket for those who need it */ + Curl_sndbuf_init(conn->sock[SECONDARYSOCKET]); + + Curl_xfer_setup2(data, CURL_XFER_SEND, -1, TRUE); + } + else { + /* FTP download: */ + Curl_xfer_setup2(data, CURL_XFER_RECV, + conn->proto.ftpc.retr_size_saved, TRUE); + } + + conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */ + ftp_state(data, FTP_STOP); + + return CURLE_OK; +} + +/*********************************************************************** + * + * AllowServerConnect() + * + * When we have issue the PORT command, we have told the server to connect to + * us. This function checks whether data connection is established if so it is + * accepted. + * + */ +static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected) +{ + timediff_t timeout_ms; + CURLcode result = CURLE_OK; + + *connected = FALSE; + infof(data, "Preparing for accepting server on data port"); + + /* Save the time we start accepting server connect */ + Curl_pgrsTime(data, TIMER_STARTACCEPT); + + timeout_ms = ftp_timeleft_accept(data); + if(timeout_ms < 0) { + /* if a timeout was already reached, bail out */ + failf(data, "Accept timeout occurred while waiting server connect"); + result = CURLE_FTP_ACCEPT_TIMEOUT; + goto out; + } + + /* see if the connection request is already here */ + result = ReceivedServerConnect(data, connected); + if(result) + goto out; + + if(*connected) { + result = AcceptServerConnect(data); + if(result) + goto out; + + result = InitiateTransfer(data); + if(result) + goto out; + } + else { + /* Add timeout to multi handle and break out of the loop */ + Curl_expire(data, data->set.accepttimeout ? + data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, + EXPIRE_FTP_ACCEPT); + } + +out: + CURL_TRC_FTP(data, "AllowServerConnect() -> %d", result); + return result; +} + +static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn, + char *line, size_t len, int *code) +{ + (void)data; + (void)conn; + + if((len > 3) && LASTLINE(line)) { + *code = curlx_sltosi(strtol(line, NULL, 10)); + return TRUE; + } + + return FALSE; +} + +static CURLcode ftp_readresp(struct Curl_easy *data, + int sockindex, + struct pingpong *pp, + int *ftpcode, /* return the ftp-code if done */ + size_t *size) /* size of the response */ +{ + int code; + CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size); + +#ifdef HAVE_GSSAPI + { + struct connectdata *conn = data->conn; + char * const buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf); + + /* handle the security-oriented responses 6xx ***/ + switch(code) { + case 631: + code = Curl_sec_read_msg(data, conn, buf, PROT_SAFE); + break; + case 632: + code = Curl_sec_read_msg(data, conn, buf, PROT_PRIVATE); + break; + case 633: + code = Curl_sec_read_msg(data, conn, buf, PROT_CONFIDENTIAL); + break; + default: + /* normal ftp stuff we pass through! */ + break; + } + } +#endif + + /* store the latest code for later retrieval */ + data->info.httpcode = code; + + if(ftpcode) + *ftpcode = code; + + if(421 == code) { + /* 421 means "Service not available, closing control connection." and FTP + * servers use it to signal that idle session timeout has been exceeded. + * If we ignored the response, it could end up hanging in some cases. + * + * This response code can come at any point so having it treated + * generically is a good idea. + */ + infof(data, "We got a 421 - timeout"); + ftp_state(data, FTP_STOP); + return CURLE_OPERATION_TIMEDOUT; + } + + return result; +} + +/* --- parse FTP server responses --- */ + +/* + * Curl_GetFTPResponse() is a BLOCKING function to read the full response + * from a server after a command. + * + */ + +CURLcode Curl_GetFTPResponse(struct Curl_easy *data, + ssize_t *nreadp, /* return number of bytes read */ + int *ftpcode) /* return the ftp-code */ +{ + /* + * We cannot read just one byte per read() and then go back to select() as + * the OpenSSL read() does not grok that properly. + * + * Alas, read as much as possible, split up into lines, use the ending + * line in a response or continue reading. */ + + struct connectdata *conn = data->conn; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = &conn->proto.ftpc; + struct pingpong *pp = &ftpc->pp; + size_t nread; + int cache_skip = 0; + int value_to_be_ignored = 0; + + if(ftpcode) + *ftpcode = 0; /* 0 for errors */ + else + /* make the pointer point to something for the rest of this function */ + ftpcode = &value_to_be_ignored; + + *nreadp = 0; + + while(!*ftpcode && !result) { + /* check and reset timeout value every lap */ + timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE); + timediff_t interval_ms; + + if(timeout <= 0) { + failf(data, "FTP response timeout"); + return CURLE_OPERATION_TIMEDOUT; /* already too little time */ + } + + interval_ms = 1000; /* use 1 second timeout intervals */ + if(timeout < interval_ms) + interval_ms = timeout; + + /* + * Since this function is blocking, we need to wait here for input on the + * connection and only then we call the response reading function. We do + * timeout at least every second to make the timeout check run. + * + * A caution here is that the ftp_readresp() function has a cache that may + * contain pieces of a response from the previous invoke and we need to + * make sure we do not just wait for input while there is unhandled data in + * that cache. But also, if the cache is there, we call ftp_readresp() and + * the cache was not good enough to continue we must not just busy-loop + * around this function. + * + */ + + if(Curl_dyn_len(&pp->recvbuf) && (cache_skip < 2)) { + /* + * There is a cache left since before. We then skipping the wait for + * socket action, unless this is the same cache like the previous round + * as then the cache was deemed not enough to act on and we then need to + * wait for more data anyway. + */ + } + 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", + SOCKERRNO); + return CURLE_RECV_ERROR; + + case 0: /* timeout */ + if(Curl_pgrsUpdate(data)) + return CURLE_ABORTED_BY_CALLBACK; + continue; /* just continue in our loop for the timeout duration */ + + default: /* for clarity */ + break; + } + } + result = ftp_readresp(data, FIRSTSOCKET, pp, ftpcode, &nread); + if(result) + break; + + if(!nread && Curl_dyn_len(&pp->recvbuf)) + /* bump cache skip counter as on repeated skips we must wait for more + data */ + cache_skip++; + else + /* when we got data or there is no cache left, we reset the cache skip + counter */ + cache_skip = 0; + + *nreadp += nread; + + } /* while there is buffer left and loop is requested */ + + pp->pending_resp = FALSE; + + return result; +} + +static CURLcode ftp_state_user(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = Curl_pp_sendf(data, + &conn->proto.ftpc.pp, "USER %s", + conn->user?conn->user:""); + if(!result) { + struct ftp_conn *ftpc = &conn->proto.ftpc; + ftpc->ftp_trying_alternative = FALSE; + ftp_state(data, FTP_USER); + } + return result; +} + +static CURLcode ftp_state_pwd(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD"); + if(!result) + ftp_state(data, FTP_PWD); + + return result; +} + +/* For the FTP "protocol connect" and "doing" phases only */ +static int ftp_getsock(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *socks) +{ + return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks); +} + +/* For the FTP "DO_MORE" phase only */ +static int ftp_domore_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks) +{ + struct ftp_conn *ftpc = &conn->proto.ftpc; + (void)data; + + /* When in DO_MORE state, we could be either waiting for us to connect to a + * remote site, or we could wait for that site to connect to us. Or just + * handle ordinary commands. + */ + CURL_TRC_FTP(data, "[%s] ftp_domore_getsock()", FTP_DSTATE(data)); + + if(FTP_STOP == ftpc->state) { + /* if stopped and still in this state, then we are also waiting for a + connect on the secondary connection */ + DEBUGASSERT(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD || + (conn->cfilter[SECONDARYSOCKET] && + !Curl_conn_is_connected(conn, SECONDARYSOCKET))); + socks[0] = conn->sock[FIRSTSOCKET]; + /* An unconnected SECONDARY will add its socket by itself + * via its adjust_pollset() */ + return GETSOCK_READSOCK(0); + } + return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks); +} + +/* This is called after the FTP_QUOTE state is passed. + + ftp_state_cwd() sends the range of CWD commands to the server to change to + the correct directory. It may also need to send MKD commands to create + missing ones, if that option is enabled. +*/ +static CURLcode ftp_state_cwd(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + if(ftpc->cwddone) + /* already done and fine */ + result = ftp_state_mdtm(data); + else { + /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */ + DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) || + !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')); + + ftpc->count2 = 0; /* count2 counts failed CWDs */ + + if(conn->bits.reuse && ftpc->entrypath && + /* no need to go to entrypath when we have an absolute path */ + !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) { + /* This is a reused connection. Since we change directory to where the + transfer is taking place, we must first get back to the original dir + where we ended up after login: */ + ftpc->cwdcount = 0; /* we count this as the first path, then we add one + for all upcoming ones in the ftp->dirs[] array */ + result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath); + if(!result) + ftp_state(data, FTP_CWD); + } + else { + if(ftpc->dirdepth) { + ftpc->cwdcount = 1; + /* issue the first CWD, the rest is sent when the CWD responses are + received... */ + result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", + ftpc->dirs[ftpc->cwdcount -1]); + if(!result) + ftp_state(data, FTP_CWD); + } + else { + /* No CWD necessary */ + result = ftp_state_mdtm(data); + } + } + } + return result; +} + +typedef enum { + EPRT, + PORT, + DONE +} ftpport; + +static CURLcode ftp_state_use_port(struct Curl_easy *data, + ftpport fcmd) /* start with this */ +{ + 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; + char myhost[MAX_IPADR_LEN + 1] = ""; + + struct Curl_sockaddr_storage ss; + struct Curl_addrinfo *res, *ai; + curl_socklen_t sslen; + char hbuf[NI_MAXHOST]; + struct sockaddr *sa = (struct sockaddr *)&ss; + struct sockaddr_in * const sa4 = (void *)sa; +#ifdef USE_IPV6 + struct sockaddr_in6 * const sa6 = (void *)sa; +#endif + static const char mode[][5] = { "EPRT", "PORT" }; + enum resolve_t rc; + int error; + char *host = NULL; + char *string_ftpport = data->set.str[STRING_FTPPORT]; + struct Curl_dns_entry *h = NULL; + unsigned short port_min = 0; + unsigned short port_max = 0; + unsigned short port; + bool possibly_non_local = TRUE; + char buffer[STRERROR_LEN]; + char *addr = NULL; + size_t addrlen = 0; + char ipstr[50]; + + /* Step 1, figure out what is requested, + * accepted format : + * (ipv4|ipv6|domain|interface)?(:port(-range)?)? + */ + + if(data->set.str[STRING_FTPPORT] && + (strlen(data->set.str[STRING_FTPPORT]) > 1)) { + char *ip_end = NULL; + +#ifdef USE_IPV6 + if(*string_ftpport == '[') { + /* [ipv6]:port(-range) */ + char *ip_start = string_ftpport + 1; + ip_end = strchr(ip_start, ']'); + if(ip_end) { + addrlen = ip_end - ip_start; + addr = ip_start; + } + } + else +#endif + if(*string_ftpport == ':') { + /* :port */ + ip_end = string_ftpport; + } + else { + ip_end = strchr(string_ftpport, ':'); + addr = string_ftpport; + if(ip_end) { + /* either ipv6 or (ipv4|domain|interface):port(-range) */ + addrlen = ip_end - string_ftpport; +#ifdef USE_IPV6 + if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) { + /* ipv6 */ + port_min = port_max = 0; + ip_end = NULL; /* this got no port ! */ + } +#endif + } + else + /* ipv4|interface */ + addrlen = strlen(string_ftpport); + } + + /* parse the port */ + if(ip_end) { + char *port_sep = NULL; + char *port_start = strchr(ip_end, ':'); + if(port_start) { + port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10)); + port_sep = strchr(port_start, '-'); + if(port_sep) { + port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10)); + } + else + port_max = port_min; + } + } + + /* correct errors like: + * :1234-1230 + * :-4711, in this case port_min is (unsigned)-1, + * therefore port_min > port_max for all cases + * but port_max = (unsigned)-1 + */ + if(port_min > port_max) + port_min = port_max = 0; + + if(addrlen) { + DEBUGASSERT(addr); + if(addrlen >= sizeof(ipstr)) + goto out; + memcpy(ipstr, addr, addrlen); + ipstr[addrlen] = 0; + + /* attempt to get the address of the given interface name */ + switch(Curl_if2ip(conn->remote_addr->family, +#ifdef USE_IPV6 + Curl_ipv6_scope(&conn->remote_addr->sa_addr), + conn->scope_id, +#endif + ipstr, hbuf, sizeof(hbuf))) { + case IF2IP_NOT_FOUND: + /* not an interface, use the given string as hostname instead */ + host = ipstr; + break; + case IF2IP_AF_NOT_SUPPORTED: + goto out; + case IF2IP_FOUND: + host = hbuf; /* use the hbuf for hostname */ + break; + } + } + else + /* there was only a port(-range) given, default the host */ + host = NULL; + } /* data->set.ftpport */ + + if(!host) { + const char *r; + /* not an interface and not a hostname, get default by extracting + the IP from the control connection */ + sslen = sizeof(ss); + if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { + failf(data, "getsockname() failed: %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + goto out; + } + switch(sa->sa_family) { +#ifdef USE_IPV6 + case AF_INET6: + r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf)); + break; +#endif + default: + r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); + break; + } + if(!r) { + goto out; + } + host = hbuf; /* use this hostname */ + possibly_non_local = FALSE; /* we know it is local now */ + } + + /* resolv ip/host to ip */ + rc = Curl_resolv(data, host, 0, FALSE, &h); + if(rc == CURLRESOLV_PENDING) + (void)Curl_resolver_wait_resolv(data, &h); + if(h) { + res = h->addr; + /* when we return from this function, we can forget about this entry + to we can unlock it now already */ + Curl_resolv_unlock(data, h); + } /* (h) */ + else + res = NULL; /* failure! */ + + if(!res) { + failf(data, "failed to resolve the address provided to PORT: %s", host); + goto out; + } + + host = NULL; + + /* step 2, create a socket for the requested address */ + error = 0; + for(ai = res; ai; ai = ai->ai_next) { + if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) { + error = SOCKERRNO; + continue; + } + break; + } + if(!ai) { + failf(data, "socket failure: %s", + Curl_strerror(error, buffer, sizeof(buffer))); + goto out; + } + CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket", + FTP_DSTATE(data)); + + /* step 3, bind to a suitable local address */ + + memcpy(sa, ai->ai_addr, ai->ai_addrlen); + sslen = ai->ai_addrlen; + + for(port = port_min; port <= port_max;) { + if(sa->sa_family == AF_INET) + sa4->sin_port = htons(port); +#ifdef USE_IPV6 + else + sa6->sin6_port = htons(port); +#endif + /* Try binding the given address. */ + if(bind(portsock, sa, sslen) ) { + /* It failed. */ + error = SOCKERRNO; + if(possibly_non_local && (error == EADDRNOTAVAIL)) { + /* The requested bind address is not local. Use the address used for + * the control connection instead and restart the port loop + */ + infof(data, "bind(port=%hu) on non-local address failed: %s", port, + Curl_strerror(error, buffer, sizeof(buffer))); + + sslen = sizeof(ss); + if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { + failf(data, "getsockname() failed: %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + goto out; + } + port = port_min; + possibly_non_local = FALSE; /* do not try this again */ + continue; + } + if(error != EADDRINUSE && error != EACCES) { + failf(data, "bind(port=%hu) failed: %s", port, + Curl_strerror(error, buffer, sizeof(buffer))); + goto out; + } + } + else + break; + + port++; + } + + /* maybe all ports were in use already */ + if(port > port_max) { + failf(data, "bind() failed, we ran out of ports"); + goto out; + } + + /* get the name again after the bind() so that we can extract the + port number it uses now */ + sslen = sizeof(ss); + if(getsockname(portsock, sa, &sslen)) { + failf(data, "getsockname() failed: %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + goto out; + } + CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d", + FTP_DSTATE(data), port); + + /* step 4, listen on the socket */ + + if(listen(portsock, 1)) { + failf(data, "socket failure: %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + goto out; + } + CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d", + FTP_DSTATE(data), port); + + /* step 5, send the proper FTP command */ + + /* get a plain printable version of the numerical address to work with + below */ + Curl_printable_address(ai, myhost, sizeof(myhost)); + +#ifdef USE_IPV6 + if(!conn->bits.ftp_use_eprt && conn->bits.ipv6) + /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the + request and enable EPRT again! */ + conn->bits.ftp_use_eprt = TRUE; +#endif + + /* Replace any filter on SECONDARY with one listening on this socket */ + result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); + if(result) + goto out; + portsock = CURL_SOCKET_BAD; /* now held in filter */ + + for(; fcmd != DONE; fcmd++) { + + if(!conn->bits.ftp_use_eprt && (EPRT == fcmd)) + /* if disabled, goto next */ + continue; + + if((PORT == fcmd) && sa->sa_family != AF_INET) + /* PORT is IPv4 only */ + continue; + + switch(sa->sa_family) { + case AF_INET: + port = ntohs(sa4->sin_port); + break; +#ifdef USE_IPV6 + case AF_INET6: + port = ntohs(sa6->sin6_port); + break; +#endif + default: + continue; /* might as well skip this */ + } + + if(EPRT == fcmd) { + /* + * Two fine examples from RFC2428; + * + * EPRT |1|132.235.1.2|6275| + * + * EPRT |2|1080::8:800:200C:417A|5282| + */ + + result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd], + sa->sa_family == AF_INET?1:2, + myhost, port); + if(result) { + failf(data, "Failure sending EPRT command: %s", + curl_easy_strerror(result)); + goto out; + } + break; + } + if(PORT == fcmd) { + /* large enough for [IP address],[num],[num] */ + char target[sizeof(myhost) + 20]; + char *source = myhost; + char *dest = target; + + /* translate x.x.x.x to x,x,x,x */ + while(*source) { + if(*source == '.') + *dest = ','; + else + *dest = *source; + dest++; + source++; + } + *dest = 0; + msnprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff)); + + result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target); + if(result) { + failf(data, "Failure sending PORT command: %s", + curl_easy_strerror(result)); + goto out; + } + break; + } + } + + /* store which command was sent */ + ftpc->count1 = fcmd; + + ftp_state(data, FTP_PORT); + +out: + if(result) { + ftp_state(data, FTP_STOP); + } + if(portsock != CURL_SOCKET_BAD) + Curl_socket_close(data, conn, portsock); + return result; +} + +static CURLcode ftp_state_use_pasv(struct Curl_easy *data, + struct connectdata *conn) +{ + struct ftp_conn *ftpc = &conn->proto.ftpc; + CURLcode result = CURLE_OK; + /* + Here's the executive summary on what to do: + + PASV is RFC959, expect: + 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2) + + LPSV is RFC1639, expect: + 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2) + + EPSV is RFC2428, expect: + 229 Entering Extended Passive Mode (|||port|) + + */ + + static const char mode[][5] = { "EPSV", "PASV" }; + int modeoff; + +#ifdef PF_INET6 + if(!conn->bits.ftp_use_epsv && conn->bits.ipv6) + /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the + request and enable EPSV again! */ + conn->bits.ftp_use_epsv = TRUE; +#endif + + modeoff = conn->bits.ftp_use_epsv?0:1; + + result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]); + if(!result) { + ftpc->count1 = modeoff; + ftp_state(data, FTP_PASV); + infof(data, "Connect data stream passively"); + } + return result; +} + +/* + * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc. + * + * REST is the last command in the chain of commands when a "head"-like + * request is made. Thus, if an actual transfer is to be made this is where we + * take off for real. + */ +static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = data->req.p.ftp; + struct connectdata *conn = data->conn; + + if(ftp->transfer != PPTRANSFER_BODY) { + /* does not transfer any data */ + + /* still possibly do PRE QUOTE jobs */ + ftp_state(data, FTP_RETR_PREQUOTE); + result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE); + } + else if(data->set.ftp_use_port) { + /* We have chosen to use the PORT (or similar) command */ + result = ftp_state_use_port(data, EPRT); + } + else { + /* We have chosen (this is default) to use the PASV (or similar) command */ + if(data->set.ftp_use_pret) { + /* The user has requested that we send a PRET command + to prepare the server for the upcoming PASV */ + struct ftp_conn *ftpc = &conn->proto.ftpc; + if(!conn->proto.ftpc.file) + result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s", + data->set.str[STRING_CUSTOMREQUEST]? + data->set.str[STRING_CUSTOMREQUEST]: + (data->state.list_only?"NLST":"LIST")); + else if(data->state.upload) + result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s", + conn->proto.ftpc.file); + else + result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s", + conn->proto.ftpc.file); + if(!result) + ftp_state(data, FTP_PRET); + } + else + result = ftp_state_use_pasv(data, conn); + } + return result; +} + +static CURLcode ftp_state_rest(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = data->req.p.ftp; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) { + /* if a "head"-like request is being made (on a file) */ + + /* Determine if server can respond to REST command and therefore + whether it supports range */ + result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0); + if(!result) + ftp_state(data, FTP_REST); + } + else + result = ftp_state_prepare_transfer(data); + + return result; +} + +static CURLcode ftp_state_size(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = data->req.p.ftp; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) { + /* if a "head"-like request is being made (on a file) */ + + /* we know ftpc->file is a valid pointer to a filename */ + result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); + if(!result) + ftp_state(data, FTP_SIZE); + } + else + result = ftp_state_rest(data, conn); + + return result; +} + +static CURLcode ftp_state_list(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = data->req.p.ftp; + struct connectdata *conn = data->conn; + + /* If this output is to be machine-parsed, the NLST command might be better + to use, since the LIST command output is not specified or standard in any + way. It has turned out that the NLST list output is not the same on all + servers either... */ + + /* + if FTPFILE_NOCWD was specified, we should add the path + as argument for the LIST / NLST / or custom command. + Whether the server will support this, is uncertain. + + The other ftp_filemethods will CWD into dir/dir/ first and + then just do LIST (in that case: nothing to do here) + */ + char *lstArg = NULL; + char *cmd; + + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) { + /* url-decode before evaluation: e.g. paths starting/ending with %2f */ + const char *slashPos = NULL; + char *rawPath = NULL; + result = Curl_urldecode(ftp->path, 0, &rawPath, NULL, REJECT_CTRL); + if(result) + return result; + + slashPos = strrchr(rawPath, '/'); + if(slashPos) { + /* chop off the file part if format is dir/file otherwise remove + the trailing slash for dir/dir/ except for absolute path / */ + size_t n = slashPos - rawPath; + if(n == 0) + ++n; + + lstArg = rawPath; + lstArg[n] = '\0'; + } + else + free(rawPath); + } + + cmd = aprintf("%s%s%s", + data->set.str[STRING_CUSTOMREQUEST]? + data->set.str[STRING_CUSTOMREQUEST]: + (data->state.list_only?"NLST":"LIST"), + lstArg? " ": "", + lstArg? lstArg: ""); + free(lstArg); + + if(!cmd) + return CURLE_OUT_OF_MEMORY; + + result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", cmd); + free(cmd); + + if(!result) + ftp_state(data, FTP_LIST); + + return result; +} + +static CURLcode ftp_state_retr_prequote(struct Curl_easy *data) +{ + /* We have sent the TYPE, now we must send the list of prequote strings */ + return ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE); +} + +static CURLcode ftp_state_stor_prequote(struct Curl_easy *data) +{ + /* We have sent the TYPE, now we must send the list of prequote strings */ + return ftp_state_quote(data, TRUE, FTP_STOR_PREQUOTE); +} + +static CURLcode ftp_state_type(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = data->req.p.ftp; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + /* If we have selected NOBODY and HEADER, it means that we only want file + information. Which in FTP cannot be much more than the file size and + date. */ + if(data->req.no_body && ftpc->file && + ftp_need_type(conn, data->state.prefer_ascii)) { + /* The SIZE command is _not_ RFC 959 specified, and therefore many servers + may not support it! It is however the only way we have to get a file's + size! */ + + ftp->transfer = PPTRANSFER_INFO; + /* this means no actual transfer will be made */ + + /* Some servers return different sizes for different modes, and thus we + must set the proper type before we check the size */ + result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_TYPE); + if(result) + return result; + } + else + result = ftp_state_size(data, conn); + + return result; +} + +/* This is called after the CWD commands have been done in the beginning of + the DO phase */ +static CURLcode ftp_state_mdtm(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + /* Requested time of file or time-depended transfer? */ + if((data->set.get_filetime || data->set.timecondition) && ftpc->file) { + + /* we have requested to get the modified-time of the file, this is a white + spot as the MDTM is not mentioned in RFC959 */ + result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file); + + if(!result) + ftp_state(data, FTP_MDTM); + } + else + result = ftp_state_type(data); + + return result; +} + + +/* This is called after the TYPE and possible quote commands have been sent */ +static CURLcode ftp_state_ul_setup(struct Curl_easy *data, + bool sizechecked) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct FTP *ftp = data->req.p.ftp; + struct ftp_conn *ftpc = &conn->proto.ftpc; + bool append = data->set.remote_append; + + if((data->state.resume_from && !sizechecked) || + ((data->state.resume_from > 0) && sizechecked)) { + /* we are about to continue the uploading of a file */ + /* 1. get already existing file's size. We use the SIZE command for this + which may not exist in the server! The SIZE command is not in + RFC959. */ + + /* 2. This used to set REST. But since we can do append, we + do not another ftp command. We just skip the source file + offset and then we APPEND the rest on the file instead */ + + /* 3. pass file-size number of bytes in the source file */ + /* 4. lower the infilesize counter */ + /* => transfer as usual */ + int seekerr = CURL_SEEKFUNC_OK; + + if(data->state.resume_from < 0) { + /* Got no given size to start from, figure it out */ + result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); + if(!result) + ftp_state(data, FTP_STOR_SIZE); + return result; + } + + /* enable append */ + append = TRUE; + + /* Let's read off the proper amount of bytes from the input. */ + if(data->set.seek_func) { + Curl_set_in_callback(data, true); + seekerr = data->set.seek_func(data->set.seek_client, + data->state.resume_from, SEEK_SET); + Curl_set_in_callback(data, false); + } + + if(seekerr != CURL_SEEKFUNC_OK) { + curl_off_t passed = 0; + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_FTP_COULDNT_USE_REST; + } + /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ + do { + char scratch[4*1024]; + size_t readthisamountnow = + (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ? + sizeof(scratch) : + curlx_sotouz(data->state.resume_from - passed); + + size_t actuallyread = + data->state.fread_func(scratch, 1, readthisamountnow, + data->state.in); + + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + return CURLE_FTP_COULDNT_USE_REST; + } + } while(passed < data->state.resume_from); + } + /* now, decrease the size of the read */ + if(data->state.infilesize>0) { + data->state.infilesize -= data->state.resume_from; + + if(data->state.infilesize <= 0) { + infof(data, "File already completely uploaded"); + + /* no data to transfer */ + Curl_xfer_setup_nop(data); + + /* Set ->transfer so that we will not get any error in + * ftp_done() because we did not transfer anything! */ + ftp->transfer = PPTRANSFER_NONE; + + ftp_state(data, FTP_STOP); + return CURLE_OK; + } + } + /* we have passed, proceed as normal */ + } /* resume_from */ + + result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s", + ftpc->file); + if(!result) + ftp_state(data, FTP_STOR); + + return result; +} + +static CURLcode ftp_state_quote(struct Curl_easy *data, + bool init, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = data->req.p.ftp; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + bool quote = FALSE; + struct curl_slist *item; + + switch(instate) { + case FTP_QUOTE: + default: + item = data->set.quote; + break; + case FTP_RETR_PREQUOTE: + case FTP_STOR_PREQUOTE: + item = data->set.prequote; + break; + case FTP_POSTQUOTE: + item = data->set.postquote; + break; + } + + /* + * This state uses: + * 'count1' to iterate over the commands to send + * 'count2' to store whether to allow commands to fail + */ + + if(init) + ftpc->count1 = 0; + else + ftpc->count1++; + + if(item) { + int i = 0; + + /* Skip count1 items in the linked list */ + while((i< ftpc->count1) && item) { + item = item->next; + i++; + } + if(item) { + char *cmd = item->data; + if(cmd[0] == '*') { + cmd++; + ftpc->count2 = 1; /* the sent command is allowed to fail */ + } + else + ftpc->count2 = 0; /* failure means cancel operation */ + + result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); + if(result) + return result; + ftp_state(data, instate); + quote = TRUE; + } + } + + if(!quote) { + /* No more quote to send, continue to ... */ + switch(instate) { + case FTP_QUOTE: + default: + result = ftp_state_cwd(data, conn); + break; + case FTP_RETR_PREQUOTE: + if(ftp->transfer != PPTRANSFER_BODY) + ftp_state(data, FTP_STOP); + else { + if(ftpc->known_filesize != -1) { + Curl_pgrsSetDownloadSize(data, ftpc->known_filesize); + result = ftp_state_retr(data, ftpc->known_filesize); + } + else { + if(data->set.ignorecl || data->state.prefer_ascii) { + /* 'ignorecl' is used to support download of growing files. It + prevents the state machine from requesting the file size from + the server. With an unknown file size the download continues + until the server terminates it, otherwise the client stops if + the received byte count exceeds the reported file size. Set + option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this + behavior. + + In addition: asking for the size for 'TYPE A' transfers is not + constructive since servers do not report the converted size. So + skip it. + */ + result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); + if(!result) + ftp_state(data, FTP_RETR); + } + else { + result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); + if(!result) + ftp_state(data, FTP_RETR_SIZE); + } + } + } + break; + case FTP_STOR_PREQUOTE: + result = ftp_state_ul_setup(data, FALSE); + break; + case FTP_POSTQUOTE: + break; + } + } + + return result; +} + +/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV + problems */ +static CURLcode ftp_epsv_disable(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + + if(conn->bits.ipv6 +#ifndef CURL_DISABLE_PROXY + && !(conn->bits.tunnel_proxy || conn->bits.socksproxy) +#endif + ) { + /* We cannot disable EPSV when doing IPv6, so this is instead a fail */ + failf(data, "Failed EPSV attempt, exiting"); + return CURLE_WEIRD_SERVER_REPLY; + } + + infof(data, "Failed EPSV attempt. Disabling EPSV"); + /* disable it for next transfer */ + conn->bits.ftp_use_epsv = FALSE; + Curl_conn_close(data, SECONDARYSOCKET); + Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET); + data->state.errorbuf = FALSE; /* allow error message to get + rewritten */ + result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV"); + if(!result) { + conn->proto.ftpc.count1++; + /* remain in/go to the FTP_PASV state */ + ftp_state(data, FTP_PASV); + } + return result; +} + + +static char *control_address(struct connectdata *conn) +{ + /* Returns the control connection IP address. + If a proxy tunnel is used, returns the original hostname instead, because + the effective control connection address is the proxy address, + not the ftp host. */ +#ifndef CURL_DISABLE_PROXY + if(conn->bits.tunnel_proxy || conn->bits.socksproxy) + return conn->host.name; +#endif + return conn->primary.remote_ip; +} + +static bool match_pasv_6nums(const char *p, + unsigned int *array) /* 6 numbers */ +{ + int i; + for(i = 0; i < 6; i++) { + unsigned long num; + char *endp; + if(i) { + if(*p != ',') + return FALSE; + p++; + } + if(!ISDIGIT(*p)) + return FALSE; + num = strtoul(p, &endp, 10); + if(num > 255) + return FALSE; + array[i] = (unsigned int)num; + p = endp; + } + return TRUE; +} + +static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, + int ftpcode) +{ + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + CURLcode result; + struct Curl_dns_entry *addr = NULL; + enum resolve_t rc; + unsigned short connectport; /* the local port connect() should use! */ + struct pingpong *pp = &ftpc->pp; + char *str = + Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */ + + /* if we come here again, make sure the former name is cleared */ + Curl_safefree(ftpc->newhost); + + if((ftpc->count1 == 0) && + (ftpcode == 229)) { + /* positive EPSV response */ + char *ptr = strchr(str, '('); + if(ptr) { + char sep; + ptr++; + /* |||12345| */ + sep = ptr[0]; + /* the ISDIGIT() check here is because strtoul() accepts leading minus + etc */ + if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { + char *endp; + unsigned long num = strtoul(&ptr[3], &endp, 10); + if(*endp != sep) + ptr = NULL; + else if(num > 0xffff) { + failf(data, "Illegal port number in EPSV reply"); + return CURLE_FTP_WEIRD_PASV_REPLY; + } + if(ptr) { + ftpc->newport = (unsigned short)(num & 0xffff); + ftpc->newhost = strdup(control_address(conn)); + if(!ftpc->newhost) + return CURLE_OUT_OF_MEMORY; + } + } + else + ptr = NULL; + } + if(!ptr) { + failf(data, "Weirdly formatted EPSV reply"); + return CURLE_FTP_WEIRD_PASV_REPLY; + } + } + else if((ftpc->count1 == 1) && + (ftpcode == 227)) { + /* positive PASV response */ + unsigned int ip[6]; + + /* + * Scan for a sequence of six comma-separated numbers and use them as + * IP+port indicators. + * + * Found reply-strings include: + * "227 Entering Passive Mode (127,0,0,1,4,51)" + * "227 Data transfer will passively listen to 127,0,0,1,4,51" + * "227 Entering passive mode. 127,0,0,1,4,51" + */ + while(*str) { + if(match_pasv_6nums(str, ip)) + break; + str++; + } + + if(!*str) { + failf(data, "Couldn't interpret the 227-response"); + return CURLE_FTP_WEIRD_227_FORMAT; + } + + /* we got OK from server */ + if(data->set.ftp_skip_ip) { + /* told to ignore the remotely given IP but instead use the host we used + for the control connection */ + infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead", + ip[0], ip[1], ip[2], ip[3], + conn->host.name); + ftpc->newhost = strdup(control_address(conn)); + } + else + ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + + if(!ftpc->newhost) + return CURLE_OUT_OF_MEMORY; + + ftpc->newport = (unsigned short)(((ip[4]<<8) + ip[5]) & 0xffff); + } + else if(ftpc->count1 == 0) { + /* EPSV failed, move on to PASV */ + return ftp_epsv_disable(data, conn); + } + else { + failf(data, "Bad PASV/EPSV response: %03d", ftpcode); + return CURLE_FTP_WEIRD_PASV_REPLY; + } + +#ifndef CURL_DISABLE_PROXY + if(conn->bits.proxy) { + /* + * This connection uses a proxy and we need to connect to the proxy again + * here. We do not want to rely on a former host lookup that might've + * expired now, instead we remake the lookup here and now! + */ + const char * const host_name = conn->bits.socksproxy ? + conn->socks_proxy.host.name : conn->http_proxy.host.name; + rc = Curl_resolv(data, host_name, conn->primary.remote_port, FALSE, &addr); + if(rc == CURLRESOLV_PENDING) + /* BLOCKING, ignores the return code but 'addr' will be NULL in + case of failure */ + (void)Curl_resolver_wait_resolv(data, &addr); + + /* we connect to the proxy's port */ + connectport = (unsigned short)conn->primary.remote_port; + + if(!addr) { + failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport); + return CURLE_COULDNT_RESOLVE_PROXY; + } + } + else +#endif + { + /* normal, direct, ftp connection */ + DEBUGASSERT(ftpc->newhost); + + /* postponed address resolution in case of tcp fastopen */ + if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) { + Curl_conn_ev_update_info(data, conn); + Curl_safefree(ftpc->newhost); + ftpc->newhost = strdup(control_address(conn)); + if(!ftpc->newhost) + return CURLE_OUT_OF_MEMORY; + } + + rc = Curl_resolv(data, ftpc->newhost, ftpc->newport, FALSE, &addr); + if(rc == CURLRESOLV_PENDING) + /* BLOCKING */ + (void)Curl_resolver_wait_resolv(data, &addr); + + connectport = ftpc->newport; /* we connect to the remote port */ + + if(!addr) { + failf(data, "cannot resolve new host %s:%hu", + ftpc->newhost, connectport); + return CURLE_FTP_CANT_GET_HOST; + } + } + + result = Curl_conn_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 are done using this address */ + if(ftpc->count1 == 0 && ftpcode == 229) + return ftp_epsv_disable(data, conn); + + return result; + } + + + /* + * When this is used from the multi interface, this might've returned with + * the 'connected' set to FALSE and thus we are now awaiting a non-blocking + * connect to connect. + */ + + if(data->set.verbose) + /* this just dumps information about this second connection */ + ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport); + + Curl_resolv_unlock(data, addr); /* we are done using this address */ + + Curl_safefree(conn->secondaryhostname); + conn->secondary_port = ftpc->newport; + conn->secondaryhostname = strdup(ftpc->newhost); + if(!conn->secondaryhostname) + return CURLE_OUT_OF_MEMORY; + + conn->bits.do_more = TRUE; + ftp_state(data, FTP_STOP); /* this phase is completed */ + + return result; +} + +static CURLcode ftp_state_port_resp(struct Curl_easy *data, + int ftpcode) +{ + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + ftpport fcmd = (ftpport)ftpc->count1; + CURLcode result = CURLE_OK; + + /* The FTP spec tells a positive response should have code 200. + Be more permissive here to tolerate deviant servers. */ + if(ftpcode / 100 != 2) { + /* the command failed */ + + if(EPRT == fcmd) { + infof(data, "disabling EPRT usage"); + conn->bits.ftp_use_eprt = FALSE; + } + fcmd++; + + if(fcmd == DONE) { + failf(data, "Failed to do PORT"); + result = CURLE_FTP_PORT_FAILED; + } + else + /* try next */ + result = ftp_state_use_port(data, fcmd); + } + else { + infof(data, "Connect data stream actively"); + ftp_state(data, FTP_STOP); /* end of DO phase */ + result = ftp_dophase_done(data, FALSE); + } + + return result; +} + +static int twodigit(const char *p) +{ + return (p[0]-'0') * 10 + (p[1]-'0'); +} + +static bool ftp_213_date(const char *p, int *year, int *month, int *day, + int *hour, int *minute, int *second) +{ + size_t len = strlen(p); + if(len < 14) + return FALSE; + *year = twodigit(&p[0]) * 100 + twodigit(&p[2]); + *month = twodigit(&p[4]); + *day = twodigit(&p[6]); + *hour = twodigit(&p[8]); + *minute = twodigit(&p[10]); + *second = twodigit(&p[12]); + + if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) || + (*second > 60)) + return FALSE; + return TRUE; +} + +static CURLcode client_write_header(struct Curl_easy *data, + char *buf, size_t blen) +{ + /* Some replies from an FTP server are written to the client + * as CLIENTWRITE_HEADER, formatted as if they came from a + * HTTP conversation. + * In all protocols, CLIENTWRITE_HEADER data is only passed to + * the body write callback when data->set.include_header is set + * via CURLOPT_HEADER. + * For historic reasons, FTP never played this game and expects + * all its HEADERs to do that always. Set that flag during the + * call to Curl_client_write() so it does the right thing. + * + * Notice that we cannot enable this flag for FTP in general, + * as an FTP transfer might involve an HTTP proxy connection and + * headers from CONNECT should not automatically be part of the + * output. */ + CURLcode result; + int save = data->set.include_header; + data->set.include_header = TRUE; + result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen); + data->set.include_header = save? TRUE:FALSE; + return result; +} + +static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, + int ftpcode) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = data->req.p.ftp; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + switch(ftpcode) { + case 213: + { + /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the + last .sss part is optional and means fractions of a second */ + int year, month, day, hour, minute, second; + struct pingpong *pp = &ftpc->pp; + char *resp = Curl_dyn_ptr(&pp->recvbuf) + 4; + if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) { + /* we have a time, reformat it */ + char timebuf[24]; + msnprintf(timebuf, sizeof(timebuf), + "%04d%02d%02d %02d:%02d:%02d GMT", + year, month, day, hour, minute, second); + /* now, convert this into a time() value: */ + data->info.filetime = Curl_getdate_capped(timebuf); + } + +#ifdef CURL_FTP_HTTPSTYLE_HEAD + /* If we asked for a time of the file and we actually got one as well, + we "emulate" an HTTP-style header in our output. */ + + if(data->req.no_body && + ftpc->file && + data->set.get_filetime && + (data->info.filetime >= 0) ) { + char headerbuf[128]; + int headerbuflen; + time_t filetime = data->info.filetime; + struct tm buffer; + const struct tm *tm = &buffer; + + result = Curl_gmtime(filetime, &buffer); + if(result) + return result; + + /* format: "Tue, 15 Nov 1994 12:45:26" */ + headerbuflen = msnprintf(headerbuf, sizeof(headerbuf), + "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", + Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], + tm->tm_mday, + Curl_month[tm->tm_mon], + tm->tm_year + 1900, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + result = client_write_header(data, headerbuf, headerbuflen); + if(result) + return result; + } /* end of a ridiculous amount of conditionals */ +#endif + } + break; + default: + infof(data, "unsupported MDTM reply format"); + break; + case 550: /* 550 is used for several different problems, e.g. + "No such file or directory" or "Permission denied". + It does not mean that the file does not exist at all. */ + infof(data, "MDTM failed: file does not exist or permission problem," + " continuing"); + break; + } + + if(data->set.timecondition) { + if((data->info.filetime > 0) && (data->set.timevalue > 0)) { + switch(data->set.timecondition) { + case CURL_TIMECOND_IFMODSINCE: + default: + if(data->info.filetime <= data->set.timevalue) { + infof(data, "The requested document is not new enough"); + ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ + data->info.timecond = TRUE; + ftp_state(data, FTP_STOP); + return CURLE_OK; + } + break; + case CURL_TIMECOND_IFUNMODSINCE: + if(data->info.filetime > data->set.timevalue) { + infof(data, "The requested document is not old enough"); + ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ + data->info.timecond = TRUE; + ftp_state(data, FTP_STOP); + return CURLE_OK; + } + break; + } /* switch */ + } + else { + infof(data, "Skipping time comparison"); + } + } + + if(!result) + result = ftp_state_type(data); + + return result; +} + +static CURLcode ftp_state_type_resp(struct Curl_easy *data, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + if(ftpcode/100 != 2) { + /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a + successful 'TYPE I'. While that is not as RFC959 says, it is still a + positive response code and we allow that. */ + failf(data, "Couldn't set desired mode"); + return CURLE_FTP_COULDNT_SET_TYPE; + } + if(ftpcode != 200) + infof(data, "Got a %03d response code instead of the assumed 200", + ftpcode); + + if(instate == FTP_TYPE) + result = ftp_state_size(data, conn); + else if(instate == FTP_LIST_TYPE) + result = ftp_state_list(data); + else if(instate == FTP_RETR_TYPE) + result = ftp_state_retr_prequote(data); + else if(instate == FTP_STOR_TYPE) + result = ftp_state_stor_prequote(data); + + return result; +} + +static CURLcode ftp_state_retr(struct Curl_easy *data, + curl_off_t filesize) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = data->req.p.ftp; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_DSTATE(data)); + if(data->set.max_filesize && (filesize > data->set.max_filesize)) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + ftp->downloadsize = filesize; + + if(data->state.resume_from) { + /* We always (attempt to) get the size of downloads, so it is done before + this even when not doing resumes. */ + if(filesize == -1) { + infof(data, "ftp server does not support SIZE"); + /* We could not get the size and therefore we cannot know if there really + is a part of the file left to get, although the server will just + close the connection when we start the connection so it will not cause + us any harm, just not make us exit as nicely. */ + } + else { + /* We got a file size report, so we check that there actually is a + part of the file left to get, or else we go home. */ + if(data->state.resume_from< 0) { + /* We are supposed to download the last abs(from) bytes */ + if(filesize < -data->state.resume_from) { + failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T + ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", + data->state.resume_from, filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* convert to size to download */ + ftp->downloadsize = -data->state.resume_from; + /* download from where? */ + data->state.resume_from = filesize - ftp->downloadsize; + } + else { + if(filesize < data->state.resume_from) { + failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T + ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", + data->state.resume_from, filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* Now store the number of bytes we are expected to download */ + ftp->downloadsize = filesize-data->state.resume_from; + } + } + + if(ftp->downloadsize == 0) { + /* no data to transfer */ + Curl_xfer_setup_nop(data); + infof(data, "File already completely downloaded"); + + /* Set ->transfer so that we will not get any error in ftp_done() + * because we did not transfer the any file */ + ftp->transfer = PPTRANSFER_NONE; + ftp_state(data, FTP_STOP); + return CURLE_OK; + } + + /* Set resume file transfer offset */ + infof(data, "Instructs server to resume from offset %" + CURL_FORMAT_CURL_OFF_T, data->state.resume_from); + + result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T, + data->state.resume_from); + if(!result) + ftp_state(data, FTP_RETR_REST); + } + else { + /* no resume */ + result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); + if(!result) + ftp_state(data, FTP_RETR); + } + + return result; +} + +static CURLcode ftp_state_size_resp(struct Curl_easy *data, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + curl_off_t filesize = -1; + char *buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf); + size_t len = data->conn->proto.ftpc.pp.nfinal; + + /* get the size from the ascii string: */ + if(ftpcode == 213) { + /* To allow servers to prepend "rubbish" in the response string, we scan + for all the digits at the end of the response and parse only those as a + number. */ + char *start = &buf[4]; + char *fdigit = memchr(start, '\r', len); + if(fdigit) { + fdigit--; + if(*fdigit == '\n') + fdigit--; + while(ISDIGIT(fdigit[-1]) && (fdigit > start)) + fdigit--; + } + else + fdigit = start; + /* ignores parsing errors, which will make the size remain unknown */ + (void)curlx_strtoofft(fdigit, NULL, 10, &filesize); + + } + else if(ftpcode == 550) { /* "No such file or directory" */ + /* allow a SIZE failure for (resumed) uploads, when probing what command + to use */ + if(instate != FTP_STOR_SIZE) { + failf(data, "The file does not exist"); + return CURLE_REMOTE_FILE_NOT_FOUND; + } + } + + if(instate == FTP_SIZE) { +#ifdef CURL_FTP_HTTPSTYLE_HEAD + if(-1 != filesize) { + char clbuf[128]; + int clbuflen = msnprintf(clbuf, sizeof(clbuf), + "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize); + result = client_write_header(data, clbuf, clbuflen); + if(result) + return result; + } +#endif + Curl_pgrsSetDownloadSize(data, filesize); + result = ftp_state_rest(data, data->conn); + } + else if(instate == FTP_RETR_SIZE) { + Curl_pgrsSetDownloadSize(data, filesize); + result = ftp_state_retr(data, filesize); + } + else if(instate == FTP_STOR_SIZE) { + data->state.resume_from = filesize; + result = ftp_state_ul_setup(data, TRUE); + } + + return result; +} + +static CURLcode ftp_state_rest_resp(struct Curl_easy *data, + struct connectdata *conn, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + switch(instate) { + case FTP_REST: + default: +#ifdef CURL_FTP_HTTPSTYLE_HEAD + if(ftpcode == 350) { + char buffer[24]= { "Accept-ranges: bytes\r\n" }; + result = client_write_header(data, buffer, strlen(buffer)); + if(result) + return result; + } +#endif + result = ftp_state_prepare_transfer(data); + break; + + case FTP_RETR_REST: + if(ftpcode != 350) { + failf(data, "Couldn't use REST"); + result = CURLE_FTP_COULDNT_USE_REST; + } + else { + result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); + if(!result) + ftp_state(data, FTP_RETR); + } + break; + } + + return result; +} + +static CURLcode ftp_state_stor_resp(struct Curl_easy *data, + int ftpcode, ftpstate instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + if(ftpcode >= 400) { + failf(data, "Failed FTP upload: %0d", ftpcode); + ftp_state(data, FTP_STOP); + /* oops, we never close the sockets! */ + return CURLE_UPLOAD_FAILED; + } + + conn->proto.ftpc.state_saved = instate; + + /* PORT means we are now awaiting the server to connect to us. */ + if(data->set.ftp_use_port) { + bool connected; + + ftp_state(data, FTP_STOP); /* no longer in STOR state */ + + result = AllowServerConnect(data, &connected); + if(result) + return result; + + if(!connected) { + struct ftp_conn *ftpc = &conn->proto.ftpc; + infof(data, "Data conn was not available immediately"); + ftpc->wait_data_conn = TRUE; + } + + return CURLE_OK; + } + return InitiateTransfer(data); +} + +/* for LIST and RETR responses */ +static CURLcode ftp_state_get_resp(struct Curl_easy *data, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = data->req.p.ftp; + struct connectdata *conn = data->conn; + + if((ftpcode == 150) || (ftpcode == 125)) { + + /* + A; + 150 Opening BINARY mode data connection for /etc/passwd (2241 + bytes). (ok, the file is being transferred) + + B: + 150 Opening ASCII mode data connection for /bin/ls + + C: + 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes). + + D: + 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes) + + E: + 125 Data connection already open; Transfer starting. */ + + curl_off_t size = -1; /* default unknown size */ + + + /* + * It appears that there are FTP-servers that return size 0 for files when + * SIZE is used on the file while being in BINARY mode. To work around + * that (stupid) behavior, we attempt to parse the RETR response even if + * the SIZE returned size zero. + * + * Debugging help from Salvatore Sorrentino on February 26, 2003. + */ + + if((instate != FTP_LIST) && + !data->state.prefer_ascii && + !data->set.ignorecl && + (ftp->downloadsize < 1)) { + /* + * It seems directory listings either do not show the size or very + * often uses size 0 anyway. ASCII transfers may very well turn out + * that the transferred amount of data is not the same as this line + * tells, why using this number in those cases only confuses us. + * + * Example D above makes this parsing a little tricky */ + char *bytes; + char *buf = Curl_dyn_ptr(&conn->proto.ftpc.pp.recvbuf); + bytes = strstr(buf, " bytes"); + if(bytes) { + long in = (long)(--bytes-buf); + /* this is a hint there is size information in there! ;-) */ + while(--in) { + /* scan for the left parenthesis and break there */ + if('(' == *bytes) + break; + /* skip only digits */ + if(!ISDIGIT(*bytes)) { + bytes = NULL; + break; + } + /* one more estep backwards */ + bytes--; + } + /* if we have nothing but digits: */ + if(bytes) { + ++bytes; + /* get the number! */ + (void)curlx_strtoofft(bytes, NULL, 10, &size); + } + } + } + else if(ftp->downloadsize > -1) + size = ftp->downloadsize; + + if(size > data->req.maxdownload && data->req.maxdownload > 0) + size = data->req.size = data->req.maxdownload; + else if((instate != FTP_LIST) && (data->state.prefer_ascii)) + size = -1; /* kludge for servers that understate ASCII mode file size */ + + infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T, + data->req.maxdownload); + + if(instate != FTP_LIST) + infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T, + size); + + /* FTP download: */ + conn->proto.ftpc.state_saved = instate; + conn->proto.ftpc.retr_size_saved = size; + + if(data->set.ftp_use_port) { + bool connected; + + result = AllowServerConnect(data, &connected); + if(result) + return result; + + if(!connected) { + struct ftp_conn *ftpc = &conn->proto.ftpc; + infof(data, "Data conn was not available immediately"); + ftp_state(data, FTP_STOP); + ftpc->wait_data_conn = TRUE; + } + } + else + return InitiateTransfer(data); + } + else { + if((instate == FTP_LIST) && (ftpcode == 450)) { + /* simply no matching files in the dir listing */ + ftp->transfer = PPTRANSFER_NONE; /* do not download anything */ + ftp_state(data, FTP_STOP); /* this phase is over */ + } + else { + failf(data, "RETR response: %03d", ftpcode); + return instate == FTP_RETR && ftpcode == 550? + CURLE_REMOTE_FILE_NOT_FOUND: + CURLE_FTP_COULDNT_RETR_FILE; + } + } + + return result; +} + +/* after USER, PASS and ACCT */ +static CURLcode ftp_state_loggedin(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + if(conn->bits.ftp_use_control_ssl) { + /* PBSZ = PROTECTION BUFFER SIZE. + + The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says: + + Specifically, the PROT command MUST be preceded by a PBSZ + command and a PBSZ command MUST be preceded by a successful + security data exchange (the TLS negotiation in this case) + + ... (and on page 8): + + Thus the PBSZ command must still be issued, but must have a + parameter of '0' to indicate that no buffering is taking place + and the data connection should not be encapsulated. + */ + result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0); + if(!result) + ftp_state(data, FTP_PBSZ); + } + else { + result = ftp_state_pwd(data, conn); + } + return result; +} + +/* for USER and PASS responses */ +static CURLcode ftp_state_user_resp(struct Curl_easy *data, + int ftpcode) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + /* some need password anyway, and others just return 2xx ignored */ + if((ftpcode == 331) && (ftpc->state == FTP_USER)) { + /* 331 Password required for ... + (the server requires to send the user's password too) */ + result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", + conn->passwd?conn->passwd:""); + if(!result) + ftp_state(data, FTP_PASS); + } + else if(ftpcode/100 == 2) { + /* 230 User ... logged in. + (the user logged in with or without password) */ + result = ftp_state_loggedin(data); + } + else if(ftpcode == 332) { + if(data->set.str[STRING_FTP_ACCOUNT]) { + result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s", + data->set.str[STRING_FTP_ACCOUNT]); + if(!result) + ftp_state(data, FTP_ACCT); + } + else { + failf(data, "ACCT requested but none available"); + result = CURLE_LOGIN_DENIED; + } + } + else { + /* All other response codes, like: + + 530 User ... access denied + (the server denies to log the specified user) */ + + if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] && + !ftpc->ftp_trying_alternative) { + /* Ok, USER failed. Let's try the supplied command. */ + result = + Curl_pp_sendf(data, &ftpc->pp, "%s", + data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); + if(!result) { + ftpc->ftp_trying_alternative = TRUE; + ftp_state(data, FTP_USER); + } + } + else { + failf(data, "Access denied: %03d", ftpcode); + result = CURLE_LOGIN_DENIED; + } + } + return result; +} + +/* for ACCT response */ +static CURLcode ftp_state_acct_resp(struct Curl_easy *data, + int ftpcode) +{ + CURLcode result = CURLE_OK; + if(ftpcode != 230) { + failf(data, "ACCT rejected by server: %03d", ftpcode); + result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */ + } + else + result = ftp_state_loggedin(data); + + return result; +} + + +static CURLcode ftp_statemachine(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result; + int ftpcode; + struct ftp_conn *ftpc = &conn->proto.ftpc; + struct pingpong *pp = &ftpc->pp; + static const char * const ftpauth[] = { "SSL", "TLS" }; + size_t nread = 0; + + if(pp->sendleft) + return Curl_pp_flushsend(data, pp); + + result = ftp_readresp(data, FIRSTSOCKET, pp, &ftpcode, &nread); + if(result) + return result; + + if(ftpcode) { + /* we have now received a full FTP server response */ + switch(ftpc->state) { + case FTP_WAIT220: + if(ftpcode == 230) { + /* 230 User logged in - already! Take as 220 if TLS required. */ + if(data->set.use_ssl <= CURLUSESSL_TRY || + conn->bits.ftp_use_control_ssl) + return ftp_state_user_resp(data, ftpcode); + } + else if(ftpcode != 220) { + failf(data, "Got a %03d ftp-server response when 220 was expected", + ftpcode); + return CURLE_WEIRD_SERVER_REPLY; + } + + /* We have received a 220 response fine, now we proceed. */ +#ifdef HAVE_GSSAPI + if(data->set.krb) { + /* If not anonymous login, try a secure login. Note that this + procedure is still BLOCKING. */ + + Curl_sec_request_prot(conn, "private"); + /* We set private first as default, in case the line below fails to + set a valid level */ + Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]); + + if(Curl_sec_login(data, conn)) { + failf(data, "secure login failed"); + return CURLE_WEIRD_SERVER_REPLY; + } + infof(data, "Authentication successful"); + } +#endif + + if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) { + /* We do not have a SSL/TLS control connection yet, but FTPS is + requested. Try a FTPS connection now */ + + ftpc->count3 = 0; + switch(data->set.ftpsslauth) { + case CURLFTPAUTH_DEFAULT: + case CURLFTPAUTH_SSL: + ftpc->count2 = 1; /* add one to get next */ + ftpc->count1 = 0; + break; + case CURLFTPAUTH_TLS: + ftpc->count2 = -1; /* subtract one to get next */ + ftpc->count1 = 1; + break; + default: + failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d", + (int)data->set.ftpsslauth); + return CURLE_UNKNOWN_OPTION; /* we do not know what to do */ + } + result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", + ftpauth[ftpc->count1]); + if(!result) + ftp_state(data, FTP_AUTH); + } + else + result = ftp_state_user(data, conn); + break; + + case FTP_AUTH: + /* we have gotten the response to a previous AUTH command */ + + if(pp->overflow) + return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */ + + /* RFC2228 (page 5) says: + * + * If the server is willing to accept the named security mechanism, + * and does not require any security data, it must respond with + * reply code 234/334. + */ + + if((ftpcode == 234) || (ftpcode == 334)) { + /* this was BLOCKING, keep it so for now */ + bool done; + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + if(result) { + /* we failed and bail out */ + return CURLE_USE_SSL_FAILED; + } + } + result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done); + if(!result) { + conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */ + conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */ + result = ftp_state_user(data, conn); + } + } + else if(ftpc->count3 < 1) { + ftpc->count3++; + ftpc->count1 += ftpc->count2; /* get next attempt */ + result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", + ftpauth[ftpc->count1]); + /* remain in this same state */ + } + else { + if(data->set.use_ssl > CURLUSESSL_TRY) + /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */ + result = CURLE_USE_SSL_FAILED; + else + /* ignore the failure and continue */ + result = ftp_state_user(data, conn); + } + break; + + case FTP_USER: + case FTP_PASS: + result = ftp_state_user_resp(data, ftpcode); + break; + + case FTP_ACCT: + result = ftp_state_acct_resp(data, ftpcode); + break; + + case FTP_PBSZ: + result = + Curl_pp_sendf(data, &ftpc->pp, "PROT %c", + data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); + if(!result) + ftp_state(data, FTP_PROT); + break; + + case FTP_PROT: + if(ftpcode/100 == 2) + /* We have enabled SSL for the data connection! */ + conn->bits.ftp_use_data_ssl = + (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE; + /* FTP servers typically responds with 500 if they decide to reject + our 'P' request */ + else if(data->set.use_ssl > CURLUSESSL_CONTROL) + /* we failed and bails out */ + return CURLE_USE_SSL_FAILED; + + if(data->set.ftp_ccc) { + /* CCC - Clear Command Channel + */ + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC"); + if(!result) + ftp_state(data, FTP_CCC); + } + else + result = ftp_state_pwd(data, conn); + break; + + case FTP_CCC: + if(ftpcode < 500) { + /* First shut down the SSL layer (note: this call will block) */ + /* This has only been tested on the proftpd server, and the mod_tls + * code sends a close notify alert without waiting for a close notify + * alert in response. Thus we wait for a close notify alert from the + * server, but we do not send one. Let's hope other servers do + * the same... */ + result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET, + (data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)); + + if(result) + failf(data, "Failed to clear the command channel (CCC)"); + } + if(!result) + /* Then continue as normal */ + result = ftp_state_pwd(data, conn); + break; + + case FTP_PWD: + if(ftpcode == 257) { + char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first + letter */ + bool entry_extracted = FALSE; + struct dynbuf out; + Curl_dyn_init(&out, 1000); + + /* Reply format is like + 257[rubbish]"" and the + RFC959 says + + The directory name can contain any character; embedded + double-quotes should be escaped by double-quotes (the + "quote-doubling" convention). + */ + + /* scan for the first double-quote for non-standard responses */ + while(*ptr != '\n' && *ptr != '\0' && *ptr != '"') + ptr++; + + if('\"' == *ptr) { + /* it started good */ + for(ptr++; *ptr; ptr++) { + if('\"' == *ptr) { + if('\"' == ptr[1]) { + /* "quote-doubling" */ + result = Curl_dyn_addn(&out, &ptr[1], 1); + ptr++; + } + else { + /* end of path */ + if(Curl_dyn_len(&out)) + entry_extracted = TRUE; + break; /* get out of this loop */ + } + } + else + result = Curl_dyn_addn(&out, ptr, 1); + if(result) + return result; + } + } + if(entry_extracted) { + /* If the path name does not look like an absolute path (i.e.: it + does not start with a '/'), we probably need some server-dependent + adjustments. For example, this is the case when connecting to + an OS400 FTP server: this server supports two name syntaxes, + the default one being incompatible with standard paths. In + addition, this server switches automatically to the regular path + syntax when one is encountered in a command: this results in + having an entrypath in the wrong syntax when later used in CWD. + The method used here is to check the server OS: we do it only + if the path name looks strange to minimize overhead on other + systems. */ + char *dir = Curl_dyn_ptr(&out); + + if(!ftpc->server_os && dir[0] != '/') { + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST"); + if(result) { + free(dir); + return result; + } + Curl_safefree(ftpc->entrypath); + ftpc->entrypath = dir; /* remember this */ + infof(data, "Entry path is '%s'", ftpc->entrypath); + /* also save it where getinfo can access it: */ + data->state.most_recent_ftp_entrypath = ftpc->entrypath; + ftp_state(data, FTP_SYST); + break; + } + + Curl_safefree(ftpc->entrypath); + ftpc->entrypath = dir; /* remember this */ + infof(data, "Entry path is '%s'", ftpc->entrypath); + /* also save it where getinfo can access it: */ + data->state.most_recent_ftp_entrypath = ftpc->entrypath; + } + else { + /* could not get the path */ + Curl_dyn_free(&out); + infof(data, "Failed to figure out path"); + } + } + ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ + CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data)); + break; + + case FTP_SYST: + if(ftpcode == 215) { + char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first + letter */ + char *os; + char *start; + + /* Reply format is like + 215 + */ + while(*ptr == ' ') + ptr++; + for(start = ptr; *ptr && *ptr != ' '; ptr++) + ; + os = Curl_memdup0(start, ptr - start); + if(!os) + return CURLE_OUT_OF_MEMORY; + + /* Check for special servers here. */ + if(strcasecompare(os, "OS/400")) { + /* Force OS400 name format 1. */ + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1"); + if(result) { + free(os); + return result; + } + /* remember target server OS */ + Curl_safefree(ftpc->server_os); + ftpc->server_os = os; + ftp_state(data, FTP_NAMEFMT); + break; + } + /* Nothing special for the target server. */ + /* remember target server OS */ + Curl_safefree(ftpc->server_os); + ftpc->server_os = os; + } + else { + /* Cannot identify server OS. Continue anyway and cross fingers. */ + } + + ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ + CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data)); + break; + + case FTP_NAMEFMT: + if(ftpcode == 250) { + /* Name format change successful: reload initial path. */ + ftp_state_pwd(data, conn); + break; + } + + ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ + CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data)); + break; + + case FTP_QUOTE: + case FTP_POSTQUOTE: + case FTP_RETR_PREQUOTE: + case FTP_STOR_PREQUOTE: + if((ftpcode >= 400) && !ftpc->count2) { + /* failure response code, and not allowed to fail */ + failf(data, "QUOT command failed with %03d", ftpcode); + result = CURLE_QUOTE_ERROR; + } + else + result = ftp_state_quote(data, FALSE, ftpc->state); + break; + + case FTP_CWD: + if(ftpcode/100 != 2) { + /* failure to CWD there */ + if(data->set.ftp_create_missing_dirs && + ftpc->cwdcount && !ftpc->count2) { + /* try making it */ + ftpc->count2++; /* counter to prevent CWD-MKD loops */ + + /* count3 is set to allow MKD to fail once per dir. In the case when + CWD fails and then MKD fails (due to another session raced it to + create the dir) this then allows for a second try to CWD to it. */ + ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0; + + result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s", + ftpc->dirs[ftpc->cwdcount - 1]); + if(!result) + ftp_state(data, FTP_MKD); + } + else { + /* return failure */ + failf(data, "Server denied you to change to the given directory"); + ftpc->cwdfail = TRUE; /* do not remember this path as we failed + to enter it */ + result = CURLE_REMOTE_ACCESS_DENIED; + } + } + else { + /* success */ + ftpc->count2 = 0; + if(++ftpc->cwdcount <= ftpc->dirdepth) + /* send next CWD */ + result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", + ftpc->dirs[ftpc->cwdcount - 1]); + else + result = ftp_state_mdtm(data); + } + break; + + case FTP_MKD: + if((ftpcode/100 != 2) && !ftpc->count3--) { + /* failure to MKD the dir */ + failf(data, "Failed to MKD dir: %03d", ftpcode); + result = CURLE_REMOTE_ACCESS_DENIED; + } + else { + ftp_state(data, FTP_CWD); + /* send CWD */ + result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", + ftpc->dirs[ftpc->cwdcount - 1]); + } + break; + + case FTP_MDTM: + result = ftp_state_mdtm_resp(data, ftpcode); + break; + + case FTP_TYPE: + case FTP_LIST_TYPE: + case FTP_RETR_TYPE: + case FTP_STOR_TYPE: + result = ftp_state_type_resp(data, ftpcode, ftpc->state); + break; + + case FTP_SIZE: + case FTP_RETR_SIZE: + case FTP_STOR_SIZE: + result = ftp_state_size_resp(data, ftpcode, ftpc->state); + break; + + case FTP_REST: + case FTP_RETR_REST: + result = ftp_state_rest_resp(data, conn, ftpcode, ftpc->state); + break; + + case FTP_PRET: + if(ftpcode != 200) { + /* there only is this one standard OK return code. */ + failf(data, "PRET command not accepted: %03d", ftpcode); + return CURLE_FTP_PRET_FAILED; + } + result = ftp_state_use_pasv(data, conn); + break; + + case FTP_PASV: + result = ftp_state_pasv_resp(data, ftpcode); + break; + + case FTP_PORT: + result = ftp_state_port_resp(data, ftpcode); + break; + + case FTP_LIST: + case FTP_RETR: + result = ftp_state_get_resp(data, ftpcode, ftpc->state); + break; + + case FTP_STOR: + result = ftp_state_stor_resp(data, ftpcode, ftpc->state); + break; + + case FTP_QUIT: + default: + /* internal error */ + ftp_state(data, FTP_STOP); + break; + } + } /* if(ftpcode) */ + + return result; +} + + +/* called repeatedly until done from multi.c */ +static CURLcode ftp_multi_statemach(struct Curl_easy *data, + bool *done) +{ + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE); + + /* Check for the state outside of the Curl_socket_check() return code checks + since at times we are in fact already in this state when this function + gets called. */ + *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE; + + return result; +} + +static CURLcode ftp_block_statemach(struct Curl_easy *data, + struct connectdata *conn) +{ + struct ftp_conn *ftpc = &conn->proto.ftpc; + struct pingpong *pp = &ftpc->pp; + CURLcode result = CURLE_OK; + + while(ftpc->state != FTP_STOP) { + result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */); + if(result) + break; + } + + return result; +} + +/* + * ftp_connect() should do everything that is to be considered a part of + * the connection phase. + * + * The variable 'done' points to will be TRUE if the protocol-layer connect + * phase is done when this function returns, or FALSE if not. + * + */ +static CURLcode ftp_connect(struct Curl_easy *data, + bool *done) /* see description above */ +{ + CURLcode result; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + struct pingpong *pp = &ftpc->pp; + + *done = FALSE; /* default to not done yet */ + + /* We always support persistent connections on ftp */ + connkeep(conn, "FTP default"); + + PINGPONG_SETUP(pp, ftp_statemachine, ftp_endofresp); + + if(conn->handler->flags & PROTOPT_SSL) { + /* BLOCKING */ + result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); + if(result) + return result; + conn->bits.ftp_use_control_ssl = TRUE; + } + + Curl_pp_init(pp); /* once per transfer */ + + /* When we connect, we start in the state where we await the 220 + response */ + ftp_state(data, FTP_WAIT220); + + result = ftp_multi_statemach(data, done); + + return result; +} + +/*********************************************************************** + * + * ftp_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + struct connectdata *conn = data->conn; + struct FTP *ftp = data->req.p.ftp; + struct ftp_conn *ftpc = &conn->proto.ftpc; + struct pingpong *pp = &ftpc->pp; + ssize_t nread; + int ftpcode; + CURLcode result = CURLE_OK; + char *rawPath = NULL; + size_t pathLen = 0; + + if(!ftp) + return CURLE_OK; + + switch(status) { + case CURLE_BAD_DOWNLOAD_RESUME: + case CURLE_FTP_WEIRD_PASV_REPLY: + case CURLE_FTP_PORT_FAILED: + case CURLE_FTP_ACCEPT_FAILED: + case CURLE_FTP_ACCEPT_TIMEOUT: + case CURLE_FTP_COULDNT_SET_TYPE: + case CURLE_FTP_COULDNT_RETR_FILE: + case CURLE_PARTIAL_FILE: + case CURLE_UPLOAD_FAILED: + case CURLE_REMOTE_ACCESS_DENIED: + case CURLE_FILESIZE_EXCEEDED: + case CURLE_REMOTE_FILE_NOT_FOUND: + case CURLE_WRITE_ERROR: + /* the connection stays alive fine even though this happened */ + case CURLE_OK: /* does not affect the control connection's status */ + if(!premature) + break; + + /* until we cope better with prematurely ended requests, let them + * fallback as if in complete failure */ + FALLTHROUGH(); + default: /* by default, an error means the control connection is + wedged and should not be used anymore */ + ftpc->ctl_valid = FALSE; + ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the + current path, as this connection is going */ + connclose(conn, "FTP ended with bad error code"); + result = status; /* use the already set error code */ + break; + } + + if(data->state.wildcardmatch) { + if(data->set.chunk_end && ftpc->file) { + Curl_set_in_callback(data, true); + data->set.chunk_end(data->set.wildcardptr); + Curl_set_in_callback(data, false); + } + ftpc->known_filesize = -1; + } + + if(!result) + /* get the url-decoded "raw" path */ + result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, + REJECT_CTRL); + if(result) { + /* We can limp along anyway (and should try to since we may already be in + * the error path) */ + ftpc->ctl_valid = FALSE; /* mark control connection as bad */ + connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ + free(ftpc->prevpath); + ftpc->prevpath = NULL; /* no path remembering */ + } + else { /* remember working directory for connection reuse */ + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) + free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */ + else { + free(ftpc->prevpath); + + if(!ftpc->cwdfail) { + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + pathLen = 0; /* relative path => working directory is FTP home */ + else + pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */ + + rawPath[pathLen] = '\0'; + ftpc->prevpath = rawPath; + } + else { + free(rawPath); + ftpc->prevpath = NULL; /* no path */ + } + } + + if(ftpc->prevpath) + infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath); + } + + /* free the dir tree and file parts */ + freedirs(ftpc); + + /* shut down the socket to inform the server we are done */ + +#ifdef _WIN32_WCE + shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */ +#endif + + if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) { + if(!result && ftpc->dont_check && data->req.maxdownload > 0) { + /* partial download completed */ + result = Curl_pp_sendf(data, pp, "%s", "ABOR"); + if(result) { + failf(data, "Failure sending ABOR command: %s", + curl_easy_strerror(result)); + ftpc->ctl_valid = FALSE; /* mark control connection as bad */ + connclose(conn, "ABOR command failed"); /* connection closure */ + } + } + + close_secondarysocket(data); + } + + if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid && + pp->pending_resp && !premature) { + /* + * Let's see what the server says about the transfer we just performed, + * but lower the timeout as sometimes this connection has died while the + * data has been transferred. This happens when doing through NATs etc that + * abandon old silent connections. + */ + timediff_t old_time = pp->response_time; + + pp->response_time = 60*1000; /* give it only a minute for now */ + pp->response = Curl_now(); /* timeout relative now */ + + result = Curl_GetFTPResponse(data, &nread, &ftpcode); + + pp->response_time = old_time; /* set this back to previous value */ + + if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) { + failf(data, "control connection looks dead"); + ftpc->ctl_valid = FALSE; /* mark control connection as bad */ + connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */ + } + + if(result) { + Curl_safefree(ftp->pathalloc); + return result; + } + + if(ftpc->dont_check && data->req.maxdownload > 0) { + /* we have just sent ABOR and there is no reliable way to check if it was + * successful or not; we have to close the connection now */ + infof(data, "partial download completed, closing connection"); + connclose(conn, "Partial download with no ability to check"); + return result; + } + + if(!ftpc->dont_check) { + /* 226 Transfer complete, 250 Requested file action okay, completed. */ + switch(ftpcode) { + case 226: + case 250: + break; + case 552: + failf(data, "Exceeded storage allocation"); + result = CURLE_REMOTE_DISK_FULL; + break; + default: + failf(data, "server did not report OK, got %d", ftpcode); + result = CURLE_PARTIAL_FILE; + break; + } + } + } + + if(result || premature) + /* the response code from the transfer showed an error already so no + use checking further */ + ; + else if(data->state.upload) { + if((-1 != data->state.infilesize) && + (data->state.infilesize != data->req.writebytecount) && + !data->set.crlf && + (ftp->transfer == PPTRANSFER_BODY)) { + failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T + " out of %" CURL_FORMAT_CURL_OFF_T " bytes)", + data->req.writebytecount, data->state.infilesize); + result = CURLE_PARTIAL_FILE; + } + } + else { + if((-1 != data->req.size) && + (data->req.size != data->req.bytecount) && +#ifdef CURL_DO_LINEEND_CONV + /* Most FTP servers do not adjust their file SIZE response for CRLFs, + * so we will check to see if the discrepancy can be explained by the + * number of CRLFs we have changed to LFs. + */ + ((data->req.size + data->state.crlf_conversions) != + data->req.bytecount) && +#endif /* CURL_DO_LINEEND_CONV */ + (data->req.maxdownload != data->req.bytecount)) { + failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T + " bytes", data->req.bytecount); + result = CURLE_PARTIAL_FILE; + } + else if(!ftpc->dont_check && + !data->req.bytecount && + (data->req.size>0)) { + failf(data, "No data was received"); + result = CURLE_FTP_COULDNT_RETR_FILE; + } + } + + /* clear these for next connection */ + ftp->transfer = PPTRANSFER_BODY; + ftpc->dont_check = FALSE; + + /* Send any post-transfer QUOTE strings? */ + if(!status && !result && !premature && data->set.postquote) + result = ftp_sendquote(data, conn, data->set.postquote); + CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_DSTATE(data), result); + Curl_safefree(ftp->pathalloc); + return result; +} + +/*********************************************************************** + * + * ftp_sendquote() + * + * Where a 'quote' means a list of custom commands to send to the server. + * The quote list is passed as an argument. + * + * BLOCKING + */ + +static +CURLcode ftp_sendquote(struct Curl_easy *data, + struct connectdata *conn, struct curl_slist *quote) +{ + struct curl_slist *item; + struct ftp_conn *ftpc = &conn->proto.ftpc; + struct pingpong *pp = &ftpc->pp; + + item = quote; + while(item) { + if(item->data) { + ssize_t nread; + char *cmd = item->data; + bool acceptfail = FALSE; + CURLcode result; + int ftpcode = 0; + + /* if a command starts with an asterisk, which a legal FTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server responds. */ + + if(cmd[0] == '*') { + cmd++; + acceptfail = TRUE; + } + + result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); + if(!result) { + pp->response = Curl_now(); /* timeout relative now */ + result = Curl_GetFTPResponse(data, &nread, &ftpcode); + } + if(result) + return result; + + if(!acceptfail && (ftpcode >= 400)) { + failf(data, "QUOT string not accepted: %s", cmd); + return CURLE_QUOTE_ERROR; + } + } + + item = item->next; + } + + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_need_type() + * + * Returns TRUE if we in the current situation should send TYPE + */ +static int ftp_need_type(struct connectdata *conn, + bool ascii_wanted) +{ + return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I'); +} + +/*********************************************************************** + * + * ftp_nb_type() + * + * Set TYPE. We only deal with ASCII or BINARY so this function + * sets one of them. + * If the transfer type is not sent, simulate on OK response in newstate + */ +static CURLcode ftp_nb_type(struct Curl_easy *data, + struct connectdata *conn, + bool ascii, ftpstate newstate) +{ + struct ftp_conn *ftpc = &conn->proto.ftpc; + CURLcode result; + char want = (char)(ascii?'A':'I'); + + if(ftpc->transfertype == want) { + ftp_state(data, newstate); + return ftp_state_type_resp(data, 200, newstate); + } + + result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want); + if(!result) { + ftp_state(data, newstate); + + /* keep track of our current transfer type */ + ftpc->transfertype = want; + } + return result; +} + +/*************************************************************************** + * + * ftp_pasv_verbose() + * + * This function only outputs some informationals about this second connection + * when we have issued a PASV command before and thus we have connected to a + * possibly new IP address. + * + */ +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void +ftp_pasv_verbose(struct Curl_easy *data, + struct Curl_addrinfo *ai, + char *newhost, /* ascii version */ + int port) +{ + char buf[256]; + Curl_printable_address(ai, buf, sizeof(buf)); + infof(data, "Connecting to %s (%s) port %d", newhost, buf, port); +} +#endif + +/* + * ftp_do_more() + * + * This function shall be called when the second FTP (data) connection is + * connected. + * + * 'complete' can return 0 for incomplete, 1 for done and -1 for go back + * (which basically is only for when PASV is being sent to retry a failed + * EPSV). + */ + +static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) +{ + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + CURLcode result = CURLE_OK; + bool connected = FALSE; + bool complete = FALSE; + + /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP + * proxy then the state will not be valid until after that connection is + * complete */ + struct FTP *ftp = NULL; + + /* if the second connection is not done yet, wait for it to have + * connected to the remote host. When using proxy tunneling, this + * means the tunnel needs to have been establish. However, we + * can not expect the remote host to talk to us in any way yet. + * So, when using ftps: the SSL handshake will not start until we + * tell the remote server that we are there. */ + if(conn->cfilter[SECONDARYSOCKET]) { + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); + if(result || !Curl_conn_is_ip_connected(data, SECONDARYSOCKET)) { + if(result && (ftpc->count1 == 0)) { + *completep = -1; /* go back to DOING please */ + /* this is a EPSV connect failing, try PASV instead */ + return ftp_epsv_disable(data, conn); + } + return result; + } + } + + /* Curl_proxy_connect might have moved the protocol state */ + ftp = data->req.p.ftp; + + if(ftpc->state) { + /* already in a state so skip the initial commands. + They are only done to kickstart the do_more state */ + result = ftp_multi_statemach(data, &complete); + + *completep = (int)complete; + + /* if we got an error or if we do not wait for a data connection return + immediately */ + if(result || !ftpc->wait_data_conn) + return result; + + /* if we reach the end of the FTP state machine here, *complete will be + TRUE but so is ftpc->wait_data_conn, which says we need to wait for the + data connection and therefore we are not actually complete */ + *completep = 0; + } + + if(ftp->transfer <= PPTRANSFER_INFO) { + /* a transfer is about to take place, or if not a filename was given so we + will do a SIZE on it later and then we need the right TYPE first */ + + if(ftpc->wait_data_conn) { + bool serv_conned; + + result = ReceivedServerConnect(data, &serv_conned); + if(result) + return result; /* Failed to accept data connection */ + + if(serv_conned) { + /* It looks data connection is established */ + result = AcceptServerConnect(data); + ftpc->wait_data_conn = FALSE; + if(!result) + result = InitiateTransfer(data); + + if(result) + return result; + + *completep = 1; /* this state is now complete when the server has + connected back to us */ + } + } + else if(data->state.upload) { + result = ftp_nb_type(data, conn, data->state.prefer_ascii, + FTP_STOR_TYPE); + if(result) + return result; + + result = ftp_multi_statemach(data, &complete); + *completep = (int)complete; + } + else { + /* download */ + ftp->downloadsize = -1; /* unknown as of yet */ + + result = Curl_range(data); + + if(result == CURLE_OK && data->req.maxdownload >= 0) { + /* Do not check for successful transfer */ + ftpc->dont_check = TRUE; + } + + if(result) + ; + else if(data->state.list_only || !ftpc->file) { + /* The specified path ends with a slash, and therefore we think this + is a directory that is requested, use LIST. But before that we + need to set ASCII transfer mode. */ + + /* But only if a body transfer was requested. */ + if(ftp->transfer == PPTRANSFER_BODY) { + result = ftp_nb_type(data, conn, TRUE, FTP_LIST_TYPE); + if(result) + return result; + } + /* otherwise just fall through */ + } + else { + result = ftp_nb_type(data, conn, data->state.prefer_ascii, + FTP_RETR_TYPE); + if(result) + return result; + } + + result = ftp_multi_statemach(data, &complete); + *completep = (int)complete; + } + return result; + } + + /* no data to transfer */ + Curl_xfer_setup_nop(data); + + if(!ftpc->wait_data_conn) { + /* no waiting for the data connection so this is now complete */ + *completep = 1; + CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_DSTATE(data), + (int)result); + } + + return result; +} + + + +/*********************************************************************** + * + * ftp_perform() + * + * This is the actual DO function for FTP. Get a file/directory according to + * the options previously setup. + */ + +static +CURLcode ftp_perform(struct Curl_easy *data, + bool *connected, /* connect status after PASV / PORT */ + bool *dophase_done) +{ + /* this is FTP and no proxy */ + CURLcode result = CURLE_OK; + + CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_DSTATE(data)); + + if(data->req.no_body) { + /* requested no body means no transfer... */ + struct FTP *ftp = data->req.p.ftp; + ftp->transfer = PPTRANSFER_INFO; + } + + *dophase_done = FALSE; /* not done yet */ + + /* start the first command in the DO phase */ + result = ftp_state_quote(data, TRUE, FTP_QUOTE); + if(result) + return result; + + /* run the state-machine */ + result = ftp_multi_statemach(data, dophase_done); + + *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET); + + if(*connected) + infof(data, "[FTP] [%s] perform, DATA connection established", + FTP_DSTATE(data)); + else + CURL_TRC_FTP(data, "[%s] perform, awaiting DATA connect", + FTP_DSTATE(data)); + + if(*dophase_done) + CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_DSTATE(data)); + + return result; +} + +static void wc_data_dtor(void *ptr) +{ + struct ftp_wc *ftpwc = ptr; + if(ftpwc && ftpwc->parser) + Curl_ftp_parselist_data_free(&ftpwc->parser); + free(ftpwc); +} + +static CURLcode init_wc_data(struct Curl_easy *data) +{ + char *last_slash; + struct FTP *ftp = data->req.p.ftp; + char *path = ftp->path; + struct WildcardData *wildcard = data->wildcard; + CURLcode result = CURLE_OK; + struct ftp_wc *ftpwc = NULL; + + last_slash = strrchr(ftp->path, '/'); + if(last_slash) { + last_slash++; + if(last_slash[0] == '\0') { + wildcard->state = CURLWC_CLEAN; + result = ftp_parse_url_path(data); + return result; + } + wildcard->pattern = strdup(last_slash); + if(!wildcard->pattern) + return CURLE_OUT_OF_MEMORY; + last_slash[0] = '\0'; /* cut file from path */ + } + else { /* there is only 'wildcard pattern' or nothing */ + if(path[0]) { + wildcard->pattern = strdup(path); + if(!wildcard->pattern) + return CURLE_OUT_OF_MEMORY; + path[0] = '\0'; + } + else { /* only list */ + wildcard->state = CURLWC_CLEAN; + result = ftp_parse_url_path(data); + return result; + } + } + + /* program continues only if URL is not ending with slash, allocate needed + resources for wildcard transfer */ + + /* allocate ftp protocol specific wildcard data */ + ftpwc = calloc(1, sizeof(struct ftp_wc)); + if(!ftpwc) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + /* INITIALIZE parselist structure */ + ftpwc->parser = Curl_ftp_parselist_data_alloc(); + if(!ftpwc->parser) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */ + wildcard->dtor = wc_data_dtor; + + /* wildcard does not support NOCWD option (assert it?) */ + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + data->set.ftp_filemethod = FTPFILE_MULTICWD; + + /* try to parse ftp URL */ + result = ftp_parse_url_path(data); + if(result) { + goto fail; + } + + wildcard->path = strdup(ftp->path); + if(!wildcard->path) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + /* backup old write_function */ + ftpwc->backup.write_function = data->set.fwrite_func; + /* parsing write function */ + data->set.fwrite_func = Curl_ftp_parselist; + /* backup old file descriptor */ + ftpwc->backup.file_descriptor = data->set.out; + /* let the writefunc callback know the transfer */ + data->set.out = data; + + infof(data, "Wildcard - Parsing started"); + return CURLE_OK; + +fail: + if(ftpwc) { + Curl_ftp_parselist_data_free(&ftpwc->parser); + free(ftpwc); + } + Curl_safefree(wildcard->pattern); + wildcard->dtor = ZERO_NULL; + wildcard->ftpwc = NULL; + return result; +} + +static CURLcode wc_statemach(struct Curl_easy *data) +{ + struct WildcardData * const wildcard = data->wildcard; + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + + for(;;) { + switch(wildcard->state) { + case CURLWC_INIT: + result = init_wc_data(data); + if(wildcard->state == CURLWC_CLEAN) + /* only listing! */ + return result; + wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING; + return result; + + case CURLWC_MATCHING: { + /* In this state is LIST response successfully parsed, so lets restore + previous WRITEFUNCTION callback and WRITEDATA pointer */ + struct ftp_wc *ftpwc = wildcard->ftpwc; + data->set.fwrite_func = ftpwc->backup.write_function; + data->set.out = ftpwc->backup.file_descriptor; + ftpwc->backup.write_function = ZERO_NULL; + ftpwc->backup.file_descriptor = NULL; + wildcard->state = CURLWC_DOWNLOADING; + + if(Curl_ftp_parselist_geterror(ftpwc->parser)) { + /* error found in LIST parsing */ + wildcard->state = CURLWC_CLEAN; + continue; + } + if(wildcard->filelist.size == 0) { + /* no corresponding file */ + wildcard->state = CURLWC_CLEAN; + return CURLE_REMOTE_FILE_NOT_FOUND; + } + continue; + } + + case CURLWC_DOWNLOADING: { + /* filelist has at least one file, lets get first one */ + struct ftp_conn *ftpc = &conn->proto.ftpc; + struct curl_fileinfo *finfo = wildcard->filelist.head->ptr; + struct FTP *ftp = data->req.p.ftp; + + char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename); + if(!tmp_path) + return CURLE_OUT_OF_MEMORY; + + /* switch default ftp->path and tmp_path */ + free(ftp->pathalloc); + ftp->pathalloc = ftp->path = tmp_path; + + infof(data, "Wildcard - START of \"%s\"", finfo->filename); + if(data->set.chunk_bgn) { + long userresponse; + Curl_set_in_callback(data, true); + userresponse = data->set.chunk_bgn( + finfo, data->set.wildcardptr, (int)wildcard->filelist.size); + Curl_set_in_callback(data, false); + switch(userresponse) { + case CURL_CHUNK_BGN_FUNC_SKIP: + infof(data, "Wildcard - \"%s\" skipped by user", + finfo->filename); + wildcard->state = CURLWC_SKIP; + continue; + case CURL_CHUNK_BGN_FUNC_FAIL: + return CURLE_CHUNK_FAILED; + } + } + + if(finfo->filetype != CURLFILETYPE_FILE) { + wildcard->state = CURLWC_SKIP; + continue; + } + + if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE) + ftpc->known_filesize = finfo->size; + + result = ftp_parse_url_path(data); + if(result) + return result; + + /* we do not need the Curl_fileinfo of first file anymore */ + Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); + + if(wildcard->filelist.size == 0) { /* remains only one file to down. */ + wildcard->state = CURLWC_CLEAN; + /* after that will be ftp_do called once again and no transfer + will be done because of CURLWC_CLEAN state */ + return CURLE_OK; + } + return result; + } + + case CURLWC_SKIP: { + if(data->set.chunk_end) { + Curl_set_in_callback(data, true); + data->set.chunk_end(data->set.wildcardptr); + Curl_set_in_callback(data, false); + } + Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); + wildcard->state = (wildcard->filelist.size == 0) ? + CURLWC_CLEAN : CURLWC_DOWNLOADING; + continue; + } + + case CURLWC_CLEAN: { + struct ftp_wc *ftpwc = wildcard->ftpwc; + result = CURLE_OK; + if(ftpwc) + result = Curl_ftp_parselist_geterror(ftpwc->parser); + + wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE; + return result; + } + + case CURLWC_DONE: + case CURLWC_ERROR: + case CURLWC_CLEAR: + if(wildcard->dtor) { + wildcard->dtor(wildcard->ftpwc); + wildcard->ftpwc = NULL; + } + return result; + } + } + /* UNREACHABLE */ +} + +/*********************************************************************** + * + * ftp_do() + * + * This function is registered as 'curl_do' function. It decodes the path + * parts etc as a wrapper to the actual DO function (ftp_perform). + * + * The input argument is already checked for validity. + */ +static CURLcode ftp_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + *done = FALSE; /* default to false */ + ftpc->wait_data_conn = FALSE; /* default to no such wait */ + +#ifdef CURL_DO_LINEEND_CONV + { + /* FTP data may need conversion. */ + struct Curl_cwriter *ftp_lc_writer; + + result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc, + CURL_CW_CONTENT_DECODE); + if(result) + return result; + + result = Curl_cwriter_add(data, ftp_lc_writer); + if(result) { + Curl_cwriter_free(data, ftp_lc_writer); + return result; + } + } +#endif /* CURL_DO_LINEEND_CONV */ + + if(data->state.wildcardmatch) { + result = wc_statemach(data); + if(data->wildcard->state == CURLWC_SKIP || + data->wildcard->state == CURLWC_DONE) { + /* do not call ftp_regular_transfer */ + return CURLE_OK; + } + if(result) /* error, loop or skipping the file */ + return result; + } + else { /* no wildcard FSM needed */ + result = ftp_parse_url_path(data); + if(result) + return result; + } + + result = ftp_regular_transfer(data, done); + + return result; +} + +/*********************************************************************** + * + * ftp_quit() + * + * This should be called before calling sclose() on an ftp control connection + * (not data connections). We should then wait for the response from the + * server before returning. The calling code should then try to close the + * connection. + * + */ +static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + + if(conn->proto.ftpc.ctl_valid) { + result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT"); + if(result) { + failf(data, "Failure sending QUIT command: %s", + curl_easy_strerror(result)); + conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */ + connclose(conn, "QUIT command failed"); /* mark for connection closure */ + ftp_state(data, FTP_STOP); + return result; + } + + ftp_state(data, FTP_QUIT); + + result = ftp_block_statemach(data, conn); + } + + return result; +} + +/*********************************************************************** + * + * ftp_disconnect() + * + * Disconnect from an FTP server. Cleanup protocol-specific per-connection + * resources. BLOCKING. + */ +static CURLcode ftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + struct ftp_conn *ftpc = &conn->proto.ftpc; + struct pingpong *pp = &ftpc->pp; + + /* We cannot send quit unconditionally. If this connection is stale or + bad in any way, sending quit and waiting around here will make the + disconnect wait in vain and cause more problems than we need to. + + ftp_quit() will check the state of ftp->ctl_valid. If it is ok it + will try to send the QUIT command, otherwise it will just return. + */ + if(dead_connection) + ftpc->ctl_valid = FALSE; + + /* The FTP session may or may not have been allocated/setup at this point! */ + (void)ftp_quit(data, conn); /* ignore errors on the QUIT */ + + if(ftpc->entrypath) { + if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) { + data->state.most_recent_ftp_entrypath = NULL; + } + Curl_safefree(ftpc->entrypath); + } + + freedirs(ftpc); + Curl_safefree(ftpc->account); + Curl_safefree(ftpc->alternative_to_user); + Curl_safefree(ftpc->prevpath); + Curl_safefree(ftpc->server_os); + Curl_pp_disconnect(pp); + Curl_sec_end(conn); + return CURLE_OK; +} + +#ifdef _MSC_VER +/* warning C4706: assignment within conditional expression */ +#pragma warning(disable:4706) +#endif + +/*********************************************************************** + * + * ftp_parse_url_path() + * + * Parse the URL path into separate path components. + * + */ +static +CURLcode ftp_parse_url_path(struct Curl_easy *data) +{ + /* the ftp struct is already inited in ftp_connect() */ + struct FTP *ftp = data->req.p.ftp; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + const char *slashPos = NULL; + const char *fileName = NULL; + CURLcode result = CURLE_OK; + char *rawPath = NULL; /* url-decoded "raw" path */ + size_t pathLen = 0; + + ftpc->ctl_valid = FALSE; + ftpc->cwdfail = FALSE; + + /* url-decode ftp path before further evaluation */ + result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL); + if(result) { + failf(data, "path contains control characters"); + return result; + } + + switch(data->set.ftp_filemethod) { + case FTPFILE_NOCWD: /* fastest, but less standard-compliant */ + + if((pathLen > 0) && (rawPath[pathLen - 1] != '/')) + fileName = rawPath; /* this is a full file path */ + /* + else: ftpc->file is not used anywhere other than for operations on + a file. In other words, never for directory operations. + So we can safely leave filename as NULL here and use it as a + argument in dir/file decisions. + */ + break; + + case FTPFILE_SINGLECWD: + slashPos = strrchr(rawPath, '/'); + if(slashPos) { + /* get path before last slash, except for / */ + size_t dirlen = slashPos - rawPath; + if(dirlen == 0) + dirlen = 1; + + ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) { + free(rawPath); + return CURLE_OUT_OF_MEMORY; + } + + ftpc->dirs[0] = Curl_memdup0(rawPath, dirlen); + if(!ftpc->dirs[0]) { + free(rawPath); + return CURLE_OUT_OF_MEMORY; + } + + ftpc->dirdepth = 1; /* we consider it to be a single dir */ + fileName = slashPos + 1; /* rest is filename */ + } + else + fileName = rawPath; /* filename only (or empty) */ + break; + + default: /* allow pretty much anything */ + case FTPFILE_MULTICWD: { + /* current position: begin of next path component */ + const char *curPos = rawPath; + + /* number of entries allocated for the 'dirs' array */ + size_t dirAlloc = 0; + const char *str = rawPath; + for(; *str != 0; ++str) + if(*str == '/') + ++dirAlloc; + + if(dirAlloc) { + ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) { + free(rawPath); + return CURLE_OUT_OF_MEMORY; + } + + /* parse the URL path into separate path components */ + while((slashPos = strchr(curPos, '/'))) { + size_t compLen = slashPos - curPos; + + /* path starts with a slash: add that as a directory */ + if((compLen == 0) && (ftpc->dirdepth == 0)) + ++compLen; + + /* we skip empty path components, like "x//y" since the FTP command + CWD requires a parameter and a non-existent parameter a) does not + work on many servers and b) has no effect on the others. */ + if(compLen > 0) { + char *comp = Curl_memdup0(curPos, compLen); + if(!comp) { + free(rawPath); + return CURLE_OUT_OF_MEMORY; + } + ftpc->dirs[ftpc->dirdepth++] = comp; + } + curPos = slashPos + 1; + } + } + DEBUGASSERT((size_t)ftpc->dirdepth <= dirAlloc); + fileName = curPos; /* the rest is the filename (or empty) */ + } + break; + } /* switch */ + + if(fileName && *fileName) + ftpc->file = strdup(fileName); + else + ftpc->file = NULL; /* instead of point to a zero byte, + we make it a NULL pointer */ + + if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { + /* We need a filename when uploading. Return error! */ + failf(data, "Uploading to a URL without a filename"); + free(rawPath); + return CURLE_URL_MALFORMAT; + } + + ftpc->cwddone = FALSE; /* default to not done */ + + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) + ftpc->cwddone = TRUE; /* skip CWD for absolute paths */ + else { /* newly created FTP connections are already in entry path */ + const char *oldPath = conn->bits.reuse ? ftpc->prevpath : ""; + if(oldPath) { + size_t n = pathLen; + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + n = 0; /* CWD to entry for relative paths */ + else + n -= ftpc->file?strlen(ftpc->file):0; + + if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) { + infof(data, "Request has same path as previous transfer"); + ftpc->cwddone = TRUE; + } + } + } + + free(rawPath); + return CURLE_OK; +} + +/* call this when the DO phase has completed */ +static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected) +{ + struct connectdata *conn = data->conn; + struct FTP *ftp = data->req.p.ftp; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + if(connected) { + int completed; + CURLcode result = ftp_do_more(data, &completed); + + if(result) { + close_secondarysocket(data); + return result; + } + } + + if(ftp->transfer != PPTRANSFER_BODY) + /* no data to transfer */ + Curl_xfer_setup_nop(data); + else if(!connected) + /* since we did not connect now, we want do_more to get called */ + conn->bits.do_more = TRUE; + + ftpc->ctl_valid = TRUE; /* seems good */ + + return CURLE_OK; +} + +/* called from multi.c while DOing */ +static CURLcode ftp_doing(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = ftp_multi_statemach(data, dophase_done); + + if(result) + CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_DSTATE(data)); + else if(*dophase_done) { + result = ftp_dophase_done(data, FALSE /* not connected */); + + CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_DSTATE(data)); + } + return result; +} + +/*********************************************************************** + * + * ftp_regular_transfer() + * + * The input argument is already checked for validity. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. + * + * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the + * ftp_done() function without finding any major problem. + */ +static +CURLcode ftp_regular_transfer(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + data->req.size = -1; /* make sure this is unknown at this point */ + + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); + + ftpc->ctl_valid = TRUE; /* starts good */ + + result = ftp_perform(data, + &connected, /* have we connected after PASV/PORT */ + dophase_done); /* all commands in the DO-phase done? */ + + if(!result) { + + if(!*dophase_done) + /* the DO phase has not completed yet */ + return CURLE_OK; + + result = ftp_dophase_done(data, connected); + + if(result) + return result; + } + else + freedirs(ftpc); + + return result; +} + +static CURLcode ftp_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + char *type; + struct FTP *ftp; + CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + ftp = calloc(1, sizeof(struct FTP)); + if(!ftp) + return CURLE_OUT_OF_MEMORY; + + /* clone connection related data that is FTP specific */ + if(data->set.str[STRING_FTP_ACCOUNT]) { + ftpc->account = strdup(data->set.str[STRING_FTP_ACCOUNT]); + if(!ftpc->account) { + free(ftp); + return CURLE_OUT_OF_MEMORY; + } + } + if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) { + ftpc->alternative_to_user = + strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); + if(!ftpc->alternative_to_user) { + Curl_safefree(ftpc->account); + free(ftp); + return CURLE_OUT_OF_MEMORY; + } + } + data->req.p.ftp = ftp; + + ftp->path = &data->state.up.path[1]; /* do not include the initial slash */ + + /* FTP URLs support an extension like ";type=" that + * we will try to get now! */ + type = strstr(ftp->path, ";type="); + + if(!type) + type = strstr(conn->host.rawalloc, ";type="); + + if(type) { + char command; + *type = 0; /* it was in the middle of the hostname */ + command = Curl_raw_toupper(type[6]); + + switch(command) { + case 'A': /* ASCII mode */ + data->state.prefer_ascii = TRUE; + break; + + case 'D': /* directory mode */ + data->state.list_only = TRUE; + break; + + case 'I': /* binary mode */ + default: + /* switch off ASCII */ + data->state.prefer_ascii = FALSE; + break; + } + } + + /* get some initial data into the ftp struct */ + ftp->transfer = PPTRANSFER_BODY; + ftp->downloadsize = 0; + ftpc->known_filesize = -1; /* unknown size for now */ + ftpc->use_ssl = data->set.use_ssl; + ftpc->ccc = data->set.ftp_ccc; + + CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(conn), result); + return result; +} + +#endif /* CURL_DISABLE_FTP */ diff --git a/third_party/curl/lib/ftp.h b/third_party/curl/lib/ftp.h new file mode 100644 index 0000000000..3d0af01587 --- /dev/null +++ b/third_party/curl/lib/ftp.h @@ -0,0 +1,167 @@ +#ifndef HEADER_CURL_FTP_H +#define HEADER_CURL_FTP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "pingpong.h" + +#ifndef CURL_DISABLE_FTP +extern const struct Curl_handler Curl_handler_ftp; + +#ifdef USE_SSL +extern const struct Curl_handler Curl_handler_ftps; +#endif + +CURLcode Curl_GetFTPResponse(struct Curl_easy *data, ssize_t *nread, + int *ftpcode); +#endif /* CURL_DISABLE_FTP */ + +/**************************************************************************** + * FTP unique setup + ***************************************************************************/ +enum { + FTP_STOP, /* do nothing state, stops the state machine */ + FTP_WAIT220, /* waiting for the initial 220 response immediately after + a connect */ + FTP_AUTH, + FTP_USER, + FTP_PASS, + FTP_ACCT, + FTP_PBSZ, + FTP_PROT, + FTP_CCC, + FTP_PWD, + FTP_SYST, + FTP_NAMEFMT, + FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ + FTP_RETR_PREQUOTE, + FTP_STOR_PREQUOTE, + FTP_POSTQUOTE, + FTP_CWD, /* change dir */ + FTP_MKD, /* if the dir did not exist */ + FTP_MDTM, /* to figure out the datestamp */ + FTP_TYPE, /* to set type when doing a head-like request */ + FTP_LIST_TYPE, /* set type when about to do a dir list */ + FTP_RETR_TYPE, /* set type when about to RETR a file */ + FTP_STOR_TYPE, /* set type when about to STOR a file */ + FTP_SIZE, /* get the remote file's size for head-like request */ + FTP_RETR_SIZE, /* get the remote file's size for RETR */ + FTP_STOR_SIZE, /* get the size for STOR */ + FTP_REST, /* when used to check if the server supports it in head-like */ + FTP_RETR_REST, /* when asking for "resume" in for RETR */ + FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */ + FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */ + FTP_PASV, /* generic state for PASV and EPSV, check count1 */ + FTP_LIST, /* generic state for LIST, NLST or a custom list command */ + FTP_RETR, + FTP_STOR, /* generic state for STOR and APPE */ + FTP_QUIT, + FTP_LAST /* never used */ +}; +typedef unsigned char ftpstate; /* use the enum values */ + +struct ftp_parselist_data; /* defined later in ftplistparser.c */ + +struct ftp_wc { + struct ftp_parselist_data *parser; + + struct { + curl_write_callback write_function; + FILE *file_descriptor; + } backup; +}; + +typedef enum { + FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */ + FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */ + FTPFILE_SINGLECWD = 3 /* make one CWD, then SIZE / RETR / STOR on the + file */ +} curl_ftpfile; + +/* This FTP struct is used in the Curl_easy. All FTP data that is + connection-oriented must be in FTP_conn to properly deal with the fact that + perhaps the Curl_easy is changed between the times the connection is + used. */ +struct FTP { + char *path; /* points to the urlpieces struct field */ + char *pathalloc; /* if non-NULL a pointer to an allocated path */ + + /* transfer a file/body or not, done as a typedefed enum just to make + debuggers display the full symbol and not just the numerical value */ + curl_pp_transfer transfer; + curl_off_t downloadsize; +}; + + +/* ftp_conn is used for struct connection-oriented data in the connectdata + struct */ +struct ftp_conn { + struct pingpong pp; + char *account; + char *alternative_to_user; + char *entrypath; /* the PWD reply when we logged on */ + char *file; /* url-decoded filename (or path) */ + char **dirs; /* realloc()ed array for path components */ + char *newhost; + char *prevpath; /* url-decoded conn->path from the previous transfer */ + char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a + and others (A/I or zero) */ + curl_off_t retr_size_saved; /* Size of retrieved file saved */ + char *server_os; /* The target server operating system. */ + curl_off_t known_filesize; /* file size is different from -1, if wildcard + LIST parsing was done and wc_statemach set + it */ + int dirdepth; /* number of entries used in the 'dirs' array */ + int cwdcount; /* number of CWD commands issued */ + int count1; /* general purpose counter for the state machine */ + int count2; /* general purpose counter for the state machine */ + int count3; /* general purpose counter for the state machine */ + /* newhost is the (allocated) IP addr or hostname to connect the data + connection to */ + unsigned short newport; + ftpstate state; /* always use ftp.c:state() to change state! */ + ftpstate state_saved; /* transfer type saved to be reloaded after data + connection is established */ + unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or + IMAP or POP3 or others! (type: curl_usessl)*/ + unsigned char ccc; /* ccc level for this connection */ + BIT(ftp_trying_alternative); + BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer) + file size and 226/250 status check. It should still + read the line, just ignore the result. */ + BIT(ctl_valid); /* Tells Curl_ftp_quit() whether or not to do anything. If + the connection has timed out or been closed, this + should be FALSE when it gets to Curl_ftp_quit() */ + BIT(cwddone); /* if it has been determined that the proper CWD combo + already has been done */ + BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent + caching the current directory */ + BIT(wait_data_conn); /* this is set TRUE if data connection is waited */ +}; + +#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */ + +#endif /* HEADER_CURL_FTP_H */ diff --git a/third_party/curl/lib/ftplistparser.c b/third_party/curl/lib/ftplistparser.c new file mode 100644 index 0000000000..448f3a43ab --- /dev/null +++ b/third_party/curl/lib/ftplistparser.c @@ -0,0 +1,1041 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/** + * Now implemented: + * + * 1) Unix version 1 + * drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog + * 2) Unix version 2 + * drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog + * 3) Unix version 3 + * drwxr-xr-x 1 1 1 512 Jan 29 23:32 prog + * 4) Unix symlink + * lrwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog -> prog2000 + * 5) DOS style + * 01-29-97 11:32PM prog + */ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_FTP + +#include + +#include "urldata.h" +#include "fileinfo.h" +#include "llist.h" +#include "strtoofft.h" +#include "ftp.h" +#include "ftplistparser.h" +#include "curl_fnmatch.h" +#include "curl_memory.h" +#include "multiif.h" +/* The last #include file should be: */ +#include "memdebug.h" + +typedef enum { + PL_UNIX_TOTALSIZE = 0, + PL_UNIX_FILETYPE, + PL_UNIX_PERMISSION, + PL_UNIX_HLINKS, + PL_UNIX_USER, + PL_UNIX_GROUP, + PL_UNIX_SIZE, + PL_UNIX_TIME, + PL_UNIX_FILENAME, + PL_UNIX_SYMLINK +} pl_unix_mainstate; + +typedef union { + enum { + PL_UNIX_TOTALSIZE_INIT = 0, + PL_UNIX_TOTALSIZE_READING + } total_dirsize; + + enum { + PL_UNIX_HLINKS_PRESPACE = 0, + PL_UNIX_HLINKS_NUMBER + } hlinks; + + enum { + PL_UNIX_USER_PRESPACE = 0, + PL_UNIX_USER_PARSING + } user; + + enum { + PL_UNIX_GROUP_PRESPACE = 0, + PL_UNIX_GROUP_NAME + } group; + + enum { + PL_UNIX_SIZE_PRESPACE = 0, + PL_UNIX_SIZE_NUMBER + } size; + + enum { + PL_UNIX_TIME_PREPART1 = 0, + PL_UNIX_TIME_PART1, + PL_UNIX_TIME_PREPART2, + PL_UNIX_TIME_PART2, + PL_UNIX_TIME_PREPART3, + PL_UNIX_TIME_PART3 + } time; + + enum { + PL_UNIX_FILENAME_PRESPACE = 0, + PL_UNIX_FILENAME_NAME, + PL_UNIX_FILENAME_WINDOWSEOL + } filename; + + enum { + PL_UNIX_SYMLINK_PRESPACE = 0, + PL_UNIX_SYMLINK_NAME, + PL_UNIX_SYMLINK_PRETARGET1, + PL_UNIX_SYMLINK_PRETARGET2, + PL_UNIX_SYMLINK_PRETARGET3, + PL_UNIX_SYMLINK_PRETARGET4, + PL_UNIX_SYMLINK_TARGET, + PL_UNIX_SYMLINK_WINDOWSEOL + } symlink; +} pl_unix_substate; + +typedef enum { + PL_WINNT_DATE = 0, + PL_WINNT_TIME, + PL_WINNT_DIRORSIZE, + PL_WINNT_FILENAME +} pl_winNT_mainstate; + +typedef union { + enum { + PL_WINNT_TIME_PRESPACE = 0, + PL_WINNT_TIME_TIME + } time; + enum { + PL_WINNT_DIRORSIZE_PRESPACE = 0, + PL_WINNT_DIRORSIZE_CONTENT + } dirorsize; + enum { + PL_WINNT_FILENAME_PRESPACE = 0, + PL_WINNT_FILENAME_CONTENT, + PL_WINNT_FILENAME_WINEOL + } filename; +} pl_winNT_substate; + +/* This struct is used in wildcard downloading - for parsing LIST response */ +struct ftp_parselist_data { + enum { + OS_TYPE_UNKNOWN = 0, + OS_TYPE_UNIX, + OS_TYPE_WIN_NT + } os_type; + + union { + struct { + pl_unix_mainstate main; + pl_unix_substate sub; + } UNIX; + + struct { + pl_winNT_mainstate main; + pl_winNT_substate sub; + } NT; + } state; + + CURLcode error; + struct fileinfo *file_data; + unsigned int item_length; + size_t item_offset; + struct { + size_t filename; + size_t user; + size_t group; + size_t time; + size_t perm; + size_t symlink_target; + } offsets; +}; + +static void fileinfo_dtor(void *user, void *element) +{ + (void)user; + Curl_fileinfo_cleanup(element); +} + +CURLcode Curl_wildcard_init(struct WildcardData *wc) +{ + Curl_llist_init(&wc->filelist, fileinfo_dtor); + wc->state = CURLWC_INIT; + + return CURLE_OK; +} + +void Curl_wildcard_dtor(struct WildcardData **wcp) +{ + struct WildcardData *wc = *wcp; + if(!wc) + return; + + if(wc->dtor) { + wc->dtor(wc->ftpwc); + wc->dtor = ZERO_NULL; + wc->ftpwc = NULL; + } + DEBUGASSERT(wc->ftpwc == NULL); + + Curl_llist_destroy(&wc->filelist, NULL); + free(wc->path); + wc->path = NULL; + free(wc->pattern); + wc->pattern = NULL; + wc->state = CURLWC_INIT; + free(wc); + *wcp = NULL; +} + +struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void) +{ + return calloc(1, sizeof(struct ftp_parselist_data)); +} + + +void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp) +{ + struct ftp_parselist_data *parser = *parserp; + if(parser) + Curl_fileinfo_cleanup(parser->file_data); + free(parser); + *parserp = NULL; +} + + +CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data) +{ + return pl_data->error; +} + + +#define FTP_LP_MALFORMATED_PERM 0x01000000 + +static unsigned int ftp_pl_get_permission(const char *str) +{ + unsigned int permissions = 0; + /* USER */ + if(str[0] == 'r') + permissions |= 1 << 8; + else if(str[0] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + if(str[1] == 'w') + permissions |= 1 << 7; + else if(str[1] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + + if(str[2] == 'x') + permissions |= 1 << 6; + else if(str[2] == 's') { + permissions |= 1 << 6; + permissions |= 1 << 11; + } + else if(str[2] == 'S') + permissions |= 1 << 11; + else if(str[2] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + /* GROUP */ + if(str[3] == 'r') + permissions |= 1 << 5; + else if(str[3] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + if(str[4] == 'w') + permissions |= 1 << 4; + else if(str[4] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + if(str[5] == 'x') + permissions |= 1 << 3; + else if(str[5] == 's') { + permissions |= 1 << 3; + permissions |= 1 << 10; + } + else if(str[5] == 'S') + permissions |= 1 << 10; + else if(str[5] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + /* others */ + if(str[6] == 'r') + permissions |= 1 << 2; + else if(str[6] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + if(str[7] == 'w') + permissions |= 1 << 1; + else if(str[7] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + if(str[8] == 'x') + permissions |= 1; + else if(str[8] == 't') { + permissions |= 1; + permissions |= 1 << 9; + } + else if(str[8] == 'T') + permissions |= 1 << 9; + else if(str[8] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + + return permissions; +} + +static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, + struct fileinfo *infop) +{ + curl_fnmatch_callback compare; + struct WildcardData *wc = data->wildcard; + struct ftp_wc *ftpwc = wc->ftpwc; + struct Curl_llist *llist = &wc->filelist; + struct ftp_parselist_data *parser = ftpwc->parser; + bool add = TRUE; + struct curl_fileinfo *finfo = &infop->info; + + /* set the finfo pointers */ + char *str = Curl_dyn_ptr(&infop->buf); + finfo->filename = str + parser->offsets.filename; + finfo->strings.group = parser->offsets.group ? + str + parser->offsets.group : NULL; + finfo->strings.perm = parser->offsets.perm ? + str + parser->offsets.perm : NULL; + finfo->strings.target = parser->offsets.symlink_target ? + str + parser->offsets.symlink_target : NULL; + finfo->strings.time = str + parser->offsets.time; + finfo->strings.user = parser->offsets.user ? + str + parser->offsets.user : NULL; + + /* get correct fnmatch callback */ + compare = data->set.fnmatch; + if(!compare) + compare = Curl_fnmatch; + + /* filter pattern-corresponding filenames */ + Curl_set_in_callback(data, true); + if(compare(data->set.fnmatch_data, wc->pattern, + finfo->filename) == 0) { + /* discard symlink which is containing multiple " -> " */ + if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && + (strstr(finfo->strings.target, " -> "))) { + add = FALSE; + } + } + else { + add = FALSE; + } + Curl_set_in_callback(data, false); + + if(add) { + Curl_llist_append(llist, finfo, &infop->list); + } + else { + Curl_fileinfo_cleanup(infop); + } + + ftpwc->parser->file_data = NULL; + return CURLE_OK; +} + +#define MAX_FTPLIST_BUFFER 10000 /* arbitrarily set */ + +size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, + void *connptr) +{ + size_t bufflen = size*nmemb; + struct Curl_easy *data = (struct Curl_easy *)connptr; + struct ftp_wc *ftpwc = data->wildcard->ftpwc; + struct ftp_parselist_data *parser = ftpwc->parser; + size_t i = 0; + CURLcode result; + size_t retsize = bufflen; + + if(parser->error) { /* error in previous call */ + /* scenario: + * 1. call => OK.. + * 2. call => OUT_OF_MEMORY (or other error) + * 3. (last) call => is skipped RIGHT HERE and the error is handled later + * in wc_statemach() + */ + goto fail; + } + + if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) { + /* considering info about FILE response format */ + parser->os_type = ISDIGIT(buffer[0]) ? OS_TYPE_WIN_NT : OS_TYPE_UNIX; + } + + while(i < bufflen) { /* FSM */ + char *mem; + size_t len; /* number of bytes of data in the dynbuf */ + char c = buffer[i]; + struct fileinfo *infop; + struct curl_fileinfo *finfo; + if(!parser->file_data) { /* tmp file data is not allocated yet */ + parser->file_data = Curl_fileinfo_alloc(); + if(!parser->file_data) { + parser->error = CURLE_OUT_OF_MEMORY; + goto fail; + } + parser->item_offset = 0; + parser->item_length = 0; + Curl_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER); + } + + infop = parser->file_data; + finfo = &infop->info; + + if(Curl_dyn_addn(&infop->buf, &c, 1)) { + parser->error = CURLE_OUT_OF_MEMORY; + goto fail; + } + len = Curl_dyn_len(&infop->buf); + mem = Curl_dyn_ptr(&infop->buf); + + switch(parser->os_type) { + case OS_TYPE_UNIX: + switch(parser->state.UNIX.main) { + case PL_UNIX_TOTALSIZE: + switch(parser->state.UNIX.sub.total_dirsize) { + case PL_UNIX_TOTALSIZE_INIT: + if(c == 't') { + parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING; + parser->item_length++; + } + else { + parser->state.UNIX.main = PL_UNIX_FILETYPE; + /* start FSM again not considering size of directory */ + Curl_dyn_reset(&infop->buf); + continue; + } + break; + case PL_UNIX_TOTALSIZE_READING: + parser->item_length++; + if(c == '\r') { + parser->item_length--; + Curl_dyn_setlen(&infop->buf, --len); + } + else if(c == '\n') { + mem[parser->item_length - 1] = 0; + if(!strncmp("total ", mem, 6)) { + char *endptr = mem + 6; + /* here we can deal with directory size, pass the leading + whitespace and then the digits */ + while(ISBLANK(*endptr)) + endptr++; + while(ISDIGIT(*endptr)) + endptr++; + if(*endptr) { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + parser->state.UNIX.main = PL_UNIX_FILETYPE; + Curl_dyn_reset(&infop->buf); + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + } + break; + } + break; + case PL_UNIX_FILETYPE: + switch(c) { + case '-': + finfo->filetype = CURLFILETYPE_FILE; + break; + case 'd': + finfo->filetype = CURLFILETYPE_DIRECTORY; + break; + case 'l': + finfo->filetype = CURLFILETYPE_SYMLINK; + break; + case 'p': + finfo->filetype = CURLFILETYPE_NAMEDPIPE; + break; + case 's': + finfo->filetype = CURLFILETYPE_SOCKET; + break; + case 'c': + finfo->filetype = CURLFILETYPE_DEVICE_CHAR; + break; + case 'b': + finfo->filetype = CURLFILETYPE_DEVICE_BLOCK; + break; + case 'D': + finfo->filetype = CURLFILETYPE_DOOR; + break; + default: + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + parser->state.UNIX.main = PL_UNIX_PERMISSION; + parser->item_length = 0; + parser->item_offset = 1; + break; + case PL_UNIX_PERMISSION: + parser->item_length++; + if(parser->item_length <= 9) { + if(!strchr("rwx-tTsS", c)) { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + } + else if(parser->item_length == 10) { + unsigned int perm; + if(c != ' ') { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + mem[10] = 0; /* terminate permissions */ + perm = ftp_pl_get_permission(mem + parser->item_offset); + if(perm & FTP_LP_MALFORMATED_PERM) { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM; + parser->file_data->info.perm = perm; + parser->offsets.perm = parser->item_offset; + + parser->item_length = 0; + parser->state.UNIX.main = PL_UNIX_HLINKS; + parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE; + } + break; + case PL_UNIX_HLINKS: + switch(parser->state.UNIX.sub.hlinks) { + case PL_UNIX_HLINKS_PRESPACE: + if(c != ' ') { + if(ISDIGIT(c)) { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER; + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + } + break; + case PL_UNIX_HLINKS_NUMBER: + parser->item_length ++; + if(c == ' ') { + char *p; + long int hlinks; + mem[parser->item_offset + parser->item_length - 1] = 0; + hlinks = strtol(mem + parser->item_offset, &p, 10); + if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) { + parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT; + parser->file_data->info.hardlinks = hlinks; + } + parser->item_length = 0; + parser->item_offset = 0; + parser->state.UNIX.main = PL_UNIX_USER; + parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE; + } + else if(!ISDIGIT(c)) { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + } + break; + case PL_UNIX_USER: + switch(parser->state.UNIX.sub.user) { + case PL_UNIX_USER_PRESPACE: + if(c != ' ') { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING; + } + break; + case PL_UNIX_USER_PARSING: + parser->item_length++; + if(c == ' ') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.user = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_GROUP; + parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE; + parser->item_offset = 0; + parser->item_length = 0; + } + break; + } + break; + case PL_UNIX_GROUP: + switch(parser->state.UNIX.sub.group) { + case PL_UNIX_GROUP_PRESPACE: + if(c != ' ') { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME; + } + break; + case PL_UNIX_GROUP_NAME: + parser->item_length++; + if(c == ' ') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.group = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_SIZE; + parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE; + parser->item_offset = 0; + parser->item_length = 0; + } + break; + } + break; + case PL_UNIX_SIZE: + switch(parser->state.UNIX.sub.size) { + case PL_UNIX_SIZE_PRESPACE: + if(c != ' ') { + if(ISDIGIT(c)) { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER; + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + } + break; + case PL_UNIX_SIZE_NUMBER: + parser->item_length++; + if(c == ' ') { + char *p; + curl_off_t fsize; + mem[parser->item_offset + parser->item_length - 1] = 0; + if(!curlx_strtoofft(mem + parser->item_offset, + &p, 10, &fsize)) { + if(p[0] == '\0' && fsize != CURL_OFF_T_MAX && + fsize != CURL_OFF_T_MIN) { + parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE; + parser->file_data->info.size = fsize; + } + parser->item_length = 0; + parser->item_offset = 0; + parser->state.UNIX.main = PL_UNIX_TIME; + parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1; + } + } + else if(!ISDIGIT(c)) { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + } + break; + case PL_UNIX_TIME: + switch(parser->state.UNIX.sub.time) { + case PL_UNIX_TIME_PREPART1: + if(c != ' ') { + if(ISALNUM(c)) { + parser->item_offset = len -1; + parser->item_length = 1; + parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + } + break; + case PL_UNIX_TIME_PART1: + parser->item_length++; + if(c == ' ') { + parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2; + } + else if(!ISALNUM(c) && c != '.') { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + case PL_UNIX_TIME_PREPART2: + parser->item_length++; + if(c != ' ') { + if(ISALNUM(c)) { + parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2; + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + } + break; + case PL_UNIX_TIME_PART2: + parser->item_length++; + if(c == ' ') { + parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3; + } + else if(!ISALNUM(c) && c != '.') { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + case PL_UNIX_TIME_PREPART3: + parser->item_length++; + if(c != ' ') { + if(ISALNUM(c)) { + parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3; + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + } + break; + case PL_UNIX_TIME_PART3: + parser->item_length++; + if(c == ' ') { + mem[parser->item_offset + parser->item_length -1] = 0; + parser->offsets.time = parser->item_offset; + /* + if(ftp_pl_gettime(parser, finfo->mem + parser->item_offset)) { + parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME; + } + */ + if(finfo->filetype == CURLFILETYPE_SYMLINK) { + parser->state.UNIX.main = PL_UNIX_SYMLINK; + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE; + } + else { + parser->state.UNIX.main = PL_UNIX_FILENAME; + parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE; + } + } + else if(!ISALNUM(c) && c != '.' && c != ':') { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + } + break; + case PL_UNIX_FILENAME: + switch(parser->state.UNIX.sub.filename) { + case PL_UNIX_FILENAME_PRESPACE: + if(c != ' ') { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME; + } + break; + case PL_UNIX_FILENAME_NAME: + parser->item_length++; + if(c == '\r') { + parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL; + } + else if(c == '\n') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.filename = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_FILETYPE; + result = ftp_pl_insert_finfo(data, infop); + if(result) { + parser->error = result; + goto fail; + } + } + break; + case PL_UNIX_FILENAME_WINDOWSEOL: + if(c == '\n') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.filename = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_FILETYPE; + result = ftp_pl_insert_finfo(data, infop); + if(result) { + parser->error = result; + goto fail; + } + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + } + break; + case PL_UNIX_SYMLINK: + switch(parser->state.UNIX.sub.symlink) { + case PL_UNIX_SYMLINK_PRESPACE: + if(c != ' ') { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_NAME: + parser->item_length++; + if(c == ' ') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1; + } + else if(c == '\r' || c == '\n') { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + case PL_UNIX_SYMLINK_PRETARGET1: + parser->item_length++; + if(c == '-') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2; + } + else if(c == '\r' || c == '\n') { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + else { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_PRETARGET2: + parser->item_length++; + if(c == '>') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3; + } + else if(c == '\r' || c == '\n') { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + else { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_PRETARGET3: + parser->item_length++; + if(c == ' ') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4; + /* now place where is symlink following */ + mem[parser->item_offset + parser->item_length - 4] = 0; + parser->offsets.filename = parser->item_offset; + parser->item_length = 0; + parser->item_offset = 0; + } + else if(c == '\r' || c == '\n') { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + else { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_PRETARGET4: + if(c != '\r' && c != '\n') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET; + parser->item_offset = len - 1; + parser->item_length = 1; + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + case PL_UNIX_SYMLINK_TARGET: + parser->item_length++; + if(c == '\r') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL; + } + else if(c == '\n') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.symlink_target = parser->item_offset; + result = ftp_pl_insert_finfo(data, infop); + if(result) { + parser->error = result; + goto fail; + } + parser->state.UNIX.main = PL_UNIX_FILETYPE; + } + break; + case PL_UNIX_SYMLINK_WINDOWSEOL: + if(c == '\n') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.symlink_target = parser->item_offset; + result = ftp_pl_insert_finfo(data, infop); + if(result) { + parser->error = result; + goto fail; + } + parser->state.UNIX.main = PL_UNIX_FILETYPE; + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + } + break; + } + break; + case OS_TYPE_WIN_NT: + switch(parser->state.NT.main) { + case PL_WINNT_DATE: + parser->item_length++; + if(parser->item_length < 9) { + if(!strchr("0123456789-", c)) { /* only simple control */ + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + } + else if(parser->item_length == 9) { + if(c == ' ') { + parser->state.NT.main = PL_WINNT_TIME; + parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE; + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + case PL_WINNT_TIME: + parser->item_length++; + switch(parser->state.NT.sub.time) { + case PL_WINNT_TIME_PRESPACE: + if(!ISBLANK(c)) { + parser->state.NT.sub.time = PL_WINNT_TIME_TIME; + } + break; + case PL_WINNT_TIME_TIME: + if(c == ' ') { + parser->offsets.time = parser->item_offset; + mem[parser->item_offset + parser->item_length -1] = 0; + parser->state.NT.main = PL_WINNT_DIRORSIZE; + parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE; + parser->item_length = 0; + } + else if(!strchr("APM0123456789:", c)) { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + } + break; + case PL_WINNT_DIRORSIZE: + switch(parser->state.NT.sub.dirorsize) { + case PL_WINNT_DIRORSIZE_PRESPACE: + if(c != ' ') { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT; + } + break; + case PL_WINNT_DIRORSIZE_CONTENT: + parser->item_length ++; + if(c == ' ') { + mem[parser->item_offset + parser->item_length - 1] = 0; + if(strcmp("", mem + parser->item_offset) == 0) { + finfo->filetype = CURLFILETYPE_DIRECTORY; + finfo->size = 0; + } + else { + char *endptr; + if(curlx_strtoofft(mem + + parser->item_offset, + &endptr, 10, &finfo->size)) { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + /* correct file type */ + parser->file_data->info.filetype = CURLFILETYPE_FILE; + } + + parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE; + parser->item_length = 0; + parser->state.NT.main = PL_WINNT_FILENAME; + parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; + } + break; + } + break; + case PL_WINNT_FILENAME: + switch(parser->state.NT.sub.filename) { + case PL_WINNT_FILENAME_PRESPACE: + if(c != ' ') { + parser->item_offset = len -1; + parser->item_length = 1; + parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT; + } + break; + case PL_WINNT_FILENAME_CONTENT: + parser->item_length++; + if(c == '\r') { + parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL; + mem[len - 1] = 0; + } + else if(c == '\n') { + parser->offsets.filename = parser->item_offset; + mem[len - 1] = 0; + result = ftp_pl_insert_finfo(data, infop); + if(result) { + parser->error = result; + goto fail; + } + parser->state.NT.main = PL_WINNT_DATE; + parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; + } + break; + case PL_WINNT_FILENAME_WINEOL: + if(c == '\n') { + parser->offsets.filename = parser->item_offset; + result = ftp_pl_insert_finfo(data, infop); + if(result) { + parser->error = result; + goto fail; + } + parser->state.NT.main = PL_WINNT_DATE; + parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; + } + else { + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; + } + break; + } + break; + } + break; + default: + retsize = bufflen + 1; + goto fail; + } + + i++; + } + return retsize; + +fail: + + /* Clean up any allocated memory. */ + if(parser->file_data) { + Curl_fileinfo_cleanup(parser->file_data); + parser->file_data = NULL; + } + + return retsize; +} + +#endif /* CURL_DISABLE_FTP */ diff --git a/third_party/curl/lib/ftplistparser.h b/third_party/curl/lib/ftplistparser.h new file mode 100644 index 0000000000..5ba1f6a97d --- /dev/null +++ b/third_party/curl/lib/ftplistparser.h @@ -0,0 +1,77 @@ +#ifndef HEADER_CURL_FTPLISTPARSER_H +#define HEADER_CURL_FTPLISTPARSER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifndef CURL_DISABLE_FTP + +/* WRITEFUNCTION callback for parsing LIST responses */ +size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, + void *connptr); + +struct ftp_parselist_data; /* defined inside ftplibparser.c */ + +CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data); + +struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void); + +void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data); + +/* list of wildcard process states */ +typedef enum { + CURLWC_CLEAR = 0, + CURLWC_INIT = 1, + CURLWC_MATCHING, /* library is trying to get list of addresses for + downloading */ + CURLWC_DOWNLOADING, + CURLWC_CLEAN, /* deallocate resources and reset settings */ + CURLWC_SKIP, /* skip over concrete file */ + CURLWC_ERROR, /* error cases */ + CURLWC_DONE /* if is wildcard->state == CURLWC_DONE wildcard loop + will end */ +} wildcard_states; + +typedef void (*wildcard_dtor)(void *ptr); + +/* struct keeping information about wildcard download process */ +struct WildcardData { + char *path; /* path to the directory, where we trying wildcard-match */ + char *pattern; /* wildcard pattern */ + struct Curl_llist filelist; /* llist with struct Curl_fileinfo */ + struct ftp_wc *ftpwc; /* pointer to FTP wildcard data */ + wildcard_dtor dtor; + unsigned char state; /* wildcard_states */ +}; + +CURLcode Curl_wildcard_init(struct WildcardData *wc); +void Curl_wildcard_dtor(struct WildcardData **wcp); + +struct Curl_easy; + +#else +/* FTP is disabled */ +#define Curl_wildcard_dtor(x) +#endif /* CURL_DISABLE_FTP */ +#endif /* HEADER_CURL_FTPLISTPARSER_H */ diff --git a/third_party/curl/lib/functypes.h b/third_party/curl/lib/functypes.h new file mode 100644 index 0000000000..ea66d3281d --- /dev/null +++ b/third_party/curl/lib/functypes.h @@ -0,0 +1,115 @@ +#ifndef HEADER_CURL_FUNCTYPES_H +#define HEADER_CURL_FUNCTYPES_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +/* defaults: + + ssize_t recv(int, void *, size_t, int); + ssize_t send(int, const void *, size_t, int); + + If other argument or return types are needed: + + 1. For systems that run configure or cmake, the alternatives are provided + here. + 2. For systems with config-*.h files, define them there. +*/ + +#ifdef _WIN32 +/* int recv(SOCKET, char *, int, int) */ +#define RECV_TYPE_ARG1 SOCKET +#define RECV_TYPE_ARG2 char * +#define RECV_TYPE_ARG3 int +#define RECV_TYPE_RETV int + +/* int send(SOCKET, const char *, int, int); */ +#define SEND_TYPE_ARG1 SOCKET +#define SEND_TYPE_ARG2 char * +#define SEND_TYPE_ARG3 int +#define SEND_TYPE_RETV int + +#elif defined(__AMIGA__) /* Any AmigaOS flavour */ + +/* long recv(long, char *, long, long); */ +#define RECV_TYPE_ARG1 long +#define RECV_TYPE_ARG2 char * +#define RECV_TYPE_ARG3 long +#define RECV_TYPE_ARG4 long +#define RECV_TYPE_RETV long + +/* int send(int, const char *, int, int); */ +#define SEND_TYPE_ARG1 int +#define SEND_TYPE_ARG2 char * +#define SEND_TYPE_ARG3 int +#define SEND_TYPE_RETV int +#endif + + +#ifndef RECV_TYPE_ARG1 +#define RECV_TYPE_ARG1 int +#endif + +#ifndef RECV_TYPE_ARG2 +#define RECV_TYPE_ARG2 void * +#endif + +#ifndef RECV_TYPE_ARG3 +#define RECV_TYPE_ARG3 size_t +#endif + +#ifndef RECV_TYPE_ARG4 +#define RECV_TYPE_ARG4 int +#endif + +#ifndef RECV_TYPE_RETV +#define RECV_TYPE_RETV ssize_t +#endif + +#ifndef SEND_QUAL_ARG2 +#define SEND_QUAL_ARG2 const +#endif + +#ifndef SEND_TYPE_ARG1 +#define SEND_TYPE_ARG1 int +#endif + +#ifndef SEND_TYPE_ARG2 +#define SEND_TYPE_ARG2 void * +#endif + +#ifndef SEND_TYPE_ARG3 +#define SEND_TYPE_ARG3 size_t +#endif + +#ifndef SEND_TYPE_ARG4 +#define SEND_TYPE_ARG4 int +#endif + +#ifndef SEND_TYPE_RETV +#define SEND_TYPE_RETV ssize_t +#endif + +#endif /* HEADER_CURL_FUNCTYPES_H */ diff --git a/third_party/curl/lib/getenv.c b/third_party/curl/lib/getenv.c new file mode 100644 index 0000000000..49a2e50fa3 --- /dev/null +++ b/third_party/curl/lib/getenv.c @@ -0,0 +1,80 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include +#include "curl_memory.h" + +#include "memdebug.h" + +static char *GetEnv(const char *variable) +{ +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) || \ + defined(__ORBIS__) || defined(__PROSPERO__) /* PlayStation 4 and 5 */ + (void)variable; + return NULL; +#elif defined(_WIN32) + /* This uses Windows API instead of C runtime getenv() to get the environment + variable since some changes are not always visible to the latter. #4774 */ + char *buf = NULL; + char *tmp; + DWORD bufsize; + DWORD rc = 1; + const DWORD max = 32768; /* max env var size from MSCRT source */ + + for(;;) { + tmp = realloc(buf, rc); + if(!tmp) { + free(buf); + return NULL; + } + + buf = tmp; + bufsize = rc; + + /* it is possible for rc to be 0 if the variable was found but empty. + Since getenv does not make that distinction we ignore it as well. */ + rc = GetEnvironmentVariableA(variable, buf, bufsize); + if(!rc || rc == bufsize || rc > max) { + free(buf); + return NULL; + } + + /* if rc < bufsize then rc is bytes written not including null */ + if(rc < bufsize) + return buf; + + /* else rc is bytes needed, try again */ + } +#else + char *env = getenv(variable); + return (env && env[0])?strdup(env):NULL; +#endif +} + +char *curl_getenv(const char *v) +{ + return GetEnv(v); +} diff --git a/third_party/curl/lib/getinfo.c b/third_party/curl/lib/getinfo.c new file mode 100644 index 0000000000..9ca02889c6 --- /dev/null +++ b/third_party/curl/lib/getinfo.c @@ -0,0 +1,640 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "urldata.h" +#include "getinfo.h" + +#include "vtls/vtls.h" +#include "connect.h" /* Curl_getconnectinfo() */ +#include "progress.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Initialize statistical and informational data. + * + * This function is called in curl_easy_reset, curl_easy_duphandle and at the + * beginning of a perform session. It must reset the session-info variables, + * in particular all variables in struct PureInfo. + */ +CURLcode Curl_initinfo(struct Curl_easy *data) +{ + struct Progress *pro = &data->progress; + struct PureInfo *info = &data->info; + + pro->t_nslookup = 0; + pro->t_connect = 0; + pro->t_appconnect = 0; + pro->t_pretransfer = 0; + pro->t_starttransfer = 0; + pro->timespent = 0; + pro->t_redirect = 0; + pro->is_t_startransfer_set = false; + + info->httpcode = 0; + info->httpproxycode = 0; + info->httpversion = 0; + info->filetime = -1; /* -1 is an illegal time and thus means unknown */ + info->timecond = FALSE; + + info->header_size = 0; + info->request_size = 0; + info->proxyauthavail = 0; + info->httpauthavail = 0; + info->numconnects = 0; + + free(info->contenttype); + info->contenttype = NULL; + + free(info->wouldredirect); + info->wouldredirect = NULL; + + info->primary.remote_ip[0] = '\0'; + info->primary.local_ip[0] = '\0'; + info->primary.remote_port = 0; + info->primary.local_port = 0; + info->retry_after = 0; + + info->conn_scheme = 0; + info->conn_protocol = 0; + +#ifdef USE_SSL + Curl_ssl_free_certinfo(data); +#endif + return CURLE_OK; +} + +static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info, + const char **param_charp) +{ + switch(info) { + case CURLINFO_EFFECTIVE_URL: + *param_charp = data->state.url?data->state.url:(char *)""; + break; + case CURLINFO_EFFECTIVE_METHOD: { + const char *m = data->set.str[STRING_CUSTOMREQUEST]; + if(!m) { + if(data->set.opt_no_body) + m = "HEAD"; +#ifndef CURL_DISABLE_HTTP + else { + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + m = "POST"; + break; + case HTTPREQ_PUT: + m = "PUT"; + break; + default: /* this should never happen */ + case HTTPREQ_GET: + m = "GET"; + break; + case HTTPREQ_HEAD: + m = "HEAD"; + break; + } + } +#endif + } + *param_charp = m; + } + break; + case CURLINFO_CONTENT_TYPE: + *param_charp = data->info.contenttype; + break; + case CURLINFO_PRIVATE: + *param_charp = (char *) data->set.private_data; + break; + case CURLINFO_FTP_ENTRY_PATH: + /* Return the entrypath string from the most recent connection. + This pointer was copied from the connectdata structure by FTP. + The actual string may be free()ed by subsequent libcurl calls so + it must be copied to a safer area before the next libcurl call. + Callers must never free it themselves. */ + *param_charp = data->state.most_recent_ftp_entrypath; + break; + case CURLINFO_REDIRECT_URL: + /* Return the URL this request would have been redirected to if that + option had been enabled! */ + *param_charp = data->info.wouldredirect; + break; + case CURLINFO_REFERER: + /* Return the referrer header for this request, or NULL if unset */ + *param_charp = data->state.referer; + break; + case CURLINFO_PRIMARY_IP: + /* Return the ip address of the most recent (primary) connection */ + *param_charp = data->info.primary.remote_ip; + break; + case CURLINFO_LOCAL_IP: + /* Return the source/local ip address of the most recent (primary) + connection */ + *param_charp = data->info.primary.local_ip; + break; + case CURLINFO_RTSP_SESSION_ID: +#ifndef CURL_DISABLE_RTSP + *param_charp = data->set.str[STRING_RTSP_SESSION_ID]; +#else + *param_charp = NULL; +#endif + break; + case CURLINFO_SCHEME: + *param_charp = data->info.conn_scheme; + break; + case CURLINFO_CAPATH: +#ifdef CURL_CA_PATH + *param_charp = CURL_CA_PATH; +#else + *param_charp = NULL; +#endif + break; + case CURLINFO_CAINFO: +#ifdef CURL_CA_BUNDLE + *param_charp = CURL_CA_BUNDLE; +#else + *param_charp = NULL; +#endif + break; + default: + return CURLE_UNKNOWN_OPTION; + } + + return CURLE_OK; +} + +static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, + long *param_longp) +{ + curl_socket_t sockfd; + + union { + unsigned long *to_ulong; + long *to_long; + } lptr; + +#ifdef DEBUGBUILD + char *timestr = getenv("CURL_TIME"); + if(timestr) { + unsigned long val = strtoul(timestr, NULL, 10); + switch(info) { + case CURLINFO_LOCAL_PORT: + *param_longp = (long)val; + return CURLE_OK; + default: + break; + } + } + /* use another variable for this to allow different values */ + timestr = getenv("CURL_DEBUG_SIZE"); + if(timestr) { + unsigned long val = strtoul(timestr, NULL, 10); + switch(info) { + case CURLINFO_HEADER_SIZE: + case CURLINFO_REQUEST_SIZE: + *param_longp = (long)val; + return CURLE_OK; + default: + break; + } + } +#endif + + switch(info) { + case CURLINFO_RESPONSE_CODE: + *param_longp = data->info.httpcode; + break; + case CURLINFO_HTTP_CONNECTCODE: + *param_longp = data->info.httpproxycode; + break; + case CURLINFO_FILETIME: + if(data->info.filetime > LONG_MAX) + *param_longp = LONG_MAX; + else if(data->info.filetime < LONG_MIN) + *param_longp = LONG_MIN; + else + *param_longp = (long)data->info.filetime; + break; + case CURLINFO_HEADER_SIZE: + *param_longp = (long)data->info.header_size; + break; + case CURLINFO_REQUEST_SIZE: + *param_longp = (long)data->info.request_size; + break; + case CURLINFO_SSL_VERIFYRESULT: + *param_longp = data->set.ssl.certverifyresult; + break; +#ifndef CURL_DISABLE_PROXY + case CURLINFO_PROXY_SSL_VERIFYRESULT: + *param_longp = data->set.proxy_ssl.certverifyresult; + break; +#endif + case CURLINFO_REDIRECT_COUNT: + *param_longp = data->state.followlocation; + break; + case CURLINFO_HTTPAUTH_AVAIL: + lptr.to_long = param_longp; + *lptr.to_ulong = data->info.httpauthavail; + break; + case CURLINFO_PROXYAUTH_AVAIL: + lptr.to_long = param_longp; + *lptr.to_ulong = data->info.proxyauthavail; + break; + case CURLINFO_OS_ERRNO: + *param_longp = data->state.os_errno; + break; + case CURLINFO_NUM_CONNECTS: + *param_longp = data->info.numconnects; + break; + case CURLINFO_LASTSOCKET: + sockfd = Curl_getconnectinfo(data, NULL); + + /* note: this is not a good conversion for systems with 64-bit sockets and + 32-bit longs */ + if(sockfd != CURL_SOCKET_BAD) + *param_longp = (long)sockfd; + else + /* this interface is documented to return -1 in case of badness, which + may not be the same as the CURL_SOCKET_BAD value */ + *param_longp = -1; + break; + case CURLINFO_PRIMARY_PORT: + /* Return the (remote) port of the most recent (primary) connection */ + *param_longp = data->info.primary.remote_port; + break; + case CURLINFO_LOCAL_PORT: + /* Return the local port of the most recent (primary) connection */ + *param_longp = data->info.primary.local_port; + break; + case CURLINFO_PROXY_ERROR: + *param_longp = (long)data->info.pxcode; + break; + case CURLINFO_CONDITION_UNMET: + if(data->info.httpcode == 304) + *param_longp = 1L; + else + /* return if the condition prevented the document to get transferred */ + *param_longp = data->info.timecond ? 1L : 0L; + break; +#ifndef CURL_DISABLE_RTSP + case CURLINFO_RTSP_CLIENT_CSEQ: + *param_longp = data->state.rtsp_next_client_CSeq; + break; + case CURLINFO_RTSP_SERVER_CSEQ: + *param_longp = data->state.rtsp_next_server_CSeq; + break; + case CURLINFO_RTSP_CSEQ_RECV: + *param_longp = data->state.rtsp_CSeq_recv; + break; +#endif + case CURLINFO_HTTP_VERSION: + switch(data->info.httpversion) { + case 10: + *param_longp = CURL_HTTP_VERSION_1_0; + break; + case 11: + *param_longp = CURL_HTTP_VERSION_1_1; + break; + case 20: + *param_longp = CURL_HTTP_VERSION_2_0; + break; + case 30: + *param_longp = CURL_HTTP_VERSION_3; + break; + default: + *param_longp = CURL_HTTP_VERSION_NONE; + break; + } + break; + case CURLINFO_PROTOCOL: + *param_longp = (long)data->info.conn_protocol; + break; + case CURLINFO_USED_PROXY: + *param_longp = +#ifdef CURL_DISABLE_PROXY + 0 +#else + data->info.used_proxy +#endif + ; + break; + default: + return CURLE_UNKNOWN_OPTION; + } + + return CURLE_OK; +} + +#define DOUBLE_SECS(x) (double)(x)/1000000 + +static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, + curl_off_t *param_offt) +{ +#ifdef DEBUGBUILD + char *timestr = getenv("CURL_TIME"); + if(timestr) { + unsigned long val = strtoul(timestr, NULL, 10); + switch(info) { + case CURLINFO_TOTAL_TIME_T: + case CURLINFO_NAMELOOKUP_TIME_T: + case CURLINFO_CONNECT_TIME_T: + case CURLINFO_APPCONNECT_TIME_T: + case CURLINFO_PRETRANSFER_TIME_T: + case CURLINFO_STARTTRANSFER_TIME_T: + case CURLINFO_REDIRECT_TIME_T: + case CURLINFO_SPEED_DOWNLOAD_T: + case CURLINFO_SPEED_UPLOAD_T: + *param_offt = (curl_off_t)val; + return CURLE_OK; + default: + break; + } + } +#endif + switch(info) { + case CURLINFO_FILETIME_T: + *param_offt = (curl_off_t)data->info.filetime; + break; + case CURLINFO_SIZE_UPLOAD_T: + *param_offt = data->progress.uploaded; + break; + case CURLINFO_SIZE_DOWNLOAD_T: + *param_offt = data->progress.downloaded; + break; + case CURLINFO_SPEED_DOWNLOAD_T: + *param_offt = data->progress.dlspeed; + break; + case CURLINFO_SPEED_UPLOAD_T: + *param_offt = data->progress.ulspeed; + break; + case CURLINFO_CONTENT_LENGTH_DOWNLOAD_T: + *param_offt = (data->progress.flags & PGRS_DL_SIZE_KNOWN)? + data->progress.size_dl:-1; + break; + case CURLINFO_CONTENT_LENGTH_UPLOAD_T: + *param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN)? + data->progress.size_ul:-1; + break; + case CURLINFO_TOTAL_TIME_T: + *param_offt = data->progress.timespent; + break; + case CURLINFO_NAMELOOKUP_TIME_T: + *param_offt = data->progress.t_nslookup; + break; + case CURLINFO_CONNECT_TIME_T: + *param_offt = data->progress.t_connect; + break; + case CURLINFO_APPCONNECT_TIME_T: + *param_offt = data->progress.t_appconnect; + break; + case CURLINFO_PRETRANSFER_TIME_T: + *param_offt = data->progress.t_pretransfer; + break; + case CURLINFO_STARTTRANSFER_TIME_T: + *param_offt = data->progress.t_starttransfer; + break; + case CURLINFO_QUEUE_TIME_T: + *param_offt = data->progress.t_postqueue; + break; + case CURLINFO_REDIRECT_TIME_T: + *param_offt = data->progress.t_redirect; + break; + case CURLINFO_RETRY_AFTER: + *param_offt = data->info.retry_after; + break; + case CURLINFO_XFER_ID: + *param_offt = data->id; + break; + case CURLINFO_CONN_ID: + *param_offt = data->conn? + data->conn->connection_id : data->state.recent_conn_id; + break; + default: + return CURLE_UNKNOWN_OPTION; + } + + return CURLE_OK; +} + +static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info, + double *param_doublep) +{ +#ifdef DEBUGBUILD + char *timestr = getenv("CURL_TIME"); + if(timestr) { + unsigned long val = strtoul(timestr, NULL, 10); + switch(info) { + case CURLINFO_TOTAL_TIME: + case CURLINFO_NAMELOOKUP_TIME: + case CURLINFO_CONNECT_TIME: + case CURLINFO_APPCONNECT_TIME: + case CURLINFO_PRETRANSFER_TIME: + case CURLINFO_STARTTRANSFER_TIME: + case CURLINFO_REDIRECT_TIME: + case CURLINFO_SPEED_DOWNLOAD: + case CURLINFO_SPEED_UPLOAD: + *param_doublep = (double)val; + return CURLE_OK; + default: + break; + } + } +#endif + switch(info) { + case CURLINFO_TOTAL_TIME: + *param_doublep = DOUBLE_SECS(data->progress.timespent); + break; + case CURLINFO_NAMELOOKUP_TIME: + *param_doublep = DOUBLE_SECS(data->progress.t_nslookup); + break; + case CURLINFO_CONNECT_TIME: + *param_doublep = DOUBLE_SECS(data->progress.t_connect); + break; + case CURLINFO_APPCONNECT_TIME: + *param_doublep = DOUBLE_SECS(data->progress.t_appconnect); + break; + case CURLINFO_PRETRANSFER_TIME: + *param_doublep = DOUBLE_SECS(data->progress.t_pretransfer); + break; + case CURLINFO_STARTTRANSFER_TIME: + *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer); + break; + case CURLINFO_SIZE_UPLOAD: + *param_doublep = (double)data->progress.uploaded; + break; + case CURLINFO_SIZE_DOWNLOAD: + *param_doublep = (double)data->progress.downloaded; + break; + case CURLINFO_SPEED_DOWNLOAD: + *param_doublep = (double)data->progress.dlspeed; + break; + case CURLINFO_SPEED_UPLOAD: + *param_doublep = (double)data->progress.ulspeed; + break; + case CURLINFO_CONTENT_LENGTH_DOWNLOAD: + *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN)? + (double)data->progress.size_dl:-1; + break; + case CURLINFO_CONTENT_LENGTH_UPLOAD: + *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN)? + (double)data->progress.size_ul:-1; + break; + case CURLINFO_REDIRECT_TIME: + *param_doublep = DOUBLE_SECS(data->progress.t_redirect); + break; + + default: + return CURLE_UNKNOWN_OPTION; + } + + return CURLE_OK; +} + +static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info, + struct curl_slist **param_slistp) +{ + union { + struct curl_certinfo *to_certinfo; + struct curl_slist *to_slist; + } ptr; + + switch(info) { + case CURLINFO_SSL_ENGINES: + *param_slistp = Curl_ssl_engines_list(data); + break; + case CURLINFO_COOKIELIST: + *param_slistp = Curl_cookie_list(data); + break; + case CURLINFO_CERTINFO: + /* Return the a pointer to the certinfo struct. Not really an slist + pointer but we can pretend it is here */ + ptr.to_certinfo = &data->info.certs; + *param_slistp = ptr.to_slist; + break; + case CURLINFO_TLS_SESSION: + case CURLINFO_TLS_SSL_PTR: + { + struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **) + param_slistp; + struct curl_tlssessioninfo *tsi = &data->tsi; +#ifdef USE_SSL + struct connectdata *conn = data->conn; +#endif + + *tsip = tsi; + tsi->backend = Curl_ssl_backend(); + tsi->internals = NULL; + +#ifdef USE_SSL + if(conn && tsi->backend != CURLSSLBACKEND_NONE) { + tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0); + } +#endif + } + break; + default: + return CURLE_UNKNOWN_OPTION; + } + + return CURLE_OK; +} + +static CURLcode getinfo_socket(struct Curl_easy *data, CURLINFO info, + curl_socket_t *param_socketp) +{ + switch(info) { + case CURLINFO_ACTIVESOCKET: + *param_socketp = Curl_getconnectinfo(data, NULL); + break; + default: + return CURLE_UNKNOWN_OPTION; + } + + return CURLE_OK; +} + +CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...) +{ + va_list arg; + long *param_longp = NULL; + double *param_doublep = NULL; + curl_off_t *param_offt = NULL; + const char **param_charp = NULL; + struct curl_slist **param_slistp = NULL; + curl_socket_t *param_socketp = NULL; + int type; + CURLcode result = CURLE_UNKNOWN_OPTION; + + if(!data) + return CURLE_BAD_FUNCTION_ARGUMENT; + + va_start(arg, info); + + type = CURLINFO_TYPEMASK & (int)info; + switch(type) { + case CURLINFO_STRING: + param_charp = va_arg(arg, const char **); + if(param_charp) + result = getinfo_char(data, info, param_charp); + break; + case CURLINFO_LONG: + param_longp = va_arg(arg, long *); + if(param_longp) + result = getinfo_long(data, info, param_longp); + break; + case CURLINFO_DOUBLE: + param_doublep = va_arg(arg, double *); + if(param_doublep) + result = getinfo_double(data, info, param_doublep); + break; + case CURLINFO_OFF_T: + param_offt = va_arg(arg, curl_off_t *); + if(param_offt) + result = getinfo_offt(data, info, param_offt); + break; + case CURLINFO_SLIST: + param_slistp = va_arg(arg, struct curl_slist **); + if(param_slistp) + result = getinfo_slist(data, info, param_slistp); + break; + case CURLINFO_SOCKET: + param_socketp = va_arg(arg, curl_socket_t *); + if(param_socketp) + result = getinfo_socket(data, info, param_socketp); + break; + default: + break; + } + + va_end(arg); + + return result; +} diff --git a/third_party/curl/lib/getinfo.h b/third_party/curl/lib/getinfo.h new file mode 100644 index 0000000000..56bb440b43 --- /dev/null +++ b/third_party/curl/lib/getinfo.h @@ -0,0 +1,29 @@ +#ifndef HEADER_CURL_GETINFO_H +#define HEADER_CURL_GETINFO_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...); +CURLcode Curl_initinfo(struct Curl_easy *data); + +#endif /* HEADER_CURL_GETINFO_H */ diff --git a/third_party/curl/lib/gopher.c b/third_party/curl/lib/gopher.c new file mode 100644 index 0000000000..ecea32633c --- /dev/null +++ b/third_party/curl/lib/gopher.c @@ -0,0 +1,244 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_GOPHER + +#include "urldata.h" +#include +#include "transfer.h" +#include "sendf.h" +#include "cfilters.h" +#include "connect.h" +#include "progress.h" +#include "gopher.h" +#include "select.h" +#include "strdup.h" +#include "vtls/vtls.h" +#include "url.h" +#include "escape.h" +#include "warnless.h" +#include "curl_printf.h" +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* + * Forward declarations. + */ + +static CURLcode gopher_do(struct Curl_easy *data, bool *done); +#ifdef USE_SSL +static CURLcode gopher_connect(struct Curl_easy *data, bool *done); +static CURLcode gopher_connecting(struct Curl_easy *data, bool *done); +#endif + +/* + * Gopher protocol handler. + * This is also a nice simple template to build off for simple + * connect-command-download protocols. + */ + +const struct Curl_handler Curl_handler_gopher = { + "gopher", /* scheme */ + ZERO_NULL, /* setup_connection */ + gopher_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_GOPHER, /* defport */ + CURLPROTO_GOPHER, /* protocol */ + CURLPROTO_GOPHER, /* family */ + PROTOPT_NONE /* flags */ +}; + +#ifdef USE_SSL +const struct Curl_handler Curl_handler_gophers = { + "gophers", /* scheme */ + ZERO_NULL, /* setup_connection */ + gopher_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + gopher_connect, /* connect_it */ + gopher_connecting, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_GOPHER, /* defport */ + CURLPROTO_GOPHERS, /* protocol */ + CURLPROTO_GOPHER, /* family */ + PROTOPT_SSL /* flags */ +}; + +static CURLcode gopher_connect(struct Curl_easy *data, bool *done) +{ + (void)data; + (void)done; + return CURLE_OK; +} + +static CURLcode gopher_connecting(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + CURLcode result; + + result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); + if(result) + connclose(conn, "Failed TLS connection"); + *done = TRUE; + return result; +} +#endif + +static CURLcode gopher_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + char *gopherpath; + char *path = data->state.up.path; + char *query = data->state.up.query; + char *sel = NULL; + char *sel_org = NULL; + timediff_t timeout_ms; + ssize_t k; + size_t amount, len; + int what; + + *done = TRUE; /* unconditionally */ + + /* path is guaranteed non-NULL */ + DEBUGASSERT(path); + + if(query) + gopherpath = aprintf("%s?%s", path, query); + else + gopherpath = strdup(path); + + if(!gopherpath) + return CURLE_OUT_OF_MEMORY; + + /* Create selector. Degenerate cases: / and /1 => convert to "" */ + if(strlen(gopherpath) <= 2) { + sel = (char *)""; + len = strlen(sel); + free(gopherpath); + } + else { + char *newp; + + /* Otherwise, drop / and the first character (i.e., item type) ... */ + newp = gopherpath; + newp += 2; + + /* ... and finally unescape */ + result = Curl_urldecode(newp, 0, &sel, &len, REJECT_ZERO); + free(gopherpath); + if(result) + return result; + sel_org = sel; + } + + k = curlx_uztosz(len); + + for(;;) { + /* Break out of the loop if the selector is empty because OpenSSL and/or + LibreSSL fail with errno 0 if this is the case. */ + if(strlen(sel) < 1) + break; + + result = Curl_xfer_send(data, sel, k, &amount); + if(!result) { /* Which may not have written it all! */ + result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount); + if(result) + break; + + k -= amount; + sel += amount; + if(k < 1) + break; /* but it did write it all */ + } + else + break; + + timeout_ms = Curl_timeleft(data, NULL, FALSE); + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } + if(!timeout_ms) + timeout_ms = TIMEDIFF_T_MAX; + + /* Do not busyloop. The entire loop thing is a work-around as it causes a + BLOCKING behavior which is a NO-NO. This function should rather be + split up in a do and a doing piece where the pieces that are not + possible to send now will be sent in the doing function repeatedly + until the entire request is sent. + */ + what = SOCKET_WRITABLE(sockfd, timeout_ms); + if(what < 0) { + result = CURLE_SEND_ERROR; + break; + } + else if(!what) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } + } + + free(sel_org); + + if(!result) + result = Curl_xfer_send(data, "\r\n", 2, &amount); + if(result) { + failf(data, "Failed sending Gopher request"); + return result; + } + result = Curl_client_write(data, CLIENTWRITE_HEADER, (char *)"\r\n", 2); + if(result) + return result; + + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); + return CURLE_OK; +} +#endif /* CURL_DISABLE_GOPHER */ diff --git a/third_party/curl/lib/gopher.h b/third_party/curl/lib/gopher.h new file mode 100644 index 0000000000..9e3365b71c --- /dev/null +++ b/third_party/curl/lib/gopher.h @@ -0,0 +1,34 @@ +#ifndef HEADER_CURL_GOPHER_H +#define HEADER_CURL_GOPHER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#ifndef CURL_DISABLE_GOPHER +extern const struct Curl_handler Curl_handler_gopher; +#ifdef USE_SSL +extern const struct Curl_handler Curl_handler_gophers; +#endif +#endif + +#endif /* HEADER_CURL_GOPHER_H */ diff --git a/third_party/curl/lib/hash.c b/third_party/curl/lib/hash.c new file mode 100644 index 0000000000..df8e2abffe --- /dev/null +++ b/third_party/curl/lib/hash.c @@ -0,0 +1,385 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "hash.h" +#include "llist.h" +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +static void +hash_element_dtor(void *user, void *element) +{ + struct Curl_hash *h = (struct Curl_hash *) user; + struct Curl_hash_element *e = (struct Curl_hash_element *) element; + + if(e->ptr) { + if(e->dtor) + e->dtor(e->key, e->key_len, e->ptr); + else + h->dtor(e->ptr); + e->ptr = NULL; + } + + e->key_len = 0; + + free(e); +} + +/* Initializes a hash structure. + * Return 1 on error, 0 is fine. + * + * @unittest: 1602 + * @unittest: 1603 + */ +void +Curl_hash_init(struct Curl_hash *h, + size_t slots, + hash_function hfunc, + comp_function comparator, + Curl_hash_dtor dtor) +{ + DEBUGASSERT(h); + DEBUGASSERT(slots); + DEBUGASSERT(hfunc); + DEBUGASSERT(comparator); + DEBUGASSERT(dtor); + + h->table = NULL; + h->hash_func = hfunc; + h->comp_func = comparator; + h->dtor = dtor; + h->size = 0; + h->slots = slots; +} + +static struct Curl_hash_element * +mk_hash_element(const void *key, size_t key_len, const void *p, + Curl_hash_elem_dtor dtor) +{ + /* allocate the struct plus memory after it to store the key */ + struct Curl_hash_element *he = malloc(sizeof(struct Curl_hash_element) + + key_len); + if(he) { + /* copy the key */ + memcpy(he->key, key, key_len); + he->key_len = key_len; + he->ptr = (void *) p; + he->dtor = dtor; + } + return he; +} + +#define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)] + +void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, + Curl_hash_elem_dtor dtor) +{ + struct Curl_hash_element *he; + struct Curl_llist_element *le; + struct Curl_llist *l; + + DEBUGASSERT(h); + DEBUGASSERT(h->slots); + if(!h->table) { + size_t i; + h->table = malloc(h->slots * sizeof(struct Curl_llist)); + if(!h->table) + return NULL; /* OOM */ + for(i = 0; i < h->slots; ++i) + Curl_llist_init(&h->table[i], hash_element_dtor); + } + + l = FETCH_LIST(h, key, key_len); + + for(le = l->head; le; le = le->next) { + he = (struct Curl_hash_element *) le->ptr; + if(h->comp_func(he->key, he->key_len, key, key_len)) { + Curl_llist_remove(l, le, (void *)h); + --h->size; + break; + } + } + + he = mk_hash_element(key, key_len, p, dtor); + if(he) { + Curl_llist_append(l, he, &he->list); + ++h->size; + return p; /* return the new entry */ + } + + return NULL; /* failure */ +} + +/* Insert the data in the hash. If there already was a match in the hash, that + * data is replaced. This function also "lazily" allocates the table if + * needed, as it is not done in the _init function (anymore). + * + * @unittest: 1305 + * @unittest: 1602 + * @unittest: 1603 + */ +void * +Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) +{ + return Curl_hash_add2(h, key, key_len, p, NULL); +} + +/* Remove the identified hash entry. + * Returns non-zero on failure. + * + * @unittest: 1603 + */ +int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len) +{ + struct Curl_llist_element *le; + struct Curl_llist *l; + + DEBUGASSERT(h); + DEBUGASSERT(h->slots); + if(h->table) { + l = FETCH_LIST(h, key, key_len); + + for(le = l->head; le; le = le->next) { + struct Curl_hash_element *he = le->ptr; + if(h->comp_func(he->key, he->key_len, key, key_len)) { + Curl_llist_remove(l, le, (void *) h); + --h->size; + return 0; + } + } + } + return 1; +} + +/* Retrieves a hash element. + * + * @unittest: 1603 + */ +void * +Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len) +{ + struct Curl_llist_element *le; + struct Curl_llist *l; + + DEBUGASSERT(h); + if(h->table) { + DEBUGASSERT(h->slots); + l = FETCH_LIST(h, key, key_len); + for(le = l->head; le; le = le->next) { + struct Curl_hash_element *he = le->ptr; + if(h->comp_func(he->key, he->key_len, key, key_len)) { + return he->ptr; + } + } + } + + return NULL; +} + +/* Destroys all the entries in the given hash and resets its attributes, + * prepping the given hash for [static|dynamic] deallocation. + * + * @unittest: 1305 + * @unittest: 1602 + * @unittest: 1603 + */ +void +Curl_hash_destroy(struct Curl_hash *h) +{ + if(h->table) { + size_t i; + for(i = 0; i < h->slots; ++i) { + Curl_llist_destroy(&h->table[i], (void *) h); + } + Curl_safefree(h->table); + } + h->size = 0; + h->slots = 0; +} + +/* Removes all the entries in the given hash. + * + * @unittest: 1602 + */ +void +Curl_hash_clean(struct Curl_hash *h) +{ + Curl_hash_clean_with_criterium(h, NULL, NULL); +} + +/* Cleans all entries that pass the comp function criteria. */ +void +Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, + int (*comp)(void *, void *)) +{ + struct Curl_llist_element *le; + struct Curl_llist_element *lnext; + struct Curl_llist *list; + size_t i; + + if(!h || !h->table) + return; + + for(i = 0; i < h->slots; ++i) { + list = &h->table[i]; + le = list->head; /* get first list entry */ + while(le) { + struct Curl_hash_element *he = le->ptr; + lnext = le->next; + /* ask the callback function if we shall remove this entry or not */ + if(!comp || comp(user, he->ptr)) { + Curl_llist_remove(list, le, (void *) h); + --h->size; /* one less entry in the hash now */ + } + le = lnext; + } + } +} + +size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num) +{ + const char *key_str = (const char *) key; + const char *end = key_str + key_length; + size_t h = 5381; + + while(key_str < end) { + size_t j = (size_t)*key_str++; + h += h << 5; + h ^= j; + } + + return (h % slots_num); +} + +size_t Curl_str_key_compare(void *k1, size_t key1_len, + void *k2, size_t key2_len) +{ + if((key1_len == key2_len) && !memcmp(k1, k2, key1_len)) + return 1; + + return 0; +} + +void Curl_hash_start_iterate(struct Curl_hash *hash, + struct Curl_hash_iterator *iter) +{ + iter->hash = hash; + iter->slot_index = 0; + iter->current_element = NULL; +} + +struct Curl_hash_element * +Curl_hash_next_element(struct Curl_hash_iterator *iter) +{ + struct Curl_hash *h = iter->hash; + + if(!h->table) + return NULL; /* empty hash, nothing to return */ + + /* Get the next element in the current list, if any */ + if(iter->current_element) + iter->current_element = iter->current_element->next; + + /* If we have reached the end of the list, find the next one */ + if(!iter->current_element) { + size_t i; + for(i = iter->slot_index; i < h->slots; i++) { + if(h->table[i].head) { + iter->current_element = h->table[i].head; + iter->slot_index = i + 1; + break; + } + } + } + + if(iter->current_element) { + struct Curl_hash_element *he = iter->current_element->ptr; + return he; + } + return NULL; +} + +#if 0 /* useful function for debugging hashes and their contents */ +void Curl_hash_print(struct Curl_hash *h, + void (*func)(void *)) +{ + struct Curl_hash_iterator iter; + struct Curl_hash_element *he; + size_t last_index = ~0; + + if(!h) + return; + + fprintf(stderr, "=Hash dump=\n"); + + Curl_hash_start_iterate(h, &iter); + + he = Curl_hash_next_element(&iter); + while(he) { + if(iter.slot_index != last_index) { + fprintf(stderr, "index %d:", iter.slot_index); + if(last_index != ~0) { + fprintf(stderr, "\n"); + } + last_index = iter.slot_index; + } + + if(func) + func(he->ptr); + else + fprintf(stderr, " [%p]", (void *)he->ptr); + + he = Curl_hash_next_element(&iter); + } + fprintf(stderr, "\n"); +} +#endif + +void Curl_hash_offt_init(struct Curl_hash *h, + size_t slots, + Curl_hash_dtor dtor) +{ + Curl_hash_init(h, slots, Curl_hash_str, Curl_str_key_compare, dtor); +} + +void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem) +{ + return Curl_hash_add(h, &id, sizeof(id), elem); +} + +int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id) +{ + return Curl_hash_delete(h, &id, sizeof(id)); +} + +void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id) +{ + return Curl_hash_pick(h, &id, sizeof(id)); +} diff --git a/third_party/curl/lib/hash.h b/third_party/curl/lib/hash.h new file mode 100644 index 0000000000..1cf787525b --- /dev/null +++ b/third_party/curl/lib/hash.h @@ -0,0 +1,113 @@ +#ifndef HEADER_CURL_HASH_H +#define HEADER_CURL_HASH_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "llist.h" + +/* Hash function prototype */ +typedef size_t (*hash_function) (void *key, + size_t key_length, + size_t slots_num); + +/* + Comparator function prototype. Compares two keys. +*/ +typedef size_t (*comp_function) (void *key1, + size_t key1_len, + void *key2, + size_t key2_len); + +typedef void (*Curl_hash_dtor)(void *); + +struct Curl_hash { + struct Curl_llist *table; + + /* Hash function to be used for this hash table */ + hash_function hash_func; + + /* Comparator function to compare keys */ + comp_function comp_func; + Curl_hash_dtor dtor; + size_t slots; + size_t size; +}; + +typedef void (*Curl_hash_elem_dtor)(void *key, size_t key_len, void *p); + +struct Curl_hash_element { + struct Curl_llist_element list; + void *ptr; + Curl_hash_elem_dtor dtor; + size_t key_len; + char key[1]; /* allocated memory following the struct */ +}; + +struct Curl_hash_iterator { + struct Curl_hash *hash; + size_t slot_index; + struct Curl_llist_element *current_element; +}; + +void Curl_hash_init(struct Curl_hash *h, + size_t slots, + hash_function hfunc, + comp_function comparator, + Curl_hash_dtor dtor); + +void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p); +void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, + Curl_hash_elem_dtor dtor); +int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len); +void *Curl_hash_pick(struct Curl_hash *, void *key, size_t key_len); +#define Curl_hash_count(h) ((h)->size) +void Curl_hash_destroy(struct Curl_hash *h); +void Curl_hash_clean(struct Curl_hash *h); +void Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, + int (*comp)(void *, void *)); +size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num); +size_t Curl_str_key_compare(void *k1, size_t key1_len, void *k2, + size_t key2_len); +void Curl_hash_start_iterate(struct Curl_hash *hash, + struct Curl_hash_iterator *iter); +struct Curl_hash_element * +Curl_hash_next_element(struct Curl_hash_iterator *iter); + +void Curl_hash_print(struct Curl_hash *h, + void (*func)(void *)); + +/* Hash for `curl_off_t` as key */ +void Curl_hash_offt_init(struct Curl_hash *h, size_t slots, + Curl_hash_dtor dtor); + +void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem); +int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id); +void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id); + + +#endif /* HEADER_CURL_HASH_H */ diff --git a/third_party/curl/lib/headers.c b/third_party/curl/lib/headers.c new file mode 100644 index 0000000000..59ac422258 --- /dev/null +++ b/third_party/curl/lib/headers.c @@ -0,0 +1,449 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "urldata.h" +#include "strdup.h" +#include "strcase.h" +#include "sendf.h" +#include "headers.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API) + +/* Generate the curl_header struct for the user. This function MUST assign all + struct fields in the output struct. */ +static void copy_header_external(struct Curl_header_store *hs, + size_t index, + size_t amount, + struct Curl_llist_element *e, + struct curl_header *hout) +{ + struct curl_header *h = hout; + h->name = hs->name; + h->value = hs->value; + h->amount = amount; + h->index = index; + /* this will randomly OR a reserved bit for the sole purpose of making it + impossible for applications to do == comparisons, as that would otherwise + be very tempting and then lead to the reserved bits not being reserved + anymore. */ + h->origin = (unsigned int)(hs->type | (1<<27)); + h->anchor = e; +} + +/* public API */ +CURLHcode curl_easy_header(CURL *easy, + const char *name, + size_t nameindex, + unsigned int type, + int request, + struct curl_header **hout) +{ + struct Curl_llist_element *e; + struct Curl_llist_element *e_pick = NULL; + struct Curl_easy *data = easy; + size_t match = 0; + size_t amount = 0; + struct Curl_header_store *hs = NULL; + struct Curl_header_store *pick = NULL; + if(!name || !hout || !data || + (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX| + CURLH_PSEUDO)) || !type || (request < -1)) + return CURLHE_BAD_ARGUMENT; + if(!Curl_llist_count(&data->state.httphdrs)) + return CURLHE_NOHEADERS; /* no headers available */ + if(request > data->state.requests) + return CURLHE_NOREQUEST; + if(request == -1) + request = data->state.requests; + + /* we need a first round to count amount of this header */ + for(e = data->state.httphdrs.head; e; e = e->next) { + hs = e->ptr; + if(strcasecompare(hs->name, name) && + (hs->type & type) && + (hs->request == request)) { + amount++; + pick = hs; + e_pick = e; + } + } + if(!amount) + return CURLHE_MISSING; + else if(nameindex >= amount) + return CURLHE_BADINDEX; + + if(nameindex == amount - 1) + /* if the last or only occurrence is what's asked for, then we know it */ + hs = pick; + else { + for(e = data->state.httphdrs.head; e; e = e->next) { + hs = e->ptr; + if(strcasecompare(hs->name, name) && + (hs->type & type) && + (hs->request == request) && + (match++ == nameindex)) { + e_pick = e; + break; + } + } + if(!e) /* this should not happen */ + return CURLHE_MISSING; + } + /* this is the name we want */ + copy_header_external(hs, nameindex, amount, e_pick, + &data->state.headerout[0]); + *hout = &data->state.headerout[0]; + return CURLHE_OK; +} + +/* public API */ +struct curl_header *curl_easy_nextheader(CURL *easy, + unsigned int type, + int request, + struct curl_header *prev) +{ + struct Curl_easy *data = easy; + struct Curl_llist_element *pick; + struct Curl_llist_element *e; + struct Curl_header_store *hs; + size_t amount = 0; + size_t index = 0; + + if(request > data->state.requests) + return NULL; + if(request == -1) + request = data->state.requests; + + if(prev) { + pick = prev->anchor; + if(!pick) + /* something is wrong */ + return NULL; + pick = pick->next; + } + else + pick = data->state.httphdrs.head; + + if(pick) { + /* make sure it is the next header of the desired type */ + do { + hs = pick->ptr; + if((hs->type & type) && (hs->request == request)) + break; + pick = pick->next; + } while(pick); + } + + if(!pick) + /* no more headers available */ + return NULL; + + hs = pick->ptr; + + /* count number of occurrences of this name within the mask and figure out + the index for the currently selected entry */ + for(e = data->state.httphdrs.head; e; e = e->next) { + struct Curl_header_store *check = e->ptr; + if(strcasecompare(hs->name, check->name) && + (check->request == request) && + (check->type & type)) + amount++; + if(e == pick) + index = amount - 1; + } + + copy_header_external(hs, index, amount, pick, + &data->state.headerout[1]); + return &data->state.headerout[1]; +} + +static CURLcode namevalue(char *header, size_t hlen, unsigned int type, + char **name, char **value) +{ + char *end = header + hlen - 1; /* point to the last byte */ + DEBUGASSERT(hlen); + *name = header; + + if(type == CURLH_PSEUDO) { + if(*header != ':') + return CURLE_BAD_FUNCTION_ARGUMENT; + header++; + } + + /* Find the end of the header name */ + while(*header && (*header != ':')) + ++header; + + if(*header) + /* Skip over colon, null it */ + *header++ = 0; + else + return CURLE_BAD_FUNCTION_ARGUMENT; + + /* skip all leading space letters */ + while(*header && ISBLANK(*header)) + header++; + + *value = header; + + /* skip all trailing space letters */ + while((end > header) && ISSPACE(*end)) + *end-- = 0; /* nul terminate */ + return CURLE_OK; +} + +static CURLcode unfold_value(struct Curl_easy *data, const char *value, + size_t vlen) /* length of the incoming header */ +{ + struct Curl_header_store *hs; + struct Curl_header_store *newhs; + size_t olen; /* length of the old value */ + size_t oalloc; /* length of the old name + value + separator */ + size_t offset; + DEBUGASSERT(data->state.prevhead); + hs = data->state.prevhead; + olen = strlen(hs->value); + offset = hs->value - hs->buffer; + oalloc = olen + offset + 1; + + /* skip all trailing space letters */ + while(vlen && ISSPACE(value[vlen - 1])) + vlen--; + + /* save only one leading space */ + while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) { + vlen--; + value++; + } + + /* since this header block might move in the realloc below, it needs to + first be unlinked from the list and then re-added again after the + realloc */ + Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL); + + /* new size = struct + new value length + old name+value length */ + newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1); + if(!newhs) + return CURLE_OUT_OF_MEMORY; + /* ->name and ->value point into ->buffer (to keep the header allocation + in a single memory block), which now potentially have moved. Adjust + them. */ + newhs->name = newhs->buffer; + newhs->value = &newhs->buffer[offset]; + + /* put the data at the end of the previous data, not the newline */ + memcpy(&newhs->value[olen], value, vlen); + newhs->value[olen + vlen] = 0; /* null-terminate at newline */ + + /* insert this node into the list of headers */ + Curl_llist_append(&data->state.httphdrs, newhs, &newhs->node); + data->state.prevhead = newhs; + return CURLE_OK; +} + + +/* + * Curl_headers_push() gets passed a full HTTP header to store. It gets called + * immediately before the header callback. The header is CRLF terminated. + */ +CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, + unsigned char type) +{ + char *value = NULL; + char *name = NULL; + char *end; + size_t hlen; /* length of the incoming header */ + struct Curl_header_store *hs; + CURLcode result = CURLE_OUT_OF_MEMORY; + + if((header[0] == '\r') || (header[0] == '\n')) + /* ignore the body separator */ + return CURLE_OK; + + end = strchr(header, '\r'); + if(!end) { + end = strchr(header, '\n'); + if(!end) + /* neither CR nor LF as terminator is not a valid header */ + return CURLE_WEIRD_SERVER_REPLY; + } + hlen = end - header; + + if((header[0] == ' ') || (header[0] == '\t')) { + if(data->state.prevhead) + /* line folding, append value to the previous header's value */ + return unfold_value(data, header, hlen); + else { + /* cannot unfold without a previous header. Instead of erroring, just + pass the leading blanks. */ + while(hlen && ISBLANK(*header)) { + header++; + hlen--; + } + if(!hlen) + return CURLE_WEIRD_SERVER_REPLY; + } + } + + hs = calloc(1, sizeof(*hs) + hlen); + if(!hs) + return CURLE_OUT_OF_MEMORY; + memcpy(hs->buffer, header, hlen); + hs->buffer[hlen] = 0; /* nul terminate */ + + result = namevalue(hs->buffer, hlen, type, &name, &value); + if(!result) { + hs->name = name; + hs->value = value; + hs->type = type; + hs->request = data->state.requests; + + /* insert this node into the list of headers */ + Curl_llist_append(&data->state.httphdrs, hs, &hs->node); + data->state.prevhead = hs; + } + else + free(hs); + return result; +} + +/* + * Curl_headers_reset(). Reset the headers subsystem. + */ +static void headers_reset(struct Curl_easy *data) +{ + Curl_llist_init(&data->state.httphdrs, NULL); + data->state.prevhead = NULL; +} + +struct hds_cw_collect_ctx { + struct Curl_cwriter super; +}; + +static CURLcode hds_cw_collect_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t blen) +{ + if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) { + unsigned char htype = (unsigned char) + (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT : + (type & CLIENTWRITE_1XX ? CURLH_1XX : + (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER : + CURLH_HEADER))); + CURLcode result = Curl_headers_push(data, buf, htype); + CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d", + htype, blen, result); + if(result) + return result; + } + return Curl_cwriter_write(data, writer->next, type, buf, blen); +} + +static const struct Curl_cwtype hds_cw_collect = { + "hds-collect", + NULL, + Curl_cwriter_def_init, + hds_cw_collect_write, + Curl_cwriter_def_close, + sizeof(struct hds_cw_collect_ctx) +}; + +CURLcode Curl_headers_init(struct Curl_easy *data) +{ + struct Curl_cwriter *writer; + CURLcode result; + + if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) { + /* avoid installing it twice */ + if(Curl_cwriter_get_by_name(data, hds_cw_collect.name)) + return CURLE_OK; + + result = Curl_cwriter_create(&writer, data, &hds_cw_collect, + CURL_CW_PROTOCOL); + if(result) + return result; + + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + return result; + } + } + return CURLE_OK; +} + +/* + * Curl_headers_cleanup(). Free all stored headers and associated memory. + */ +CURLcode Curl_headers_cleanup(struct Curl_easy *data) +{ + struct Curl_llist_element *e; + struct Curl_llist_element *n; + + for(e = data->state.httphdrs.head; e; e = n) { + struct Curl_header_store *hs = e->ptr; + n = e->next; + free(hs); + } + headers_reset(data); + return CURLE_OK; +} + +#else /* HTTP-disabled builds below */ + +CURLHcode curl_easy_header(CURL *easy, + const char *name, + size_t index, + unsigned int origin, + int request, + struct curl_header **hout) +{ + (void)easy; + (void)name; + (void)index; + (void)origin; + (void)request; + (void)hout; + return CURLHE_NOT_BUILT_IN; +} + +struct curl_header *curl_easy_nextheader(CURL *easy, + unsigned int type, + int request, + struct curl_header *prev) +{ + (void)easy; + (void)type; + (void)request; + (void)prev; + return NULL; +} +#endif diff --git a/third_party/curl/lib/headers.h b/third_party/curl/lib/headers.h new file mode 100644 index 0000000000..d9813388c5 --- /dev/null +++ b/third_party/curl/lib/headers.h @@ -0,0 +1,62 @@ +#ifndef HEADER_CURL_HEADER_H +#define HEADER_CURL_HEADER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API) + +struct Curl_header_store { + struct Curl_llist_element node; + char *name; /* points into 'buffer' */ + char *value; /* points into 'buffer */ + int request; /* 0 is the first request, then 1.. 2.. */ + unsigned char type; /* CURLH_* defines */ + char buffer[1]; /* this is the raw header blob */ +}; + +/* + * Initialize header collecting for a transfer. + * Will add a client writer that catches CLIENTWRITE_HEADER writes. + */ +CURLcode Curl_headers_init(struct Curl_easy *data); + +/* + * Curl_headers_push() gets passed a full header to store. + */ +CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, + unsigned char type); + +/* + * Curl_headers_cleanup(). Free all stored headers and associated memory. + */ +CURLcode Curl_headers_cleanup(struct Curl_easy *data); + +#else +#define Curl_headers_init(x) CURLE_OK +#define Curl_headers_push(x,y,z) CURLE_OK +#define Curl_headers_cleanup(x) Curl_nop_stmt +#endif + +#endif /* HEADER_CURL_HEADER_H */ diff --git a/third_party/curl/lib/hmac.c b/third_party/curl/lib/hmac.c new file mode 100644 index 0000000000..90f37f0bff --- /dev/null +++ b/third_party/curl/lib/hmac.c @@ -0,0 +1,173 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC2104 Keyed-Hashing for Message Authentication + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) + +#include + +#include "curl_hmac.h" +#include "curl_memory.h" +#include "warnless.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +/* + * Generic HMAC algorithm. + * + * This module computes HMAC digests based on any hash function. Parameters + * and computing procedures are setup dynamically at HMAC computation context + * initialization. + */ + +static const unsigned char hmac_ipad = 0x36; +static const unsigned char hmac_opad = 0x5C; + + + +struct HMAC_context * +Curl_HMAC_init(const struct HMAC_params *hashparams, + const unsigned char *key, + unsigned int keylen) +{ + size_t i; + struct HMAC_context *ctxt; + unsigned char *hkey; + unsigned char b; + + /* Create HMAC context. */ + i = sizeof(*ctxt) + 2 * hashparams->hmac_ctxtsize + + hashparams->hmac_resultlen; + ctxt = malloc(i); + + if(!ctxt) + return ctxt; + + ctxt->hmac_hash = hashparams; + ctxt->hmac_hashctxt1 = (void *) (ctxt + 1); + ctxt->hmac_hashctxt2 = (void *) ((char *) ctxt->hmac_hashctxt1 + + hashparams->hmac_ctxtsize); + + /* If the key is too long, replace it by its hash digest. */ + if(keylen > hashparams->hmac_maxkeylen) { + (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1); + (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, key, keylen); + hkey = (unsigned char *) ctxt->hmac_hashctxt2 + hashparams->hmac_ctxtsize; + (*hashparams->hmac_hfinal)(hkey, ctxt->hmac_hashctxt1); + key = hkey; + keylen = hashparams->hmac_resultlen; + } + + /* Prime the two hash contexts with the modified key. */ + (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1); + (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt2); + + for(i = 0; i < keylen; i++) { + b = (unsigned char)(*key ^ hmac_ipad); + (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &b, 1); + b = (unsigned char)(*key++ ^ hmac_opad); + (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &b, 1); + } + + for(; i < hashparams->hmac_maxkeylen; i++) { + (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &hmac_ipad, 1); + (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &hmac_opad, 1); + } + + /* Done, return pointer to HMAC context. */ + return ctxt; +} + +int Curl_HMAC_update(struct HMAC_context *ctxt, + const unsigned char *data, + unsigned int len) +{ + /* Update first hash calculation. */ + (*ctxt->hmac_hash->hmac_hupdate)(ctxt->hmac_hashctxt1, data, len); + return 0; +} + + +int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *result) +{ + const struct HMAC_params *hashparams = ctxt->hmac_hash; + + /* Do not get result if called with a null parameter: only release + storage. */ + + if(!result) + result = (unsigned char *) ctxt->hmac_hashctxt2 + + ctxt->hmac_hash->hmac_ctxtsize; + + (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt1); + (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, + result, hashparams->hmac_resultlen); + (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt2); + free((char *) ctxt); + return 0; +} + +/* + * Curl_hmacit() + * + * This is used to generate a HMAC hash, for the specified input data, given + * the specified hash function and key. + * + * Parameters: + * + * hashparams [in] - The hash function (Curl_HMAC_MD5). + * key [in] - The key to use. + * keylen [in] - The length of the key. + * data [in] - The data to encrypt. + * datalen [in] - The length of the data. + * output [in/out] - The output buffer. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_hmacit(const struct HMAC_params *hashparams, + const unsigned char *key, const size_t keylen, + const unsigned char *data, const size_t datalen, + unsigned char *output) +{ + struct HMAC_context *ctxt = + Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen)); + + if(!ctxt) + return CURLE_OUT_OF_MEMORY; + + /* Update the digest with the given challenge */ + Curl_HMAC_update(ctxt, data, curlx_uztoui(datalen)); + + /* Finalise the digest */ + Curl_HMAC_final(ctxt, output); + + return CURLE_OK; +} + +#endif /* Using NTLM (without SSPI) or AWS */ diff --git a/third_party/curl/lib/hostasyn.c b/third_party/curl/lib/hostasyn.c new file mode 100644 index 0000000000..2f6762ca4e --- /dev/null +++ b/third_party/curl/lib/hostasyn.c @@ -0,0 +1,123 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +/*********************************************************************** + * Only for builds using asynchronous name resolves + **********************************************************************/ +#ifdef CURLRES_ASYNCH + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "hash.h" +#include "share.h" +#include "url.h" +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* + * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread() + * or getaddrinfo_thread() when we got the name resolved (or not!). + * + * If the status argument is CURL_ASYNC_SUCCESS, this function takes + * ownership of the Curl_addrinfo passed, storing the resolved data + * in the DNS cache. + * + * The storage operation locks and unlocks the DNS cache. + */ +CURLcode Curl_addrinfo_callback(struct Curl_easy *data, + int status, + struct Curl_addrinfo *ai) +{ + struct Curl_dns_entry *dns = NULL; + CURLcode result = CURLE_OK; + + data->state.async.status = status; + + if(CURL_ASYNC_SUCCESS == status) { + if(ai) { + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + dns = Curl_cache_addr(data, ai, + data->state.async.hostname, 0, + data->state.async.port); + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + if(!dns) { + /* failed to store, cleanup and return error */ + Curl_freeaddrinfo(ai); + result = CURLE_OUT_OF_MEMORY; + } + } + else { + result = CURLE_OUT_OF_MEMORY; + } + } + + data->state.async.dns = dns; + + /* Set async.done TRUE last in this function since it may be used multi- + threaded and once this is TRUE the other thread may read fields from the + async struct */ + data->state.async.done = TRUE; + + /* IPv4: The input hostent struct will be freed by ares when we return from + this function */ + return result; +} + +/* + * Curl_getaddrinfo() is the generic low-level name resolve API within this + * source file. There are several versions of this function - for different + * name resolve layers (selected at build-time). They all take this same set + * of arguments + */ +struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, + const char *hostname, + int port, + int *waitp) +{ + return Curl_resolver_getaddrinfo(data, hostname, port, waitp); +} + +#endif /* CURLRES_ASYNCH */ diff --git a/third_party/curl/lib/hostip.c b/third_party/curl/lib/hostip.c new file mode 100644 index 0000000000..2c1f08e744 --- /dev/null +++ b/third_party/curl/lib/hostip.c @@ -0,0 +1,1481 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETINET_IN6_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include +#include + +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "hash.h" +#include "rand.h" +#include "share.h" +#include "url.h" +#include "inet_ntop.h" +#include "inet_pton.h" +#include "multiif.h" +#include "doh.h" +#include "warnless.h" +#include "strcase.h" +#include "easy_lock.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if defined(CURLRES_SYNCH) && \ + defined(HAVE_ALARM) && \ + defined(SIGALRM) && \ + defined(HAVE_SIGSETJMP) && \ + defined(GLOBAL_INIT_IS_THREADSAFE) +/* alarm-based timeouts can only be used with all the dependencies satisfied */ +#define USE_ALARM_TIMEOUT +#endif + +#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ + +#define MAX_DNS_CACHE_SIZE 29999 + +/* + * hostip.c explained + * ================== + * + * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c + * source file are these: + * + * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use + * that. The host may not be able to resolve IPv6, but we do not really have to + * take that into account. Hosts that are not IPv6-enabled have CURLRES_IPV4 + * defined. + * + * CURLRES_ARES - is defined if libcurl is built to use c-ares for + * asynchronous name resolves. This can be Windows or *nix. + * + * CURLRES_THREADED - is defined if libcurl is built to run under (native) + * Windows, and then the name resolve will be done in a new thread, and the + * supported API will be the same as for ares-builds. + * + * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If + * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is + * defined. + * + * The host*.c sources files are split up like this: + * + * hostip.c - method-independent resolver functions and utility functions + * hostasyn.c - functions for asynchronous name resolves + * hostsyn.c - functions for synchronous name resolves + * hostip4.c - IPv4 specific functions + * hostip6.c - IPv6 specific functions + * + * The two asynchronous name resolver backends are implemented in: + * asyn-ares.c - functions for ares-using name resolves + * asyn-thread.c - functions for threaded name resolves + + * The hostip.h is the united header file for all this. It defines the + * CURLRES_* defines based on the config*.h and curl_setup.h defines. + */ + +static void freednsentry(void *freethis); + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void show_resolve_info(struct Curl_easy *data, + struct Curl_dns_entry *dns); +#else +#define show_resolve_info(x,y) Curl_nop_stmt +#endif + +/* + * Curl_printable_address() stores a printable version of the 1st address + * given in the 'ai' argument. The result will be stored in the buf that is + * bufsize bytes big. + * + * If the conversion fails, the target buffer is empty. + */ +void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, + size_t bufsize) +{ + DEBUGASSERT(bufsize); + buf[0] = 0; + + switch(ai->ai_family) { + case AF_INET: { + const struct sockaddr_in *sa4 = (const void *)ai->ai_addr; + const struct in_addr *ipaddr4 = &sa4->sin_addr; + (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize); + break; + } +#ifdef USE_IPV6 + case AF_INET6: { + const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr; + const struct in6_addr *ipaddr6 = &sa6->sin6_addr; + (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize); + break; + } +#endif + default: + break; + } +} + +/* + * Create a hostcache id string for the provided host + port, to be used by + * the DNS caching. Without alloc. Return length of the id string. + */ +static size_t +create_hostcache_id(const char *name, + size_t nlen, /* 0 or actual name length */ + int port, char *ptr, size_t buflen) +{ + size_t len = nlen ? nlen : strlen(name); + DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); + if(len > (buflen - 7)) + len = buflen - 7; + /* store and lower case the name */ + Curl_strntolower(ptr, name, len); + return msnprintf(&ptr[len], 7, ":%u", port) + len; +} + +struct hostcache_prune_data { + time_t now; + time_t oldest; /* oldest time in cache not pruned. */ + int cache_timeout; +}; + +/* + * This function is set as a callback to be called for every entry in the DNS + * cache when we want to prune old unused entries. + * + * Returning non-zero means remove the entry, return 0 to keep it in the + * cache. + */ +static int +hostcache_timestamp_remove(void *datap, void *hc) +{ + struct hostcache_prune_data *prune = + (struct hostcache_prune_data *) datap; + struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc; + + if(c->timestamp) { + /* age in seconds */ + time_t age = prune->now - c->timestamp; + if(age >= prune->cache_timeout) + return TRUE; + if(age > prune->oldest) + prune->oldest = age; + } + return FALSE; +} + +/* + * Prune the DNS cache. This assumes that a lock has already been taken. + * Returns the 'age' of the oldest still kept entry. + */ +static time_t +hostcache_prune(struct Curl_hash *hostcache, int cache_timeout, + time_t now) +{ + struct hostcache_prune_data user; + + user.cache_timeout = cache_timeout; + user.now = now; + user.oldest = 0; + + Curl_hash_clean_with_criterium(hostcache, + (void *) &user, + hostcache_timestamp_remove); + + return user.oldest; +} + +/* + * Library-wide function for pruning the DNS cache. This function takes and + * returns the appropriate locks. + */ +void Curl_hostcache_prune(struct Curl_easy *data) +{ + time_t now; + /* the timeout may be set -1 (forever) */ + int timeout = data->set.dns_cache_timeout; + + if(!data->dns.hostcache) + /* NULL hostcache means we cannot do it */ + return; + + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + now = time(NULL); + + do { + /* Remove outdated and unused entries from the hostcache */ + time_t oldest = hostcache_prune(data->dns.hostcache, timeout, now); + + if(oldest < INT_MAX) + timeout = (int)oldest; /* we know it fits */ + else + timeout = INT_MAX - 1; + + /* if the cache size is still too big, use the oldest age as new + prune limit */ + } while(timeout && (data->dns.hostcache->size > MAX_DNS_CACHE_SIZE)); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); +} + +#ifdef USE_ALARM_TIMEOUT +/* Beware this is a global and unique instance. This is used to store the + return address that we can jump back to from inside a signal handler. This + is not thread-safe stuff. */ +static sigjmp_buf curl_jmpenv; +static curl_simple_lock curl_jmpenv_lock; +#endif + +/* lookup address, returns entry if found and not stale */ +static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, + const char *hostname, + int port) +{ + struct Curl_dns_entry *dns = NULL; + char entry_id[MAX_HOSTCACHE_LEN]; + + /* Create an entry id, based upon the hostname and port */ + size_t entry_len = create_hostcache_id(hostname, 0, port, + entry_id, sizeof(entry_id)); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); + + /* No entry found in cache, check if we might have a wildcard entry */ + if(!dns && data->state.wildcard_resolve) { + entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id)); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); + } + + if(dns && (data->set.dns_cache_timeout != -1)) { + /* See whether the returned entry is stale. Done before we release lock */ + struct hostcache_prune_data user; + + user.now = time(NULL); + user.cache_timeout = data->set.dns_cache_timeout; + user.oldest = 0; + + if(hostcache_timestamp_remove(&user, dns)) { + infof(data, "Hostname in DNS cache was stale, zapped"); + dns = NULL; /* the memory deallocation is being handled by the hash */ + Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); + } + } + + /* See if the returned entry matches the required resolve mode */ + if(dns && data->conn->ip_version != CURL_IPRESOLVE_WHATEVER) { + int pf = PF_INET; + bool found = false; + struct Curl_addrinfo *addr = dns->addr; + +#ifdef PF_INET6 + if(data->conn->ip_version == CURL_IPRESOLVE_V6) + pf = PF_INET6; +#endif + + while(addr) { + if(addr->ai_family == pf) { + found = true; + break; + } + addr = addr->ai_next; + } + + if(!found) { + infof(data, "Hostname in DNS cache does not have needed family, zapped"); + dns = NULL; /* the memory deallocation is being handled by the hash */ + Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); + } + } + return dns; +} + +/* + * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache. + * + * Curl_resolv() checks initially and multi_runsingle() checks each time + * it discovers the handle in the state WAITRESOLVE whether the hostname + * has already been resolved and the address has already been stored in + * the DNS cache. This short circuits waiting for a lot of pending + * lookups for the same hostname requested by different handles. + * + * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. + * + * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after + * use, or we will leak memory! + */ +struct Curl_dns_entry * +Curl_fetch_addr(struct Curl_easy *data, + const char *hostname, + int port) +{ + struct Curl_dns_entry *dns = NULL; + + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + dns = fetch_addr(data, hostname, port); + + if(dns) + dns->inuse++; /* we use it! */ + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + return dns; +} + +#ifndef CURL_DISABLE_SHUFFLE_DNS +/* + * Return # of addresses in a Curl_addrinfo struct + */ +static int num_addresses(const struct Curl_addrinfo *addr) +{ + int i = 0; + while(addr) { + addr = addr->ai_next; + i++; + } + return i; +} + +UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, + struct Curl_addrinfo **addr); +/* + * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' + * struct by re-linking its linked list. + * + * The addr argument should be the address of a pointer to the head node of a + * `Curl_addrinfo` list and it will be modified to point to the new head after + * shuffling. + * + * Not declared static only to make it easy to use in a unit test! + * + * @unittest: 1608 + */ +UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, + struct Curl_addrinfo **addr) +{ + CURLcode result = CURLE_OK; + const int num_addrs = num_addresses(*addr); + + if(num_addrs > 1) { + struct Curl_addrinfo **nodes; + infof(data, "Shuffling %i addresses", num_addrs); + + nodes = malloc(num_addrs*sizeof(*nodes)); + if(nodes) { + int i; + unsigned int *rnd; + const size_t rnd_size = num_addrs * sizeof(*rnd); + + /* build a plain array of Curl_addrinfo pointers */ + nodes[0] = *addr; + for(i = 1; i < num_addrs; i++) { + nodes[i] = nodes[i-1]->ai_next; + } + + rnd = malloc(rnd_size); + if(rnd) { + /* Fisher-Yates shuffle */ + if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { + struct Curl_addrinfo *swap_tmp; + for(i = num_addrs - 1; i > 0; i--) { + swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)]; + nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i]; + nodes[i] = swap_tmp; + } + + /* relink list in the new order */ + for(i = 1; i < num_addrs; i++) { + nodes[i-1]->ai_next = nodes[i]; + } + + nodes[num_addrs-1]->ai_next = NULL; + *addr = nodes[0]; + } + free(rnd); + } + else + result = CURLE_OUT_OF_MEMORY; + free(nodes); + } + else + result = CURLE_OUT_OF_MEMORY; + } + return result; +} +#endif + +/* + * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. + * + * When calling Curl_resolv() has resulted in a response with a returned + * address, we call this function to store the information in the dns + * cache etc + * + * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. + */ +struct Curl_dns_entry * +Curl_cache_addr(struct Curl_easy *data, + struct Curl_addrinfo *addr, + const char *hostname, + size_t hostlen, /* length or zero */ + int port) +{ + char entry_id[MAX_HOSTCACHE_LEN]; + size_t entry_len; + struct Curl_dns_entry *dns; + struct Curl_dns_entry *dns2; + +#ifndef CURL_DISABLE_SHUFFLE_DNS + /* shuffle addresses if requested */ + if(data->set.dns_shuffle_addresses) { + CURLcode result = Curl_shuffle_addr(data, &addr); + if(result) + return NULL; + } +#endif + if(!hostlen) + hostlen = strlen(hostname); + + /* Create a new cache entry */ + dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen); + if(!dns) { + return NULL; + } + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_hostcache_id(hostname, hostlen, port, + entry_id, sizeof(entry_id)); + + dns->inuse = 1; /* the cache has the first reference */ + dns->addr = addr; /* this is the address(es) */ + time(&dns->timestamp); + if(dns->timestamp == 0) + dns->timestamp = 1; /* zero indicates permanent CURLOPT_RESOLVE entry */ + dns->hostport = port; + if(hostlen) + memcpy(dns->hostname, hostname, hostlen); + + /* Store the resolved data in our DNS cache. */ + dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1, + (void *)dns); + if(!dns2) { + free(dns); + return NULL; + } + + dns = dns2; + dns->inuse++; /* mark entry as in-use */ + return dns; +} + +#ifdef USE_IPV6 +/* return a static IPv6 ::1 for the name */ +static struct Curl_addrinfo *get_localhost6(int port, const char *name) +{ + struct Curl_addrinfo *ca; + const size_t ss_size = sizeof(struct sockaddr_in6); + const size_t hostlen = strlen(name); + struct sockaddr_in6 sa6; + unsigned char ipv6[16]; + unsigned short port16 = (unsigned short)(port & 0xffff); + ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1); + if(!ca) + return NULL; + + sa6.sin6_family = AF_INET6; + sa6.sin6_port = htons(port16); + sa6.sin6_flowinfo = 0; + sa6.sin6_scope_id = 0; + + (void)Curl_inet_pton(AF_INET6, "::1", ipv6); + memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6)); + + ca->ai_flags = 0; + ca->ai_family = AF_INET6; + ca->ai_socktype = SOCK_STREAM; + ca->ai_protocol = IPPROTO_TCP; + ca->ai_addrlen = (curl_socklen_t)ss_size; + ca->ai_next = NULL; + ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); + memcpy(ca->ai_addr, &sa6, ss_size); + ca->ai_canonname = (char *)ca->ai_addr + ss_size; + strcpy(ca->ai_canonname, name); + return ca; +} +#else +#define get_localhost6(x,y) NULL +#endif + +/* return a static IPv4 127.0.0.1 for the given name */ +static struct Curl_addrinfo *get_localhost(int port, const char *name) +{ + struct Curl_addrinfo *ca; + struct Curl_addrinfo *ca6; + const size_t ss_size = sizeof(struct sockaddr_in); + const size_t hostlen = strlen(name); + struct sockaddr_in sa; + unsigned int ipv4; + unsigned short port16 = (unsigned short)(port & 0xffff); + + /* memset to clear the sa.sin_zero field */ + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(port16); + if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1) + return NULL; + memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4)); + + ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1); + if(!ca) + return NULL; + ca->ai_flags = 0; + ca->ai_family = AF_INET; + ca->ai_socktype = SOCK_STREAM; + ca->ai_protocol = IPPROTO_TCP; + ca->ai_addrlen = (curl_socklen_t)ss_size; + ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); + memcpy(ca->ai_addr, &sa, ss_size); + ca->ai_canonname = (char *)ca->ai_addr + ss_size; + strcpy(ca->ai_canonname, name); + + ca6 = get_localhost6(port, name); + if(!ca6) + return ca; + ca6->ai_next = ca; + return ca6; +} + +#ifdef USE_IPV6 +/* + * Curl_ipv6works() returns TRUE if IPv6 seems to work. + */ +bool Curl_ipv6works(struct Curl_easy *data) +{ + if(data) { + /* the nature of most system is that IPv6 status does not come and go + during a program's lifetime so we only probe the first time and then we + have the info kept for fast reuse */ + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + if(data->multi->ipv6_up == IPV6_UNKNOWN) { + bool works = Curl_ipv6works(NULL); + data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD; + } + return data->multi->ipv6_up == IPV6_WORKS; + } + else { + int ipv6_works = -1; + /* probe to see if we have a working IPv6 stack */ + curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0); + if(s == CURL_SOCKET_BAD) + /* an IPv6 address was requested but we cannot get/use one */ + ipv6_works = 0; + else { + ipv6_works = 1; + sclose(s); + } + return (ipv6_works>0)?TRUE:FALSE; + } +} +#endif /* USE_IPV6 */ + +/* + * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4 + * (or IPv6 if supported) address. + */ +bool Curl_host_is_ipnum(const char *hostname) +{ + struct in_addr in; +#ifdef USE_IPV6 + struct in6_addr in6; +#endif + if(Curl_inet_pton(AF_INET, hostname, &in) > 0 +#ifdef USE_IPV6 + || Curl_inet_pton(AF_INET6, hostname, &in6) > 0 +#endif + ) + return TRUE; + return FALSE; +} + + +/* return TRUE if 'part' is a case insensitive tail of 'full' */ +static bool tailmatch(const char *full, const char *part) +{ + size_t plen = strlen(part); + size_t flen = strlen(full); + if(plen > flen) + return FALSE; + return strncasecompare(part, &full[flen - plen], plen); +} + +/* + * Curl_resolv() is the main name resolve function within libcurl. It resolves + * a name and returns a pointer to the entry in the 'entry' argument (if one + * is provided). This function might return immediately if we are using asynch + * resolves. See the return codes. + * + * The cache entry we return will get its 'inuse' counter increased when this + * function is used. You MUST call Curl_resolv_unlock() later (when you are + * done using this struct) to decrease the counter again. + * + * Return codes: + * + * CURLRESOLV_ERROR (-1) = error, no pointer + * CURLRESOLV_RESOLVED (0) = OK, pointer provided + * CURLRESOLV_PENDING (1) = waiting for response, no pointer + */ + +enum resolve_t Curl_resolv(struct Curl_easy *data, + const char *hostname, + int port, + bool allowDOH, + struct Curl_dns_entry **entry) +{ + struct Curl_dns_entry *dns = NULL; + CURLcode result; + enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */ + struct connectdata *conn = data->conn; + /* We should intentionally error and not resolve .onion TLDs */ + size_t hostname_len = strlen(hostname); + if(hostname_len >= 7 && + (curl_strequal(&hostname[hostname_len - 6], ".onion") || + curl_strequal(&hostname[hostname_len - 7], ".onion."))) { + failf(data, "Not resolving .onion address (RFC 7686)"); + return CURLRESOLV_ERROR; + } + *entry = NULL; +#ifndef CURL_DISABLE_DOH + conn->bits.doh = FALSE; /* default is not */ +#else + (void)allowDOH; +#endif + + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + dns = fetch_addr(data, hostname, port); + + if(dns) { + infof(data, "Hostname %s was found in DNS cache", hostname); + dns->inuse++; /* we use it! */ + rc = CURLRESOLV_RESOLVED; + } + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + if(!dns) { + /* The entry was not in the cache. Resolve it to IP address */ + + struct Curl_addrinfo *addr = NULL; + int respwait = 0; +#if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS) + struct in_addr in; +#endif +#ifndef CURL_DISABLE_DOH +#ifndef USE_RESOLVE_ON_IPS + const +#endif + bool ipnum = FALSE; +#endif + + /* notify the resolver start callback */ + if(data->set.resolver_start) { + int st; + Curl_set_in_callback(data, true); + st = data->set.resolver_start( +#ifdef USE_CURL_ASYNC + data->state.async.resolver, +#else + NULL, +#endif + NULL, + data->set.resolver_start_client); + Curl_set_in_callback(data, false); + if(st) + return CURLRESOLV_ERROR; + } + +#ifndef USE_RESOLVE_ON_IPS + /* First check if this is an IPv4 address string */ + if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { + /* This is a dotted IP address 123.123.123.123-style */ + addr = Curl_ip2addr(AF_INET, &in, hostname, port); + if(!addr) + return CURLRESOLV_ERROR; + } +#ifdef USE_IPV6 + else { + struct in6_addr in6; + /* check if this is an IPv6 address string */ + if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) { + /* This is an IPv6 address literal */ + addr = Curl_ip2addr(AF_INET6, &in6, hostname, port); + if(!addr) + return CURLRESOLV_ERROR; + } + } +#endif /* USE_IPV6 */ + +#else /* if USE_RESOLVE_ON_IPS */ +#ifndef CURL_DISABLE_DOH + /* First check if this is an IPv4 address string */ + if(Curl_inet_pton(AF_INET, hostname, &in) > 0) + /* This is a dotted IP address 123.123.123.123-style */ + ipnum = TRUE; +#ifdef USE_IPV6 + else { + struct in6_addr in6; + /* check if this is an IPv6 address string */ + if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) + /* This is an IPv6 address literal */ + ipnum = TRUE; + } +#endif /* USE_IPV6 */ +#endif /* CURL_DISABLE_DOH */ + +#endif /* !USE_RESOLVE_ON_IPS */ + + if(!addr) { + if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data)) + return CURLRESOLV_ERROR; + + if(strcasecompare(hostname, "localhost") || + tailmatch(hostname, ".localhost")) + addr = get_localhost(port, hostname); +#ifndef CURL_DISABLE_DOH + else if(allowDOH && data->set.doh && !ipnum) + addr = Curl_doh(data, hostname, port, &respwait); +#endif + else { + /* Check what IP specifics the app has requested and if we can provide + * it. If not, bail out. */ + if(!Curl_ipvalid(data, conn)) + return CURLRESOLV_ERROR; + /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a + non-zero value indicating that we need to wait for the response to + the resolve call */ + addr = Curl_getaddrinfo(data, hostname, port, &respwait); + } + } + if(!addr) { + if(respwait) { + /* the response to our resolve call will come asynchronously at + a later time, good or bad */ + /* First, check that we have not received the info by now */ + result = Curl_resolv_check(data, &dns); + if(result) /* error detected */ + return CURLRESOLV_ERROR; + if(dns) + rc = CURLRESOLV_RESOLVED; /* pointer provided */ + else + rc = CURLRESOLV_PENDING; /* no info yet */ + } + } + else { + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + /* we got a response, store it in the cache */ + dns = Curl_cache_addr(data, addr, hostname, 0, port); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + if(!dns) + /* returned failure, bail out nicely */ + Curl_freeaddrinfo(addr); + else { + rc = CURLRESOLV_RESOLVED; + show_resolve_info(data, dns); + } + } + } + + *entry = dns; + + return rc; +} + +#ifdef USE_ALARM_TIMEOUT +/* + * This signal handler jumps back into the main libcurl code and continues + * execution. This effectively causes the remainder of the application to run + * within a signal handler which is nonportable and could lead to problems. + */ +CURL_NORETURN static +void alarmfunc(int sig) +{ + (void)sig; + siglongjmp(curl_jmpenv, 1); +} +#endif /* USE_ALARM_TIMEOUT */ + +/* + * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a + * timeout. This function might return immediately if we are using asynch + * resolves. See the return codes. + * + * The cache entry we return will get its 'inuse' counter increased when this + * function is used. You MUST call Curl_resolv_unlock() later (when you are + * done using this struct) to decrease the counter again. + * + * If built with a synchronous resolver and use of signals is not + * disabled by the application, then a nonzero timeout will cause a + * timeout after the specified number of milliseconds. Otherwise, timeout + * is ignored. + * + * Return codes: + * + * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired + * CURLRESOLV_ERROR (-1) = error, no pointer + * CURLRESOLV_RESOLVED (0) = OK, pointer provided + * CURLRESOLV_PENDING (1) = waiting for response, no pointer + */ + +enum resolve_t Curl_resolv_timeout(struct Curl_easy *data, + const char *hostname, + int port, + struct Curl_dns_entry **entry, + timediff_t timeoutms) +{ +#ifdef USE_ALARM_TIMEOUT +#ifdef HAVE_SIGACTION + struct sigaction keep_sigact; /* store the old struct here */ + volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */ + struct sigaction sigact; +#else +#ifdef HAVE_SIGNAL + void (*keep_sigact)(int); /* store the old handler here */ +#endif /* HAVE_SIGNAL */ +#endif /* HAVE_SIGACTION */ + volatile long timeout; + volatile unsigned int prev_alarm = 0; +#endif /* USE_ALARM_TIMEOUT */ + enum resolve_t rc; + + *entry = NULL; + + if(timeoutms < 0) + /* got an already expired timeout */ + return CURLRESOLV_TIMEDOUT; + +#ifdef USE_ALARM_TIMEOUT + if(data->set.no_signal) + /* Ignore the timeout when signals are disabled */ + timeout = 0; + else + timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms; + + if(!timeout) + /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */ + return Curl_resolv(data, hostname, port, TRUE, entry); + + if(timeout < 1000) { + /* The alarm() function only provides integer second resolution, so if + we want to wait less than one second we must bail out already now. */ + failf(data, + "remaining timeout of %ld too small to resolve via SIGALRM method", + timeout); + return CURLRESOLV_TIMEDOUT; + } + /* This allows us to time-out from the name resolver, as the timeout + will generate a signal and we will siglongjmp() from that here. + This technique has problems (see alarmfunc). + This should be the last thing we do before calling Curl_resolv(), + as otherwise we would have to worry about variables that get modified + before we invoke Curl_resolv() (and thus use "volatile"). */ + curl_simple_lock_lock(&curl_jmpenv_lock); + + if(sigsetjmp(curl_jmpenv, 1)) { + /* this is coming from a siglongjmp() after an alarm signal */ + failf(data, "name lookup timed out"); + rc = CURLRESOLV_ERROR; + goto clean_up; + } + else { + /************************************************************* + * Set signal handler to catch SIGALRM + * Store the old value to be able to set it back later! + *************************************************************/ +#ifdef HAVE_SIGACTION + sigaction(SIGALRM, NULL, &sigact); + keep_sigact = sigact; + keep_copysig = TRUE; /* yes, we have a copy */ + sigact.sa_handler = alarmfunc; +#ifdef SA_RESTART + /* HPUX does not have SA_RESTART but defaults to that behavior! */ + sigact.sa_flags &= ~SA_RESTART; +#endif + /* now set the new struct */ + sigaction(SIGALRM, &sigact, NULL); +#else /* HAVE_SIGACTION */ + /* no sigaction(), revert to the much lamer signal() */ +#ifdef HAVE_SIGNAL + keep_sigact = signal(SIGALRM, alarmfunc); +#endif +#endif /* HAVE_SIGACTION */ + + /* alarm() makes a signal get sent when the timeout fires off, and that + will abort system calls */ + prev_alarm = alarm(curlx_sltoui(timeout/1000L)); + } + +#else +#ifndef CURLRES_ASYNCH + if(timeoutms) + infof(data, "timeout on name lookup is not supported"); +#else + (void)timeoutms; /* timeoutms not used with an async resolver */ +#endif +#endif /* USE_ALARM_TIMEOUT */ + + /* Perform the actual name resolution. This might be interrupted by an + * alarm if it takes too long. + */ + rc = Curl_resolv(data, hostname, port, TRUE, entry); + +#ifdef USE_ALARM_TIMEOUT +clean_up: + + if(!prev_alarm) + /* deactivate a possibly active alarm before uninstalling the handler */ + alarm(0); + +#ifdef HAVE_SIGACTION + if(keep_copysig) { + /* we got a struct as it looked before, now put that one back nice + and clean */ + sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */ + } +#else +#ifdef HAVE_SIGNAL + /* restore the previous SIGALRM handler */ + signal(SIGALRM, keep_sigact); +#endif +#endif /* HAVE_SIGACTION */ + + curl_simple_lock_unlock(&curl_jmpenv_lock); + + /* switch back the alarm() to either zero or to what it was before minus + the time we spent until now! */ + if(prev_alarm) { + /* there was an alarm() set before us, now put it back */ + timediff_t elapsed_secs = Curl_timediff(Curl_now(), + data->conn->created) / 1000; + + /* the alarm period is counted in even number of seconds */ + unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs); + + if(!alarm_set || + ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) { + /* if the alarm time-left reached zero or turned "negative" (counted + with unsigned values), we should fire off a SIGALRM here, but we + will not, and zero would be to switch it off so we never set it to + less than 1! */ + alarm(1); + rc = CURLRESOLV_TIMEDOUT; + failf(data, "Previous alarm fired off"); + } + else + alarm((unsigned int)alarm_set); + } +#endif /* USE_ALARM_TIMEOUT */ + + return rc; +} + +/* + * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been + * made, the struct may be destroyed due to pruning. It is important that only + * one unlock is made for each Curl_resolv() call. + * + * May be called with 'data' == NULL for global cache. + */ +void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns) +{ + if(data && data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + freednsentry(dns); + + if(data && data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); +} + +/* + * File-internal: release cache dns entry reference, free if inuse drops to 0 + */ +static void freednsentry(void *freethis) +{ + struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis; + DEBUGASSERT(dns && (dns->inuse>0)); + + dns->inuse--; + if(dns->inuse == 0) { + Curl_freeaddrinfo(dns->addr); +#ifdef USE_HTTPSRR + if(dns->hinfo) { + if(dns->hinfo->target) + free(dns->hinfo->target); + if(dns->hinfo->alpns) + free(dns->hinfo->alpns); + if(dns->hinfo->ipv4hints) + free(dns->hinfo->ipv4hints); + if(dns->hinfo->echconfiglist) + free(dns->hinfo->echconfiglist); + if(dns->hinfo->ipv6hints) + free(dns->hinfo->ipv6hints); + if(dns->hinfo->val) + free(dns->hinfo->val); + free(dns->hinfo); + } +#endif + free(dns); + } +} + +/* + * Curl_init_dnscache() inits a new DNS cache. + */ +void Curl_init_dnscache(struct Curl_hash *hash, size_t size) +{ + Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare, + freednsentry); +} + +/* + * Curl_hostcache_clean() + * + * This _can_ be called with 'data' == NULL but then of course no locking + * can be done! + */ + +void Curl_hostcache_clean(struct Curl_easy *data, + struct Curl_hash *hash) +{ + if(data && data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + Curl_hash_clean(hash); + + if(data && data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); +} + + +CURLcode Curl_loadhostpairs(struct Curl_easy *data) +{ + struct curl_slist *hostp; + char *host_end; + + /* Default is no wildcard found */ + data->state.wildcard_resolve = false; + + for(hostp = data->state.resolve; hostp; hostp = hostp->next) { + char entry_id[MAX_HOSTCACHE_LEN]; + if(!hostp->data) + continue; + if(hostp->data[0] == '-') { + unsigned long num = 0; + size_t entry_len; + size_t hlen = 0; + host_end = strchr(&hostp->data[1], ':'); + + if(host_end) { + hlen = host_end - &hostp->data[1]; + num = strtoul(++host_end, NULL, 10); + if(!hlen || (num > 0xffff)) + host_end = NULL; + } + if(!host_end) { + infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'", + hostp->data); + continue; + } + /* Create an entry id, based upon the hostname and port */ + entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num, + entry_id, sizeof(entry_id)); + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + /* delete entry, ignore if it did not exist */ + Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + } + else { + struct Curl_dns_entry *dns; + struct Curl_addrinfo *head = NULL, *tail = NULL; + size_t entry_len; + char address[64]; +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + char *addresses = NULL; +#endif + char *addr_begin; + char *addr_end; + char *port_ptr; + int port = 0; + char *end_ptr; + bool permanent = TRUE; + unsigned long tmp_port; + bool error = true; + char *host_begin = hostp->data; + size_t hlen = 0; + + if(host_begin[0] == '+') { + host_begin++; + permanent = FALSE; + } + host_end = strchr(host_begin, ':'); + if(!host_end) + goto err; + hlen = host_end - host_begin; + + port_ptr = host_end + 1; + tmp_port = strtoul(port_ptr, &end_ptr, 10); + if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':') + goto err; + + port = (int)tmp_port; +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + addresses = end_ptr + 1; +#endif + + while(*end_ptr) { + size_t alen; + struct Curl_addrinfo *ai; + + addr_begin = end_ptr + 1; + addr_end = strchr(addr_begin, ','); + if(!addr_end) + addr_end = addr_begin + strlen(addr_begin); + end_ptr = addr_end; + + /* allow IP(v6) address within [brackets] */ + if(*addr_begin == '[') { + if(addr_end == addr_begin || *(addr_end - 1) != ']') + goto err; + ++addr_begin; + --addr_end; + } + + alen = addr_end - addr_begin; + if(!alen) + continue; + + if(alen >= sizeof(address)) + goto err; + + memcpy(address, addr_begin, alen); + address[alen] = '\0'; + +#ifndef USE_IPV6 + if(strchr(address, ':')) { + infof(data, "Ignoring resolve address '%s', missing IPv6 support.", + address); + continue; + } +#endif + + ai = Curl_str2addr(address, port); + if(!ai) { + infof(data, "Resolve address '%s' found illegal", address); + goto err; + } + + if(tail) { + tail->ai_next = ai; + tail = tail->ai_next; + } + else { + head = tail = ai; + } + } + + if(!head) + goto err; + + error = false; +err: + if(error) { + failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'", + hostp->data); + Curl_freeaddrinfo(head); + return CURLE_SETOPT_OPTION_SYNTAX; + } + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_hostcache_id(host_begin, hlen, port, + entry_id, sizeof(entry_id)); + + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); + + if(dns) { + infof(data, "RESOLVE %.*s:%d - old addresses discarded", + (int)hlen, host_begin, port); + /* delete old entry, there are two reasons for this + 1. old entry may have different addresses. + 2. even if entry with correct addresses is already in the cache, + but if it is close to expire, then by the time next http + request is made, it can get expired and pruned because old + entry is not necessarily marked as permanent. + 3. when adding a non-permanent entry, we want it to remove and + replace an existing permanent entry. + 4. when adding a non-permanent entry, we want it to get a "fresh" + timeout that starts _now_. */ + + Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); + } + + /* put this new host in the cache */ + dns = Curl_cache_addr(data, head, host_begin, hlen, port); + if(dns) { + if(permanent) + dns->timestamp = 0; /* mark as permanent */ + /* release the returned reference; the cache itself will keep the + * entry alive: */ + dns->inuse--; + } + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + if(!dns) { + Curl_freeaddrinfo(head); + return CURLE_OUT_OF_MEMORY; + } +#ifndef CURL_DISABLE_VERBOSE_STRINGS + infof(data, "Added %.*s:%d:%s to DNS cache%s", + (int)hlen, host_begin, port, addresses, + permanent ? "" : " (non-permanent)"); +#endif + + /* Wildcard hostname */ + if((hlen == 1) && (host_begin[0] == '*')) { + infof(data, "RESOLVE *:%d using wildcard", port); + data->state.wildcard_resolve = true; + } + } + } + data->state.resolve = NULL; /* dealt with now */ + + return CURLE_OK; +} + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void show_resolve_info(struct Curl_easy *data, + struct Curl_dns_entry *dns) +{ + struct Curl_addrinfo *a; + CURLcode result = CURLE_OK; +#ifdef CURLRES_IPV6 + struct dynbuf out[2]; +#else + struct dynbuf out[1]; +#endif + DEBUGASSERT(data); + DEBUGASSERT(dns); + + if(!data->set.verbose || + /* ignore no name or numerical IP addresses */ + !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) + return; + + a = dns->addr; + + infof(data, "Host %s:%d was resolved.", + (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport); + + Curl_dyn_init(&out[0], 1024); +#ifdef CURLRES_IPV6 + Curl_dyn_init(&out[1], 1024); +#endif + + while(a) { + if( +#ifdef CURLRES_IPV6 + a->ai_family == PF_INET6 || +#endif + a->ai_family == PF_INET) { + char buf[MAX_IPADR_LEN]; + struct dynbuf *d = &out[(a->ai_family != PF_INET)]; + Curl_printable_address(a, buf, sizeof(buf)); + if(Curl_dyn_len(d)) + result = Curl_dyn_addn(d, ", ", 2); + if(!result) + result = Curl_dyn_add(d, buf); + if(result) { + infof(data, "too many IP, cannot show"); + goto fail; + } + } + a = a->ai_next; + } + +#ifdef CURLRES_IPV6 + infof(data, "IPv6: %s", + (Curl_dyn_len(&out[1]) ? Curl_dyn_ptr(&out[1]) : "(none)")); +#endif + infof(data, "IPv4: %s", + (Curl_dyn_len(&out[0]) ? Curl_dyn_ptr(&out[0]) : "(none)")); + +fail: + Curl_dyn_free(&out[0]); +#ifdef CURLRES_IPV6 + Curl_dyn_free(&out[1]); +#endif +} +#endif + +CURLcode Curl_resolv_check(struct Curl_easy *data, + struct Curl_dns_entry **dns) +{ + CURLcode result; +#if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH) + (void)data; + (void)dns; +#endif +#ifndef CURL_DISABLE_DOH + if(data->conn->bits.doh) { + result = Curl_doh_is_resolved(data, dns); + } + else +#endif + result = Curl_resolver_is_resolved(data, dns); + if(*dns) + show_resolve_info(data, *dns); + return result; +} + +int Curl_resolv_getsock(struct Curl_easy *data, + curl_socket_t *socks) +{ +#ifdef CURLRES_ASYNCH +#ifndef CURL_DISABLE_DOH + if(data->conn->bits.doh) + /* nothing to wait for during DoH resolve, those handles have their own + sockets */ + return GETSOCK_BLANK; +#endif + return Curl_resolver_getsock(data, socks); +#else + (void)data; + (void)socks; + return GETSOCK_BLANK; +#endif +} + +/* Call this function after Curl_connect() has returned async=TRUE and + then a successful name resolve has been received. + + Note: this function disconnects and frees the conn data in case of + resolve failure */ +CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done) +{ + CURLcode result; + struct connectdata *conn = data->conn; + +#ifdef USE_CURL_ASYNC + if(data->state.async.dns) { + conn->dns_entry = data->state.async.dns; + data->state.async.dns = NULL; + } +#endif + + result = Curl_setup_conn(data, protocol_done); + + if(result) { + Curl_detach_connection(data); + Curl_conncache_remove_conn(data, conn, TRUE); + Curl_disconnect(data, conn, TRUE); + } + return result; +} + +/* + * Curl_resolver_error() calls failf() with the appropriate message after a + * resolve error + */ + +#ifdef USE_CURL_ASYNC +CURLcode Curl_resolver_error(struct Curl_easy *data) +{ + const char *host_or_proxy; + CURLcode result; + +#ifndef CURL_DISABLE_PROXY + struct connectdata *conn = data->conn; + if(conn->bits.httpproxy) { + host_or_proxy = "proxy"; + result = CURLE_COULDNT_RESOLVE_PROXY; + } + else +#endif + { + host_or_proxy = "host"; + result = CURLE_COULDNT_RESOLVE_HOST; + } + + failf(data, "Could not resolve %s: %s", host_or_proxy, + data->state.async.hostname); + + return result; +} +#endif /* USE_CURL_ASYNC */ diff --git a/third_party/curl/lib/hostip.h b/third_party/curl/lib/hostip.h new file mode 100644 index 0000000000..c85a688db6 --- /dev/null +++ b/third_party/curl/lib/hostip.h @@ -0,0 +1,266 @@ +#ifndef HEADER_CURL_HOSTIP_H +#define HEADER_CURL_HOSTIP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "hash.h" +#include "curl_addrinfo.h" +#include "timeval.h" /* for timediff_t */ +#include "asyn.h" + +#include + +#ifdef USE_HTTPSRR +# include +#endif + +/* Allocate enough memory to hold the full name information structs and + * everything. OSF1 is known to require at least 8872 bytes. The buffer + * required for storing all possible aliases and IP numbers is according to + * Stevens' Unix Network Programming 2nd edition, p. 304: 8192 bytes! + */ +#define CURL_HOSTENT_SIZE 9000 + +#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this + many seconds for a name resolve */ + +#define CURL_ASYNC_SUCCESS CURLE_OK + +struct addrinfo; +struct hostent; +struct Curl_easy; +struct connectdata; + +/* + * Curl_global_host_cache_init() initializes and sets up a global DNS cache. + * Global DNS cache is general badness. Do not use. This will be removed in + * a future version. Use the share interface instead! + * + * Returns a struct Curl_hash pointer on success, NULL on failure. + */ +struct Curl_hash *Curl_global_host_cache_init(void); + +#ifdef USE_HTTPSRR + +#define CURL_MAXLEN_host_name 253 + +struct Curl_https_rrinfo { + size_t len; /* raw encoded length */ + unsigned char *val; /* raw encoded octets */ + /* + * fields from HTTPS RR, with the mandatory fields + * first (priority, target), then the others in the + * order of the keytag numbers defined at + * https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2 + */ + uint16_t priority; + char *target; + char *alpns; /* keytag = 1 */ + bool no_def_alpn; /* keytag = 2 */ + /* + * we do not support ports (keytag = 3) as we do not support + * port-switching yet + */ + unsigned char *ipv4hints; /* keytag = 4 */ + size_t ipv4hints_len; + unsigned char *echconfiglist; /* keytag = 5 */ + size_t echconfiglist_len; + unsigned char *ipv6hints; /* keytag = 6 */ + size_t ipv6hints_len; +}; +#endif + +struct Curl_dns_entry { + struct Curl_addrinfo *addr; +#ifdef USE_HTTPSRR + struct Curl_https_rrinfo *hinfo; +#endif + /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */ + time_t timestamp; + /* use-counter, use Curl_resolv_unlock to release reference */ + long inuse; + /* hostname port number that resolved to addr. */ + int hostport; + /* hostname that resolved to addr. may be NULL (unix domain sockets). */ + char hostname[1]; +}; + +bool Curl_host_is_ipnum(const char *hostname); + +/* + * Curl_resolv() returns an entry with the info for the specified host + * and port. + * + * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after + * use, or we will leak memory! + */ +/* return codes */ +enum resolve_t { + CURLRESOLV_TIMEDOUT = -2, + CURLRESOLV_ERROR = -1, + CURLRESOLV_RESOLVED = 0, + CURLRESOLV_PENDING = 1 +}; +enum resolve_t Curl_resolv(struct Curl_easy *data, + const char *hostname, + int port, + bool allowDOH, + struct Curl_dns_entry **dnsentry); +enum resolve_t Curl_resolv_timeout(struct Curl_easy *data, + const char *hostname, int port, + struct Curl_dns_entry **dnsentry, + timediff_t timeoutms); + +#ifdef USE_IPV6 +/* + * Curl_ipv6works() returns TRUE if IPv6 seems to work. + */ +bool Curl_ipv6works(struct Curl_easy *data); +#else +#define Curl_ipv6works(x) FALSE +#endif + +/* + * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've + * been set and returns TRUE if they are OK. + */ +bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn); + + +/* + * Curl_getaddrinfo() is the generic low-level name resolve API within this + * source file. There are several versions of this function - for different + * name resolve layers (selected at build-time). They all take this same set + * of arguments + */ +struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, + const char *hostname, + int port, + int *waitp); + + +/* unlock a previously resolved dns entry */ +void Curl_resolv_unlock(struct Curl_easy *data, + struct Curl_dns_entry *dns); + +/* init a new dns cache */ +void Curl_init_dnscache(struct Curl_hash *hash, size_t hashsize); + +/* prune old entries from the DNS cache */ +void Curl_hostcache_prune(struct Curl_easy *data); + +/* IPv4 threadsafe resolve function used for synch and asynch builds */ +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port); + +CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_connect); + +/* + * Curl_addrinfo_callback() is used when we build with any asynch specialty. + * Handles end of async request processing. Inserts ai into hostcache when + * status is CURL_ASYNC_SUCCESS. Twiddles fields in conn to indicate async + * request completed whether successful or failed. + */ +CURLcode Curl_addrinfo_callback(struct Curl_easy *data, + int status, + struct Curl_addrinfo *ai); + +/* + * Curl_printable_address() returns a printable version of the 1st address + * given in the 'ip' argument. The result will be stored in the buf that is + * bufsize bytes big. + */ +void Curl_printable_address(const struct Curl_addrinfo *ip, + char *buf, size_t bufsize); + +/* + * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache. + * + * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. + * + * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after + * use, or we will leak memory! + */ +struct Curl_dns_entry * +Curl_fetch_addr(struct Curl_easy *data, + const char *hostname, + int port); + +/* + * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. + * + * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. + */ +struct Curl_dns_entry * +Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr, + const char *hostname, size_t hostlen, int port); + +#ifndef INADDR_NONE +#define CURL_INADDR_NONE (in_addr_t) ~0 +#else +#define CURL_INADDR_NONE INADDR_NONE +#endif + +/* + * Function provided by the resolver backend to set DNS servers to use. + */ +CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers); + +/* + * Function provided by the resolver backend to set + * outgoing interface to use for DNS requests + */ +CURLcode Curl_set_dns_interface(struct Curl_easy *data, + const char *interf); + +/* + * Function provided by the resolver backend to set + * local IPv4 address to use as source address for DNS requests + */ +CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, + const char *local_ip4); + +/* + * Function provided by the resolver backend to set + * local IPv6 address to use as source address for DNS requests + */ +CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, + const char *local_ip6); + +/* + * Clean off entries from the cache + */ +void Curl_hostcache_clean(struct Curl_easy *data, struct Curl_hash *hash); + +/* + * Populate the cache with specified entries from CURLOPT_RESOLVE. + */ +CURLcode Curl_loadhostpairs(struct Curl_easy *data); +CURLcode Curl_resolv_check(struct Curl_easy *data, + struct Curl_dns_entry **dns); +int Curl_resolv_getsock(struct Curl_easy *data, + curl_socket_t *socks); + +CURLcode Curl_resolver_error(struct Curl_easy *data); +#endif /* HEADER_CURL_HOSTIP_H */ diff --git a/third_party/curl/lib/hostip4.c b/third_party/curl/lib/hostip4.c new file mode 100644 index 0000000000..8da809db6f --- /dev/null +++ b/third_party/curl/lib/hostip4.c @@ -0,0 +1,301 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +/*********************************************************************** + * Only for plain IPv4 builds + **********************************************************************/ +#ifdef CURLRES_IPV4 /* plain IPv4 code coming up */ + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "hash.h" +#include "share.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" + +/* + * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've + * been set and returns TRUE if they are OK. + */ +bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn) +{ + (void)data; + if(conn->ip_version == CURL_IPRESOLVE_V6) + /* An IPv6 address was requested and we cannot get/use one */ + return FALSE; + + return TRUE; /* OK, proceed */ +} + +#ifdef CURLRES_SYNCH + +/* + * Curl_getaddrinfo() - the IPv4 synchronous version. + * + * The original code to this function was from the Dancer source code, written + * by Bjorn Reese, it has since been patched and modified considerably. + * + * gethostbyname_r() is the thread-safe version of the gethostbyname() + * function. When we build for plain IPv4, we attempt to use this + * function. There are _three_ different gethostbyname_r() versions, and we + * detect which one this platform supports in the configure script and set up + * the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or + * HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME + * has the corresponding rules. This is primarily on *nix. Note that some unix + * flavours have thread-safe versions of the plain gethostbyname() etc. + * + */ +struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, + const char *hostname, + int port, + int *waitp) +{ + struct Curl_addrinfo *ai = NULL; + +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)data; +#endif + + *waitp = 0; /* synchronous response only */ + + ai = Curl_ipv4_resolve_r(hostname, port); + if(!ai) + infof(data, "Curl_ipv4_resolve_r failed for %s", hostname); + + return ai; +} +#endif /* CURLRES_SYNCH */ +#endif /* CURLRES_IPV4 */ + +#if defined(CURLRES_IPV4) && \ + !defined(CURLRES_ARES) && !defined(CURLRES_AMIGA) + +/* + * Curl_ipv4_resolve_r() - ipv4 threadsafe resolver function. + * + * This is used for both synchronous and asynchronous resolver builds, + * implying that only threadsafe code and function calls may be used. + * + */ +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, + int port) +{ +#if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) && \ + defined(HAVE_GETHOSTBYNAME_R_3) + int res; +#endif + struct Curl_addrinfo *ai = NULL; + struct hostent *h = NULL; + struct hostent *buf = NULL; + +#if defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE) + struct addrinfo hints; + char sbuf[12]; + char *sbufptr = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + if(port) { + msnprintf(sbuf, sizeof(sbuf), "%d", port); + sbufptr = sbuf; + } + + (void)Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &ai); + +#elif defined(HAVE_GETHOSTBYNAME_R) + /* + * gethostbyname_r() is the preferred resolve function for many platforms. + * Since there are three different versions of it, the following code is + * somewhat #ifdef-ridden. + */ + int h_errnop; + + buf = calloc(1, CURL_HOSTENT_SIZE); + if(!buf) + return NULL; /* major failure */ + /* + * The clearing of the buffer is a workaround for a gethostbyname_r bug in + * qnx nto and it is also _required_ for some of these functions on some + * platforms. + */ + +#if defined(HAVE_GETHOSTBYNAME_R_5) + /* Solaris, IRIX and more */ + h = gethostbyname_r(hostname, + (struct hostent *)buf, + (char *)buf + sizeof(struct hostent), + CURL_HOSTENT_SIZE - sizeof(struct hostent), + &h_errnop); + + /* If the buffer is too small, it returns NULL and sets errno to + * ERANGE. The errno is thread safe if this is compiled with + * -D_REENTRANT as then the 'errno' variable is a macro defined to get + * used properly for threads. + */ + + if(h) { + ; + } + else +#elif defined(HAVE_GETHOSTBYNAME_R_6) + /* Linux */ + + (void)gethostbyname_r(hostname, + (struct hostent *)buf, + (char *)buf + sizeof(struct hostent), + CURL_HOSTENT_SIZE - sizeof(struct hostent), + &h, /* DIFFERENCE */ + &h_errnop); + /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a + * sudden this function returns EAGAIN if the given buffer size is too + * small. Previous versions are known to return ERANGE for the same + * problem. + * + * This would not be such a big problem if older versions would not + * sometimes return EAGAIN on a common failure case. Alas, we cannot + * assume that EAGAIN *or* ERANGE means ERANGE for any given version of + * glibc. + * + * For now, we do that and thus we may call the function repeatedly and + * fail for older glibc versions that return EAGAIN, until we run out of + * buffer size (step_size grows beyond CURL_HOSTENT_SIZE). + * + * If anyone has a better fix, please tell us! + * + * ------------------------------------------------------------------- + * + * On October 23rd 2003, Dan C dug up more details on the mysteries of + * gethostbyname_r() in glibc: + * + * In glibc 2.2.5 the interface is different (this has also been + * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I cannot + * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32 + * (shipped/upgraded by Redhat 7.2) do not show this behavior! + * + * In this "buggy" version, the return code is -1 on error and 'errno' + * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a + * thread-safe variable. + */ + + if(!h) /* failure */ +#elif defined(HAVE_GETHOSTBYNAME_R_3) + /* AIX, Digital Unix/Tru64, HPUX 10, more? */ + + /* For AIX 4.3 or later, we do not use gethostbyname_r() at all, because of + * the plain fact that it does not return unique full buffers on each + * call, but instead several of the pointers in the hostent structs will + * point to the same actual data! This have the unfortunate down-side that + * our caching system breaks down horribly. Luckily for us though, AIX 4.3 + * and more recent versions have a "completely thread-safe"[*] libc where + * all the data is stored in thread-specific memory areas making calls to + * the plain old gethostbyname() work fine even for multi-threaded + * programs. + * + * This AIX 4.3 or later detection is all made in the configure script. + * + * Troels Walsted Hansen helped us work this out on March 3rd, 2003. + * + * [*] = much later we have found out that it is not at all "completely + * thread-safe", but at least the gethostbyname() function is. + */ + + if(CURL_HOSTENT_SIZE >= + (sizeof(struct hostent) + sizeof(struct hostent_data))) { + + /* August 22nd, 2000: Albert Chin-A-Young brought an updated version + * that should work! September 20: Richard Prescott worked on the buffer + * size dilemma. + */ + + res = gethostbyname_r(hostname, + (struct hostent *)buf, + (struct hostent_data *)((char *)buf + + sizeof(struct hostent))); + h_errnop = SOCKERRNO; /* we do not deal with this, but set it anyway */ + } + else + res = -1; /* failure, too smallish buffer size */ + + if(!res) { /* success */ + + h = buf; /* result expected in h */ + + /* This is the worst kind of the different gethostbyname_r() interfaces. + * Since we do not know how big buffer this particular lookup required, + * we cannot realloc down the huge alloc without doing closer analysis of + * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every + * name lookup. Fixing this would require an extra malloc() and then + * calling Curl_addrinfo_copy() that subsequent realloc()s down the new + * memory area to the actually used amount. + */ + } + else +#endif /* HAVE_...BYNAME_R_5 || HAVE_...BYNAME_R_6 || HAVE_...BYNAME_R_3 */ + { + h = NULL; /* set return code to NULL */ + free(buf); + } +#else /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) || + HAVE_GETHOSTBYNAME_R */ + /* + * Here is code for platforms that do not have a thread safe + * getaddrinfo() nor gethostbyname_r() function or for which + * gethostbyname() is the preferred one. + */ + h = gethostbyname((void *)hostname); +#endif /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) || + HAVE_GETHOSTBYNAME_R */ + + if(h) { + ai = Curl_he2ai(h, port); + + if(buf) /* used a *_r() function */ + free(buf); + } + + return ai; +} +#endif /* defined(CURLRES_IPV4) && !defined(CURLRES_ARES) && + !defined(CURLRES_AMIGA) */ diff --git a/third_party/curl/lib/hostip6.c b/third_party/curl/lib/hostip6.c new file mode 100644 index 0000000000..18969a7a7d --- /dev/null +++ b/third_party/curl/lib/hostip6.c @@ -0,0 +1,157 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +/*********************************************************************** + * Only for IPv6-enabled builds + **********************************************************************/ +#ifdef CURLRES_IPV6 + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "hash.h" +#include "share.h" +#include "url.h" +#include "inet_pton.h" +#include "connect.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've + * been set and returns TRUE if they are OK. + */ +bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn) +{ + if(conn->ip_version == CURL_IPRESOLVE_V6) + return Curl_ipv6works(data); + + return TRUE; +} + +#if defined(CURLRES_SYNCH) + +#ifdef DEBUG_ADDRINFO +static void dump_addrinfo(const struct Curl_addrinfo *ai) +{ + printf("dump_addrinfo:\n"); + for(; ai; ai = ai->ai_next) { + char buf[INET6_ADDRSTRLEN]; + printf(" fam %2d, CNAME %s, ", + ai->ai_family, ai->ai_canonname ? ai->ai_canonname : ""); + Curl_printable_address(ai, buf, sizeof(buf)); + printf("%s\n", buf); + } +} +#else +#define dump_addrinfo(x) Curl_nop_stmt +#endif + +/* + * Curl_getaddrinfo() when built IPv6-enabled (non-threading and + * non-ares version). + * + * Returns name information about the given hostname and port number. If + * successful, the 'addrinfo' is returned and the fourth argument will point + * to memory we need to free after use. That memory *MUST* be freed with + * Curl_freeaddrinfo(), nothing else. + */ +struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, + const char *hostname, + int port, + int *waitp) +{ + struct addrinfo hints; + struct Curl_addrinfo *res; + int error; + char sbuf[12]; + char *sbufptr = NULL; +#ifndef USE_RESOLVE_ON_IPS + char addrbuf[128]; +#endif + int pf = PF_INET; + + *waitp = 0; /* synchronous response only */ + + if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) + /* The stack seems to be IPv6-enabled */ + pf = PF_UNSPEC; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = pf; + hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ? + SOCK_STREAM : SOCK_DGRAM; + +#ifndef USE_RESOLVE_ON_IPS + /* + * The AI_NUMERICHOST must not be set to get synthesized IPv6 address from + * an IPv4 address on iOS and Mac OS X. + */ + if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) || + (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) { + /* the given address is numerical only, prevent a reverse lookup */ + hints.ai_flags = AI_NUMERICHOST; + } +#endif + + if(port) { + msnprintf(sbuf, sizeof(sbuf), "%d", port); + sbufptr = sbuf; + } + + error = Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &res); + if(error) { + infof(data, "getaddrinfo(3) failed for %s:%d", hostname, port); + return NULL; + } + + if(port) { + Curl_addrinfo_set_port(res, port); + } + + dump_addrinfo(res); + + return res; +} +#endif /* CURLRES_SYNCH */ + +#endif /* CURLRES_IPV6 */ diff --git a/third_party/curl/lib/hostsyn.c b/third_party/curl/lib/hostsyn.c new file mode 100644 index 0000000000..ca8b0758c4 --- /dev/null +++ b/third_party/curl/lib/hostsyn.c @@ -0,0 +1,104 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +/*********************************************************************** + * Only for builds using synchronous name resolves + **********************************************************************/ +#ifdef CURLRES_SYNCH + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "hash.h" +#include "share.h" +#include "url.h" +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* + * Function provided by the resolver backend to set DNS servers to use. + */ +CURLcode Curl_set_dns_servers(struct Curl_easy *data, + char *servers) +{ + (void)data; + (void)servers; + return CURLE_NOT_BUILT_IN; + +} + +/* + * Function provided by the resolver backend to set + * outgoing interface to use for DNS requests + */ +CURLcode Curl_set_dns_interface(struct Curl_easy *data, + const char *interf) +{ + (void)data; + (void)interf; + return CURLE_NOT_BUILT_IN; +} + +/* + * Function provided by the resolver backend to set + * local IPv4 address to use as source address for DNS requests + */ +CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, + const char *local_ip4) +{ + (void)data; + (void)local_ip4; + return CURLE_NOT_BUILT_IN; +} + +/* + * Function provided by the resolver backend to set + * local IPv6 address to use as source address for DNS requests + */ +CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, + const char *local_ip6) +{ + (void)data; + (void)local_ip6; + return CURLE_NOT_BUILT_IN; +} + +#endif /* truly sync */ diff --git a/third_party/curl/lib/hsts.c b/third_party/curl/lib/hsts.c new file mode 100644 index 0000000000..8cd77ae3cf --- /dev/null +++ b/third_party/curl/lib/hsts.c @@ -0,0 +1,576 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* + * The Strict-Transport-Security header is defined in RFC 6797: + * https://datatracker.ietf.org/doc/html/rfc6797 + */ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) +#include +#include "urldata.h" +#include "llist.h" +#include "hsts.h" +#include "curl_get_line.h" +#include "strcase.h" +#include "sendf.h" +#include "strtoofft.h" +#include "parsedate.h" +#include "fopen.h" +#include "rename.h" +#include "share.h" +#include "strdup.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define MAX_HSTS_LINE 4095 +#define MAX_HSTS_HOSTLEN 256 +#define MAX_HSTS_HOSTLENSTR "256" +#define MAX_HSTS_DATELEN 64 +#define MAX_HSTS_DATELENSTR "64" +#define UNLIMITED "unlimited" + +#if defined(DEBUGBUILD) || defined(UNITTESTS) +/* to play well with debug builds, we can *set* a fixed time this will + return */ +time_t deltatime; /* allow for "adjustments" for unit test purposes */ +static time_t hsts_debugtime(void *unused) +{ + char *timestr = getenv("CURL_TIME"); + (void)unused; + if(timestr) { + curl_off_t val; + (void)curlx_strtoofft(timestr, NULL, 10, &val); + + val += (curl_off_t)deltatime; + return (time_t)val; + } + return time(NULL); +} +#undef time +#define time(x) hsts_debugtime(x) +#endif + +struct hsts *Curl_hsts_init(void) +{ + struct hsts *h = calloc(1, sizeof(struct hsts)); + if(h) { + Curl_llist_init(&h->list, NULL); + } + return h; +} + +static void hsts_free(struct stsentry *e) +{ + free((char *)e->host); + free(e); +} + +void Curl_hsts_cleanup(struct hsts **hp) +{ + struct hsts *h = *hp; + if(h) { + struct Curl_llist_element *e; + struct Curl_llist_element *n; + for(e = h->list.head; e; e = n) { + struct stsentry *sts = e->ptr; + n = e->next; + hsts_free(sts); + } + free(h->filename); + free(h); + *hp = NULL; + } +} + +static CURLcode hsts_create(struct hsts *h, + const char *hostname, + bool subdomains, + curl_off_t expires) +{ + size_t hlen; + DEBUGASSERT(h); + DEBUGASSERT(hostname); + + hlen = strlen(hostname); + if(hlen && (hostname[hlen - 1] == '.')) + /* strip off any trailing dot */ + --hlen; + if(hlen) { + char *duphost; + struct stsentry *sts = calloc(1, sizeof(struct stsentry)); + if(!sts) + return CURLE_OUT_OF_MEMORY; + + duphost = Curl_memdup0(hostname, hlen); + if(!duphost) { + free(sts); + return CURLE_OUT_OF_MEMORY; + } + + sts->host = duphost; + sts->expires = expires; + sts->includeSubDomains = subdomains; + Curl_llist_append(&h->list, sts, &sts->node); + } + return CURLE_OK; +} + +CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, + const char *header) +{ + const char *p = header; + curl_off_t expires = 0; + bool gotma = FALSE; + bool gotinc = FALSE; + bool subdomains = FALSE; + struct stsentry *sts; + time_t now = time(NULL); + + if(Curl_host_is_ipnum(hostname)) + /* "explicit IP address identification of all forms is excluded." + / RFC 6797 */ + return CURLE_OK; + + do { + while(*p && ISBLANK(*p)) + p++; + if(strncasecompare("max-age=", p, 8)) { + bool quoted = FALSE; + CURLofft offt; + char *endp; + + if(gotma) + return CURLE_BAD_FUNCTION_ARGUMENT; + + p += 8; + while(*p && ISBLANK(*p)) + p++; + if(*p == '\"') { + p++; + quoted = TRUE; + } + offt = curlx_strtoofft(p, &endp, 10, &expires); + if(offt == CURL_OFFT_FLOW) + expires = CURL_OFF_T_MAX; + else if(offt) + /* invalid max-age */ + return CURLE_BAD_FUNCTION_ARGUMENT; + p = endp; + if(quoted) { + if(*p != '\"') + return CURLE_BAD_FUNCTION_ARGUMENT; + p++; + } + gotma = TRUE; + } + else if(strncasecompare("includesubdomains", p, 17)) { + if(gotinc) + return CURLE_BAD_FUNCTION_ARGUMENT; + subdomains = TRUE; + p += 17; + gotinc = TRUE; + } + else { + /* unknown directive, do a lame attempt to skip */ + while(*p && (*p != ';')) + p++; + } + + while(*p && ISBLANK(*p)) + p++; + if(*p == ';') + p++; + } while(*p); + + if(!gotma) + /* max-age is mandatory */ + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(!expires) { + /* remove the entry if present verbatim (without subdomain match) */ + sts = Curl_hsts(h, hostname, FALSE); + if(sts) { + Curl_llist_remove(&h->list, &sts->node, NULL); + hsts_free(sts); + } + return CURLE_OK; + } + + if(CURL_OFF_T_MAX - now < expires) + /* would overflow, use maximum value */ + expires = CURL_OFF_T_MAX; + else + expires += now; + + /* check if it already exists */ + sts = Curl_hsts(h, hostname, FALSE); + if(sts) { + /* just update these fields */ + sts->expires = expires; + sts->includeSubDomains = subdomains; + } + else + return hsts_create(h, hostname, subdomains, expires); + + return CURLE_OK; +} + +/* + * Return TRUE if the given hostname is currently an HSTS one. + * + * The 'subdomain' argument tells the function if subdomain matching should be + * attempted. + */ +struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, + bool subdomain) +{ + if(h) { + char buffer[MAX_HSTS_HOSTLEN + 1]; + time_t now = time(NULL); + size_t hlen = strlen(hostname); + struct Curl_llist_element *e; + struct Curl_llist_element *n; + + if((hlen > MAX_HSTS_HOSTLEN) || !hlen) + return NULL; + memcpy(buffer, hostname, hlen); + if(hostname[hlen-1] == '.') + /* remove the trailing dot */ + --hlen; + buffer[hlen] = 0; + hostname = buffer; + + for(e = h->list.head; e; e = n) { + struct stsentry *sts = e->ptr; + n = e->next; + if(sts->expires <= now) { + /* remove expired entries */ + Curl_llist_remove(&h->list, &sts->node, NULL); + hsts_free(sts); + continue; + } + if(subdomain && sts->includeSubDomains) { + size_t ntail = strlen(sts->host); + if(ntail < hlen) { + size_t offs = hlen - ntail; + if((hostname[offs-1] == '.') && + strncasecompare(&hostname[offs], sts->host, ntail)) + return sts; + } + } + if(strcasecompare(hostname, sts->host)) + return sts; + } + } + return NULL; /* no match */ +} + +/* + * Send this HSTS entry to the write callback. + */ +static CURLcode hsts_push(struct Curl_easy *data, + struct curl_index *i, + struct stsentry *sts, + bool *stop) +{ + struct curl_hstsentry e; + CURLSTScode sc; + struct tm stamp; + CURLcode result; + + e.name = (char *)sts->host; + e.namelen = strlen(sts->host); + e.includeSubDomains = sts->includeSubDomains; + + if(sts->expires != TIME_T_MAX) { + result = Curl_gmtime((time_t)sts->expires, &stamp); + if(result) + return result; + + msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d", + stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, + stamp.tm_hour, stamp.tm_min, stamp.tm_sec); + } + else + strcpy(e.expire, UNLIMITED); + + sc = data->set.hsts_write(data, &e, i, + data->set.hsts_write_userp); + *stop = (sc != CURLSTS_OK); + return sc == CURLSTS_FAIL ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK; +} + +/* + * Write this single hsts entry to a single output line + */ +static CURLcode hsts_out(struct stsentry *sts, FILE *fp) +{ + struct tm stamp; + if(sts->expires != TIME_T_MAX) { + CURLcode result = Curl_gmtime((time_t)sts->expires, &stamp); + if(result) + return result; + fprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n", + sts->includeSubDomains ? ".": "", sts->host, + stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, + stamp.tm_hour, stamp.tm_min, stamp.tm_sec); + } + else + fprintf(fp, "%s%s \"%s\"\n", + sts->includeSubDomains ? ".": "", sts->host, UNLIMITED); + return CURLE_OK; +} + + +/* + * Curl_https_save() writes the HSTS cache to file and callback. + */ +CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, + const char *file) +{ + struct Curl_llist_element *e; + struct Curl_llist_element *n; + CURLcode result = CURLE_OK; + FILE *out; + char *tempstore = NULL; + + if(!h) + /* no cache activated */ + return CURLE_OK; + + /* if no new name is given, use the one we stored from the load */ + if(!file && h->filename) + file = h->filename; + + if((h->flags & CURLHSTS_READONLYFILE) || !file || !file[0]) + /* marked as read-only, no file or zero length filename */ + goto skipsave; + + result = Curl_fopen(data, file, &out, &tempstore); + if(!result) { + fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n" + "# This file was generated by libcurl! Edit at your own risk.\n", + out); + for(e = h->list.head; e; e = n) { + struct stsentry *sts = e->ptr; + n = e->next; + result = hsts_out(sts, out); + if(result) + break; + } + fclose(out); + if(!result && tempstore && Curl_rename(tempstore, file)) + result = CURLE_WRITE_ERROR; + + if(result && tempstore) + unlink(tempstore); + } + free(tempstore); +skipsave: + if(data->set.hsts_write) { + /* if there is a write callback */ + struct curl_index i; /* count */ + i.total = h->list.size; + i.index = 0; + for(e = h->list.head; e; e = n) { + struct stsentry *sts = e->ptr; + bool stop; + n = e->next; + result = hsts_push(data, &i, sts, &stop); + if(result || stop) + break; + i.index++; + } + } + return result; +} + +/* only returns SERIOUS errors */ +static CURLcode hsts_add(struct hsts *h, char *line) +{ + /* Example lines: + example.com "20191231 10:00:00" + .example.net "20191231 10:00:00" + */ + char host[MAX_HSTS_HOSTLEN + 1]; + char date[MAX_HSTS_DATELEN + 1]; + int rc; + + rc = sscanf(line, + "%" MAX_HSTS_HOSTLENSTR "s \"%" MAX_HSTS_DATELENSTR "[^\"]\"", + host, date); + if(2 == rc) { + time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) : + TIME_T_MAX; + CURLcode result = CURLE_OK; + char *p = host; + bool subdomain = FALSE; + struct stsentry *e; + if(p[0] == '.') { + p++; + subdomain = TRUE; + } + /* only add it if not already present */ + e = Curl_hsts(h, p, subdomain); + if(!e) + result = hsts_create(h, p, subdomain, expires); + else { + /* the same hostname, use the largest expire time */ + if(expires > e->expires) + e->expires = expires; + } + if(result) + return result; + } + + return CURLE_OK; +} + +/* + * Load HSTS data from callback. + * + */ +static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) +{ + /* if the HSTS read callback is set, use it */ + if(data->set.hsts_read) { + CURLSTScode sc; + DEBUGASSERT(h); + do { + char buffer[MAX_HSTS_HOSTLEN + 1]; + struct curl_hstsentry e; + e.name = buffer; + e.namelen = sizeof(buffer)-1; + e.includeSubDomains = FALSE; /* default */ + e.expire[0] = 0; + e.name[0] = 0; /* just to make it clean */ + sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp); + if(sc == CURLSTS_OK) { + time_t expires; + CURLcode result; + DEBUGASSERT(e.name[0]); + if(!e.name[0]) + /* bail out if no name was stored */ + return CURLE_BAD_FUNCTION_ARGUMENT; + if(e.expire[0]) + expires = Curl_getdate_capped(e.expire); + else + expires = TIME_T_MAX; /* the end of time */ + result = hsts_create(h, e.name, + /* bitfield to bool conversion: */ + e.includeSubDomains ? TRUE : FALSE, + expires); + if(result) + return result; + } + else if(sc == CURLSTS_FAIL) + return CURLE_ABORTED_BY_CALLBACK; + } while(sc == CURLSTS_OK); + } + return CURLE_OK; +} + +/* + * Load the HSTS cache from the given file. The text based line-oriented file + * format is documented here: https://curl.se/docs/hsts.html + * + * This function only returns error on major problems that prevent hsts + * handling to work completely. It will ignore individual syntactical errors + * etc. + */ +static CURLcode hsts_load(struct hsts *h, const char *file) +{ + CURLcode result = CURLE_OK; + FILE *fp; + + /* we need a private copy of the filename so that the hsts cache file + name survives an easy handle reset */ + free(h->filename); + h->filename = strdup(file); + if(!h->filename) + return CURLE_OUT_OF_MEMORY; + + fp = fopen(file, FOPEN_READTEXT); + if(fp) { + struct dynbuf buf; + Curl_dyn_init(&buf, MAX_HSTS_LINE); + while(Curl_get_line(&buf, fp)) { + char *lineptr = Curl_dyn_ptr(&buf); + while(*lineptr && ISBLANK(*lineptr)) + lineptr++; + /* + * Skip empty or commented lines, since we know the line will have a + * trailing newline from Curl_get_line we can treat length 1 as empty. + */ + if((*lineptr == '#') || strlen(lineptr) <= 1) + continue; + + hsts_add(h, lineptr); + } + Curl_dyn_free(&buf); /* free the line buffer */ + fclose(fp); + } + return result; +} + +/* + * Curl_hsts_loadfile() loads HSTS from file + */ +CURLcode Curl_hsts_loadfile(struct Curl_easy *data, + struct hsts *h, const char *file) +{ + DEBUGASSERT(h); + (void)data; + return hsts_load(h, file); +} + +/* + * Curl_hsts_loadcb() loads HSTS from callback + */ +CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h) +{ + if(h) + return hsts_pull(data, h); + return CURLE_OK; +} + +void Curl_hsts_loadfiles(struct Curl_easy *data) +{ + struct curl_slist *l = data->state.hstslist; + if(l) { + Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE); + + while(l) { + (void)Curl_hsts_loadfile(data, data->hsts, l->data); + l = l->next; + } + Curl_share_unlock(data, CURL_LOCK_DATA_HSTS); + } +} + +#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ diff --git a/third_party/curl/lib/hsts.h b/third_party/curl/lib/hsts.h new file mode 100644 index 0000000000..21e53a3427 --- /dev/null +++ b/third_party/curl/lib/hsts.h @@ -0,0 +1,69 @@ +#ifndef HEADER_CURL_HSTS_H +#define HEADER_CURL_HSTS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) +#include +#include "llist.h" + +#if defined(DEBUGBUILD) || defined(UNITTESTS) +extern time_t deltatime; +#endif + +struct stsentry { + struct Curl_llist_element node; + const char *host; + bool includeSubDomains; + curl_off_t expires; /* the timestamp of this entry's expiry */ +}; + +/* The HSTS cache. Needs to be able to tailmatch hostnames. */ +struct hsts { + struct Curl_llist list; + char *filename; + unsigned int flags; +}; + +struct hsts *Curl_hsts_init(void); +void Curl_hsts_cleanup(struct hsts **hp); +CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, + const char *sts); +struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, + bool subdomain); +CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, + const char *file); +CURLcode Curl_hsts_loadfile(struct Curl_easy *data, + struct hsts *h, const char *file); +CURLcode Curl_hsts_loadcb(struct Curl_easy *data, + struct hsts *h); +void Curl_hsts_loadfiles(struct Curl_easy *data); +#else +#define Curl_hsts_cleanup(x) +#define Curl_hsts_loadcb(x,y) CURLE_OK +#define Curl_hsts_save(x,y,z) +#define Curl_hsts_loadfiles(x) +#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ +#endif /* HEADER_CURL_HSTS_H */ diff --git a/third_party/curl/lib/http.c b/third_party/curl/lib/http.c new file mode 100644 index 0000000000..cb379e7f94 --- /dev/null +++ b/third_party/curl/lib/http.c @@ -0,0 +1,4543 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_HTTP + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef USE_HYPER +#include +#endif + +#include "urldata.h" +#include +#include "transfer.h" +#include "sendf.h" +#include "formdata.h" +#include "mime.h" +#include "progress.h" +#include "curl_base64.h" +#include "cookie.h" +#include "vauth/vauth.h" +#include "vtls/vtls.h" +#include "vquic/vquic.h" +#include "http_digest.h" +#include "http_ntlm.h" +#include "http_negotiate.h" +#include "http_aws_sigv4.h" +#include "url.h" +#include "share.h" +#include "hostip.h" +#include "dynhds.h" +#include "http.h" +#include "headers.h" +#include "select.h" +#include "parsedate.h" /* for the week day and month names */ +#include "strtoofft.h" +#include "multiif.h" +#include "strcase.h" +#include "content_encoding.h" +#include "http_proxy.h" +#include "warnless.h" +#include "http2.h" +#include "cfilters.h" +#include "connect.h" +#include "strdup.h" +#include "altsvc.h" +#include "hsts.h" +#include "ws.h" +#include "c-hyper.h" +#include "curl_ctype.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Forward declarations. + */ + +static bool http_should_fail(struct Curl_easy *data, int httpcode); +static bool http_exp100_is_waiting(struct Curl_easy *data); +static CURLcode http_exp100_add_reader(struct Curl_easy *data); +static void http_exp100_send_anyway(struct Curl_easy *data); + +/* + * HTTP handler interface. + */ +const struct Curl_handler Curl_handler_http = { + "http", /* scheme */ + Curl_http_setup_conn, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + Curl_http_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + Curl_http_getsock_do, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + Curl_http_write_resp, /* write_resp */ + Curl_http_write_resp_hd, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTP, /* defport */ + CURLPROTO_HTTP, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL +}; + +#ifdef USE_SSL +/* + * HTTPS handler interface. + */ +const struct Curl_handler Curl_handler_https = { + "https", /* scheme */ + Curl_http_setup_conn, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + Curl_http_connect, /* connect_it */ + NULL, /* connecting */ + ZERO_NULL, /* doing */ + NULL, /* proto_getsock */ + Curl_http_getsock_do, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + Curl_http_write_resp, /* write_resp */ + Curl_http_write_resp_hd, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTPS, /* defport */ + CURLPROTO_HTTPS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ + PROTOPT_USERPWDCTRL +}; + +#endif + +CURLcode Curl_http_setup_conn(struct Curl_easy *data, + struct connectdata *conn) +{ + /* allocate the HTTP-specific struct for the Curl_easy, only to survive + during this request */ + connkeep(conn, "HTTP default"); + + if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) { + CURLcode result = Curl_conn_may_http3(data, conn); + if(result) + return result; + } + + return CURLE_OK; +} + +#ifndef CURL_DISABLE_PROXY +/* + * checkProxyHeaders() checks the linked list of custom proxy headers + * if proxy headers are not available, then it will lookup into http header + * link list + * + * It takes a connectdata struct as input to see if this is a proxy request or + * not, as it then might check a different header list. Provide the header + * prefix without colon! + */ +char *Curl_checkProxyheaders(struct Curl_easy *data, + const struct connectdata *conn, + const char *thisheader, + const size_t thislen) +{ + struct curl_slist *head; + + for(head = (conn->bits.proxy && data->set.sep_headers) ? + data->set.proxyheaders : data->set.headers; + head; head = head->next) { + if(strncasecompare(head->data, thisheader, thislen) && + Curl_headersep(head->data[thislen])) + return head->data; + } + + return NULL; +} +#else +/* disabled */ +#define Curl_checkProxyheaders(x,y,z,a) NULL +#endif + +/* + * Strip off leading and trailing whitespace from the value in the + * given HTTP header line and return a strdupped copy. Returns NULL in + * case of allocation failure. Returns an empty string if the header value + * consists entirely of whitespace. + */ +char *Curl_copy_header_value(const char *header) +{ + const char *start; + const char *end; + size_t len; + + /* Find the end of the header name */ + while(*header && (*header != ':')) + ++header; + + if(*header) + /* Skip over colon */ + ++header; + + /* Find the first non-space letter */ + start = header; + while(*start && ISSPACE(*start)) + start++; + + end = strchr(start, '\r'); + if(!end) + end = strchr(start, '\n'); + if(!end) + end = strchr(start, '\0'); + if(!end) + return NULL; + + /* skip all trailing space letters */ + while((end > start) && ISSPACE(*end)) + end--; + + /* get length of the type */ + len = end - start + 1; + + return Curl_memdup0(start, len); +} + +#ifndef CURL_DISABLE_HTTP_AUTH + +#ifndef CURL_DISABLE_BASIC_AUTH +/* + * http_output_basic() sets up an Authorization: header (or the proxy version) + * for HTTP Basic authentication. + * + * Returns CURLcode. + */ +static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) +{ + size_t size = 0; + char *authorization = NULL; + char **userp; + const char *user; + const char *pwd; + CURLcode result; + char *out; + + /* credentials are unique per transfer for HTTP, do not use the ones for the + connection */ + if(proxy) { +#ifndef CURL_DISABLE_PROXY + userp = &data->state.aptr.proxyuserpwd; + user = data->state.aptr.proxyuser; + pwd = data->state.aptr.proxypasswd; +#else + return CURLE_NOT_BUILT_IN; +#endif + } + else { + userp = &data->state.aptr.userpwd; + user = data->state.aptr.user; + pwd = data->state.aptr.passwd; + } + + out = aprintf("%s:%s", user ? user : "", pwd ? pwd : ""); + if(!out) + return CURLE_OUT_OF_MEMORY; + + result = Curl_base64_encode(out, strlen(out), &authorization, &size); + if(result) + goto fail; + + if(!authorization) { + result = CURLE_REMOTE_ACCESS_DENIED; + goto fail; + } + + free(*userp); + *userp = aprintf("%sAuthorization: Basic %s\r\n", + proxy ? "Proxy-" : "", + authorization); + free(authorization); + if(!*userp) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + +fail: + free(out); + return result; +} + +#endif + +#ifndef CURL_DISABLE_BEARER_AUTH +/* + * http_output_bearer() sets up an Authorization: header + * for HTTP Bearer authentication. + * + * Returns CURLcode. + */ +static CURLcode http_output_bearer(struct Curl_easy *data) +{ + char **userp; + CURLcode result = CURLE_OK; + + userp = &data->state.aptr.userpwd; + free(*userp); + *userp = aprintf("Authorization: Bearer %s\r\n", + data->set.str[STRING_BEARER]); + + if(!*userp) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + +fail: + return result; +} + +#endif + +#endif + +/* pickoneauth() selects the most favourable authentication method from the + * ones available and the ones we want. + * + * return TRUE if one was picked + */ +static bool pickoneauth(struct auth *pick, unsigned long mask) +{ + bool picked; + /* only deal with authentication we want */ + unsigned long avail = pick->avail & pick->want & mask; + picked = TRUE; + + /* The order of these checks is highly relevant, as this will be the order + of preference in case of the existence of multiple accepted types. */ + if(avail & CURLAUTH_NEGOTIATE) + pick->picked = CURLAUTH_NEGOTIATE; +#ifndef CURL_DISABLE_BEARER_AUTH + else if(avail & CURLAUTH_BEARER) + pick->picked = CURLAUTH_BEARER; +#endif +#ifndef CURL_DISABLE_DIGEST_AUTH + else if(avail & CURLAUTH_DIGEST) + pick->picked = CURLAUTH_DIGEST; +#endif + else if(avail & CURLAUTH_NTLM) + pick->picked = CURLAUTH_NTLM; +#ifndef CURL_DISABLE_BASIC_AUTH + else if(avail & CURLAUTH_BASIC) + pick->picked = CURLAUTH_BASIC; +#endif +#ifndef CURL_DISABLE_AWS + else if(avail & CURLAUTH_AWS_SIGV4) + pick->picked = CURLAUTH_AWS_SIGV4; +#endif + else { + pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */ + picked = FALSE; + } + pick->avail = CURLAUTH_NONE; /* clear it here */ + + return picked; +} + +/* + * http_perhapsrewind() + * + * The current request needs to be done again - maybe due to a follow + * or authentication negotiation. Check if: + * 1) a rewind of the data sent to the server is necessary + * 2) the current transfer should continue or be stopped early + */ +static CURLcode http_perhapsrewind(struct Curl_easy *data, + struct connectdata *conn) +{ + curl_off_t bytessent = data->req.writebytecount; + curl_off_t expectsend = Curl_creader_total_length(data); + curl_off_t upload_remain = (expectsend >= 0)? (expectsend - bytessent) : -1; + bool little_upload_remains = (upload_remain >= 0 && upload_remain < 2000); + bool needs_rewind = Curl_creader_needs_rewind(data); + /* By default, we would like to abort the transfer when little or unknown + * amount remains. This may be overridden by authentications further + * below! */ + bool abort_upload = (!data->req.upload_done && !little_upload_remains); + const char *ongoing_auth = NULL; + + /* We need a rewind before uploading client read data again. The + * checks below just influence of the upload is to be continued + * or aborted early. + * This depends on how much remains to be sent and in what state + * the authentication is. Some auth schemes such as NTLM do not work + * for a new connection. */ + if(needs_rewind) { + infof(data, "Need to rewind upload for next request"); + Curl_creader_set_rewind(data, TRUE); + } + + if(conn->bits.close) + /* If we already decided to close this connection, we cannot veto. */ + return CURLE_OK; + + if(abort_upload) { + /* We'd like to abort the upload - but should we? */ +#if defined(USE_NTLM) + if((data->state.authproxy.picked == CURLAUTH_NTLM) || + (data->state.authhost.picked == CURLAUTH_NTLM)) { + ongoing_auth = "NTML"; + if((conn->http_ntlm_state != NTLMSTATE_NONE) || + (conn->proxy_ntlm_state != NTLMSTATE_NONE)) { + /* The NTLM-negotiation has started, keep on sending. + * Need to do further work on same connection */ + abort_upload = FALSE; + } + } +#endif +#if defined(USE_SPNEGO) + /* There is still data left to send */ + if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) || + (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) { + ongoing_auth = "NEGOTIATE"; + if((conn->http_negotiate_state != GSS_AUTHNONE) || + (conn->proxy_negotiate_state != GSS_AUTHNONE)) { + /* The NEGOTIATE-negotiation has started, keep on sending. + * Need to do further work on same connection */ + abort_upload = FALSE; + } + } +#endif + } + + if(abort_upload) { + if(upload_remain >= 0) + infof(data, "%s%sclose instead of sending %" + CURL_FORMAT_CURL_OFF_T " more bytes", + ongoing_auth? ongoing_auth : "", + ongoing_auth? " send, " : "", + upload_remain); + else + infof(data, "%s%sclose instead of sending unknown amount " + "of more bytes", + ongoing_auth? ongoing_auth : "", + ongoing_auth? " send, " : ""); + /* We decided to abort the ongoing transfer */ + streamclose(conn, "Mid-auth HTTP and much data left to send"); + /* FIXME: questionable manipulation here, can we do this differently? */ + data->req.size = 0; /* do not download any more than 0 bytes */ + } + return CURLE_OK; +} + +/* + * Curl_http_auth_act() gets called when all HTTP headers have been received + * and it checks what authentication methods that are available and decides + * which one (if any) to use. It will set 'newurl' if an auth method was + * picked. + */ + +CURLcode Curl_http_auth_act(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + bool pickhost = FALSE; + bool pickproxy = FALSE; + CURLcode result = CURLE_OK; + unsigned long authmask = ~0ul; + + if(!data->set.str[STRING_BEARER]) + authmask &= (unsigned long)~CURLAUTH_BEARER; + + if(100 <= data->req.httpcode && data->req.httpcode <= 199) + /* this is a transient response code, ignore */ + return CURLE_OK; + + if(data->state.authproblem) + return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK; + + if((data->state.aptr.user || data->set.str[STRING_BEARER]) && + ((data->req.httpcode == 401) || + (data->req.authneg && data->req.httpcode < 300))) { + pickhost = pickoneauth(&data->state.authhost, authmask); + if(!pickhost) + data->state.authproblem = TRUE; + if(data->state.authhost.picked == CURLAUTH_NTLM && + conn->httpversion > 11) { + infof(data, "Forcing HTTP/1.1 for NTLM"); + connclose(conn, "Force HTTP/1.1 connection"); + data->state.httpwant = CURL_HTTP_VERSION_1_1; + } + } +#ifndef CURL_DISABLE_PROXY + if(conn->bits.proxy_user_passwd && + ((data->req.httpcode == 407) || + (data->req.authneg && data->req.httpcode < 300))) { + pickproxy = pickoneauth(&data->state.authproxy, + authmask & ~CURLAUTH_BEARER); + if(!pickproxy) + data->state.authproblem = TRUE; + } +#endif + + if(pickhost || pickproxy) { + result = http_perhapsrewind(data, conn); + if(result) + return result; + + /* In case this is GSS auth, the newurl field is already allocated so + we must make sure to free it before allocating a new one. As figured + out in bug #2284386 */ + Curl_safefree(data->req.newurl); + data->req.newurl = strdup(data->state.url); /* clone URL */ + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + } + else if((data->req.httpcode < 300) && + (!data->state.authhost.done) && + data->req.authneg) { + /* no (known) authentication available, + authentication is not "done" yet and + no authentication seems to be required and + we did not try HEAD or GET */ + if((data->state.httpreq != HTTPREQ_GET) && + (data->state.httpreq != HTTPREQ_HEAD)) { + data->req.newurl = strdup(data->state.url); /* clone URL */ + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + data->state.authhost.done = TRUE; + } + } + if(http_should_fail(data, data->req.httpcode)) { + failf(data, "The requested URL returned error: %d", + data->req.httpcode); + result = CURLE_HTTP_RETURNED_ERROR; + } + + return result; +} + +#ifndef CURL_DISABLE_HTTP_AUTH +/* + * Output the correct authentication header depending on the auth type + * and whether or not it is to a proxy. + */ +static CURLcode +output_auth_headers(struct Curl_easy *data, + struct connectdata *conn, + struct auth *authstatus, + const char *request, + const char *path, + bool proxy) +{ + const char *auth = NULL; + CURLcode result = CURLE_OK; + (void)conn; + +#ifdef CURL_DISABLE_DIGEST_AUTH + (void)request; + (void)path; +#endif +#ifndef CURL_DISABLE_AWS + if(authstatus->picked == CURLAUTH_AWS_SIGV4) { + auth = "AWS_SIGV4"; + result = Curl_output_aws_sigv4(data, proxy); + if(result) + return result; + } + else +#endif +#ifdef USE_SPNEGO + if(authstatus->picked == CURLAUTH_NEGOTIATE) { + auth = "Negotiate"; + result = Curl_output_negotiate(data, conn, proxy); + if(result) + return result; + } + else +#endif +#ifdef USE_NTLM + if(authstatus->picked == CURLAUTH_NTLM) { + auth = "NTLM"; + result = Curl_output_ntlm(data, proxy); + if(result) + return result; + } + else +#endif +#ifndef CURL_DISABLE_DIGEST_AUTH + if(authstatus->picked == CURLAUTH_DIGEST) { + auth = "Digest"; + result = Curl_output_digest(data, + proxy, + (const unsigned char *)request, + (const unsigned char *)path); + if(result) + return result; + } + else +#endif +#ifndef CURL_DISABLE_BASIC_AUTH + if(authstatus->picked == CURLAUTH_BASIC) { + /* Basic */ + if( +#ifndef CURL_DISABLE_PROXY + (proxy && conn->bits.proxy_user_passwd && + !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) || +#endif + (!proxy && data->state.aptr.user && + !Curl_checkheaders(data, STRCONST("Authorization")))) { + auth = "Basic"; + result = http_output_basic(data, proxy); + if(result) + return result; + } + + /* NOTE: this function should set 'done' TRUE, as the other auth + functions work that way */ + authstatus->done = TRUE; + } +#endif +#ifndef CURL_DISABLE_BEARER_AUTH + if(authstatus->picked == CURLAUTH_BEARER) { + /* Bearer */ + if((!proxy && data->set.str[STRING_BEARER] && + !Curl_checkheaders(data, STRCONST("Authorization")))) { + auth = "Bearer"; + result = http_output_bearer(data); + if(result) + return result; + } + + /* NOTE: this function should set 'done' TRUE, as the other auth + functions work that way */ + authstatus->done = TRUE; + } +#endif + + if(auth) { +#ifndef CURL_DISABLE_PROXY + infof(data, "%s auth using %s with user '%s'", + proxy ? "Proxy" : "Server", auth, + proxy ? (data->state.aptr.proxyuser ? + data->state.aptr.proxyuser : "") : + (data->state.aptr.user ? + data->state.aptr.user : "")); +#else + (void)proxy; + infof(data, "Server auth using %s with user '%s'", + auth, data->state.aptr.user ? + data->state.aptr.user : ""); +#endif + authstatus->multipass = (!authstatus->done) ? TRUE : FALSE; + } + else + authstatus->multipass = FALSE; + + return result; +} + +/** + * Curl_http_output_auth() setups the authentication headers for the + * host/proxy and the correct authentication + * method. data->state.authdone is set to TRUE when authentication is + * done. + * + * @param conn all information about the current connection + * @param request pointer to the request keyword + * @param path pointer to the requested path; should include query part + * @param proxytunnel boolean if this is the request setting up a "proxy + * tunnel" + * + * @returns CURLcode + */ +CURLcode +Curl_http_output_auth(struct Curl_easy *data, + struct connectdata *conn, + const char *request, + Curl_HttpReq httpreq, + const char *path, + bool proxytunnel) /* TRUE if this is the request setting + up the proxy tunnel */ +{ + CURLcode result = CURLE_OK; + struct auth *authhost; + struct auth *authproxy; + + DEBUGASSERT(data); + + authhost = &data->state.authhost; + authproxy = &data->state.authproxy; + + if( +#ifndef CURL_DISABLE_PROXY + (conn->bits.httpproxy && conn->bits.proxy_user_passwd) || +#endif + data->state.aptr.user || +#ifdef USE_SPNEGO + authhost->want & CURLAUTH_NEGOTIATE || + authproxy->want & CURLAUTH_NEGOTIATE || +#endif + data->set.str[STRING_BEARER]) + /* continue please */; + else { + authhost->done = TRUE; + authproxy->done = TRUE; + return CURLE_OK; /* no authentication with no user or password */ + } + + if(authhost->want && !authhost->picked) + /* The app has selected one or more methods, but none has been picked + so far by a server round-trip. Then we set the picked one to the + want one, and if this is one single bit it will be used instantly. */ + authhost->picked = authhost->want; + + if(authproxy->want && !authproxy->picked) + /* The app has selected one or more methods, but none has been picked so + far by a proxy round-trip. Then we set the picked one to the want one, + and if this is one single bit it will be used instantly. */ + authproxy->picked = authproxy->want; + +#ifndef CURL_DISABLE_PROXY + /* Send proxy authentication header if needed */ + if(conn->bits.httpproxy && + (conn->bits.tunnel_proxy == (bit)proxytunnel)) { + result = output_auth_headers(data, conn, authproxy, request, path, TRUE); + if(result) + return result; + } + else +#else + (void)proxytunnel; +#endif /* CURL_DISABLE_PROXY */ + /* we have no proxy so let's pretend we are done authenticating + with it */ + authproxy->done = TRUE; + + /* To prevent the user+password to get sent to other than the original host + due to a location-follow */ + if(Curl_auth_allowed_to_host(data) +#ifndef CURL_DISABLE_NETRC + || conn->bits.netrc +#endif + ) + result = output_auth_headers(data, conn, authhost, request, path, FALSE); + else + authhost->done = TRUE; + + if(((authhost->multipass && !authhost->done) || + (authproxy->multipass && !authproxy->done)) && + (httpreq != HTTPREQ_GET) && + (httpreq != HTTPREQ_HEAD)) { + /* Auth is required and we are not authenticated yet. Make a PUT or POST + with content-length zero as a "probe". */ + data->req.authneg = TRUE; + } + else + data->req.authneg = FALSE; + + return result; +} + +#else +/* when disabled */ +CURLcode +Curl_http_output_auth(struct Curl_easy *data, + struct connectdata *conn, + const char *request, + Curl_HttpReq httpreq, + const char *path, + bool proxytunnel) +{ + (void)data; + (void)conn; + (void)request; + (void)httpreq; + (void)path; + (void)proxytunnel; + return CURLE_OK; +} +#endif + +#if defined(USE_SPNEGO) || defined(USE_NTLM) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ + !defined(CURL_DISABLE_BASIC_AUTH) || \ + !defined(CURL_DISABLE_BEARER_AUTH) +static int is_valid_auth_separator(char ch) +{ + return ch == '\0' || ch == ',' || ISSPACE(ch); +} +#endif + +/* + * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate: + * headers. They are dealt with both in the transfer.c main loop and in the + * proxy CONNECT loop. + */ +CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, + const char *auth) /* the first non-space */ +{ + /* + * This resource requires authentication + */ + struct connectdata *conn = data->conn; +#ifdef USE_SPNEGO + curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state : + &conn->http_negotiate_state; +#endif +#if defined(USE_SPNEGO) || \ + defined(USE_NTLM) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ + !defined(CURL_DISABLE_BASIC_AUTH) || \ + !defined(CURL_DISABLE_BEARER_AUTH) + + unsigned long *availp; + struct auth *authp; + + if(proxy) { + availp = &data->info.proxyauthavail; + authp = &data->state.authproxy; + } + else { + availp = &data->info.httpauthavail; + authp = &data->state.authhost; + } +#else + (void) proxy; +#endif + + (void) conn; /* In case conditionals make it unused. */ + + /* + * Here we check if we want the specific single authentication (using ==) and + * if we do, we initiate usage of it. + * + * If the provided authentication is wanted as one out of several accepted + * types (using &), we OR this authentication type to the authavail + * variable. + * + * Note: + * + * ->picked is first set to the 'want' value (one or more bits) before the + * request is sent, and then it is again set _after_ all response 401/407 + * headers have been received but then only to a single preferred method + * (bit). + */ + + while(*auth) { +#ifdef USE_SPNEGO + if(checkprefix("Negotiate", auth) && is_valid_auth_separator(auth[9])) { + if((authp->avail & CURLAUTH_NEGOTIATE) || + Curl_auth_is_spnego_supported()) { + *availp |= CURLAUTH_NEGOTIATE; + authp->avail |= CURLAUTH_NEGOTIATE; + + if(authp->picked == CURLAUTH_NEGOTIATE) { + CURLcode result = Curl_input_negotiate(data, conn, proxy, auth); + if(!result) { + free(data->req.newurl); + data->req.newurl = strdup(data->state.url); + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + data->state.authproblem = FALSE; + /* we received a GSS auth token and we dealt with it fine */ + *negstate = GSS_AUTHRECV; + } + else + data->state.authproblem = TRUE; + } + } + } + else +#endif +#ifdef USE_NTLM + /* NTLM support requires the SSL crypto libs */ + if(checkprefix("NTLM", auth) && is_valid_auth_separator(auth[4])) { + if((authp->avail & CURLAUTH_NTLM) || + Curl_auth_is_ntlm_supported()) { + *availp |= CURLAUTH_NTLM; + authp->avail |= CURLAUTH_NTLM; + + if(authp->picked == CURLAUTH_NTLM) { + /* NTLM authentication is picked and activated */ + CURLcode result = Curl_input_ntlm(data, proxy, auth); + if(!result) { + data->state.authproblem = FALSE; + } + else { + infof(data, "Authentication problem. Ignoring this."); + data->state.authproblem = TRUE; + } + } + } + } + else +#endif +#ifndef CURL_DISABLE_DIGEST_AUTH + if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) { + if((authp->avail & CURLAUTH_DIGEST) != 0) + infof(data, "Ignoring duplicate digest auth header."); + else if(Curl_auth_is_digest_supported()) { + CURLcode result; + + *availp |= CURLAUTH_DIGEST; + authp->avail |= CURLAUTH_DIGEST; + + /* We call this function on input Digest headers even if Digest + * authentication is not activated yet, as we need to store the + * incoming data from this header in case we are going to use + * Digest */ + result = Curl_input_digest(data, proxy, auth); + if(result) { + infof(data, "Authentication problem. Ignoring this."); + data->state.authproblem = TRUE; + } + } + } + else +#endif +#ifndef CURL_DISABLE_BASIC_AUTH + if(checkprefix("Basic", auth) && + is_valid_auth_separator(auth[5])) { + *availp |= CURLAUTH_BASIC; + authp->avail |= CURLAUTH_BASIC; + if(authp->picked == CURLAUTH_BASIC) { + /* We asked for Basic authentication but got a 40X back + anyway, which basically means our name+password is not + valid. */ + authp->avail = CURLAUTH_NONE; + infof(data, "Authentication problem. Ignoring this."); + data->state.authproblem = TRUE; + } + } + else +#endif +#ifndef CURL_DISABLE_BEARER_AUTH + if(checkprefix("Bearer", auth) && + is_valid_auth_separator(auth[6])) { + *availp |= CURLAUTH_BEARER; + authp->avail |= CURLAUTH_BEARER; + if(authp->picked == CURLAUTH_BEARER) { + /* We asked for Bearer authentication but got a 40X back + anyway, which basically means our token is not valid. */ + authp->avail = CURLAUTH_NONE; + infof(data, "Authentication problem. Ignoring this."); + data->state.authproblem = TRUE; + } + } +#else + { + /* + * Empty block to terminate the if-else chain correctly. + * + * A semicolon would yield the same result here, but can cause a + * compiler warning when -Wextra is enabled. + */ + } +#endif + + /* there may be multiple methods on one line, so keep reading */ + while(*auth && *auth != ',') /* read up to the next comma */ + auth++; + if(*auth == ',') /* if we are on a comma, skip it */ + auth++; + while(*auth && ISSPACE(*auth)) + auth++; + } + + return CURLE_OK; +} + +/** + * http_should_fail() determines whether an HTTP response code has gotten us + * into an error state or not. + * + * @retval FALSE communications should continue + * + * @retval TRUE communications should not continue + */ +static bool http_should_fail(struct Curl_easy *data, int httpcode) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + + /* + ** If we have not been asked to fail on error, + ** do not fail. + */ + if(!data->set.http_fail_on_error) + return FALSE; + + /* + ** Any code < 400 is never terminal. + */ + if(httpcode < 400) + return FALSE; + + /* + ** A 416 response to a resume request is presumably because the file is + ** already completely downloaded and thus not actually a fail. + */ + if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && + httpcode == 416) + return FALSE; + + /* + ** Any code >= 400 that is not 401 or 407 is always + ** a terminal error + */ + if((httpcode != 401) && (httpcode != 407)) + return TRUE; + + /* + ** All we have left to deal with is 401 and 407 + */ + DEBUGASSERT((httpcode == 401) || (httpcode == 407)); + + /* + ** Examine the current authentication state to see if this is an error. The + ** idea is for this function to get called after processing all the headers + ** in a response message. So, if we have been to asked to authenticate a + ** particular stage, and we have done it, we are OK. If we are already + ** completely authenticated, it is not OK to get another 401 or 407. + ** + ** It is possible for authentication to go stale such that the client needs + ** to reauthenticate. Once that info is available, use it here. + */ + + /* + ** Either we are not authenticating, or we are supposed to be authenticating + ** something else. This is an error. + */ + if((httpcode == 401) && !data->state.aptr.user) + return TRUE; +#ifndef CURL_DISABLE_PROXY + if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) + return TRUE; +#endif + + return data->state.authproblem; +} + +/* + * Curl_compareheader() + * + * Returns TRUE if 'headerline' contains the 'header' with given 'content'. + * Pass headers WITH the colon. + */ +bool +Curl_compareheader(const char *headerline, /* line to check */ + const char *header, /* header keyword _with_ colon */ + const size_t hlen, /* len of the keyword in bytes */ + const char *content, /* content string to find */ + const size_t clen) /* len of the content in bytes */ +{ + /* RFC2616, section 4.2 says: "Each header field consists of a name followed + * by a colon (":") and the field value. Field names are case-insensitive. + * The field value MAY be preceded by any amount of LWS, though a single SP + * is preferred." */ + + size_t len; + const char *start; + const char *end; + DEBUGASSERT(hlen); + DEBUGASSERT(clen); + DEBUGASSERT(header); + DEBUGASSERT(content); + + if(!strncasecompare(headerline, header, hlen)) + return FALSE; /* does not start with header */ + + /* pass the header */ + start = &headerline[hlen]; + + /* pass all whitespace */ + while(*start && ISSPACE(*start)) + start++; + + /* find the end of the header line */ + end = strchr(start, '\r'); /* lines end with CRLF */ + if(!end) { + /* in case there is a non-standard compliant line here */ + end = strchr(start, '\n'); + + if(!end) + /* hm, there is no line ending here, use the zero byte! */ + end = strchr(start, '\0'); + } + + len = end-start; /* length of the content part of the input line */ + + /* find the content string in the rest of the line */ + for(; len >= clen; len--, start++) { + if(strncasecompare(start, content, clen)) + return TRUE; /* match! */ + } + + return FALSE; /* no match */ +} + +/* + * Curl_http_connect() performs HTTP stuff to do at connect-time, called from + * the generic Curl_connect(). + */ +CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + + /* We default to persistent connections. We set this already in this connect + function to make the reuse checks properly be able to check this bit. */ + connkeep(conn, "HTTP default"); + + return Curl_conn_connect(data, FIRSTSOCKET, FALSE, done); +} + +/* this returns the socket to wait for in the DO and DOING state for the multi + interface and then we are always _sending_ a request and thus we wait for + the single socket to become writable only */ +int Curl_http_getsock_do(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *socks) +{ + /* write mode */ + (void)conn; + socks[0] = Curl_conn_get_socket(data, FIRSTSOCKET); + return GETSOCK_WRITESOCK(0); +} + +/* + * Curl_http_done() gets called after a single HTTP request has been + * performed. + */ + +CURLcode Curl_http_done(struct Curl_easy *data, + CURLcode status, bool premature) +{ + struct connectdata *conn = data->conn; + + /* Clear multipass flag. If authentication is not done yet, then it will get + * a chance to be set back to true when we output the next auth header */ + data->state.authhost.multipass = FALSE; + data->state.authproxy.multipass = FALSE; + + Curl_dyn_reset(&data->state.headerb); + Curl_hyper_done(data); + + if(status) + return status; + + if(!premature && /* this check is pointless when DONE is called before the + entire operation is complete */ + !conn->bits.retry && + !data->set.connect_only && + (data->req.bytecount + + data->req.headerbytecount - + data->req.deductheadercount) <= 0) { + /* If this connection is not simply closed to be retried, AND nothing was + read from the HTTP server (that counts), this cannot be right so we + return an error here */ + failf(data, "Empty reply from server"); + /* Mark it as closed to avoid the "left intact" message */ + streamclose(conn, "Empty reply from server"); + return CURLE_GOT_NOTHING; + } + + return CURLE_OK; +} + +/* + * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons + * to avoid it include: + * + * - if the user specifically requested HTTP 1.0 + * - if the server we are connected to only supports 1.0 + * - if any server previously contacted to handle this request only supports + * 1.0. + */ +bool Curl_use_http_1_1plus(const struct Curl_easy *data, + const struct connectdata *conn) +{ + if((data->state.httpversion == 10) || (conn->httpversion == 10)) + return FALSE; + if((data->state.httpwant == CURL_HTTP_VERSION_1_0) && + (conn->httpversion <= 10)) + return FALSE; + return ((data->state.httpwant == CURL_HTTP_VERSION_NONE) || + (data->state.httpwant >= CURL_HTTP_VERSION_1_1)); +} + +#ifndef USE_HYPER +static const char *get_http_string(const struct Curl_easy *data, + const struct connectdata *conn) +{ + if(Curl_conn_is_http3(data, conn, FIRSTSOCKET)) + return "3"; + if(Curl_conn_is_http2(data, conn, FIRSTSOCKET)) + return "2"; + if(Curl_use_http_1_1plus(data, conn)) + return "1.1"; + + return "1.0"; +} +#endif + +enum proxy_use { + HEADER_SERVER, /* direct to server */ + HEADER_PROXY, /* regular request to proxy */ + HEADER_CONNECT /* sending CONNECT to a proxy */ +}; + +static bool hd_name_eq(const char *n1, size_t n1len, + const char *n2, size_t n2len) +{ + if(n1len == n2len) { + return strncasecompare(n1, n2, n1len); + } + return FALSE; +} + +CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, + bool is_connect, + struct dynhds *hds) +{ + struct connectdata *conn = data->conn; + char *ptr; + struct curl_slist *h[2]; + struct curl_slist *headers; + int numlists = 1; /* by default */ + int i; + +#ifndef CURL_DISABLE_PROXY + enum proxy_use proxy; + + if(is_connect) + proxy = HEADER_CONNECT; + else + proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy? + HEADER_PROXY:HEADER_SERVER; + + switch(proxy) { + case HEADER_SERVER: + h[0] = data->set.headers; + break; + case HEADER_PROXY: + h[0] = data->set.headers; + if(data->set.sep_headers) { + h[1] = data->set.proxyheaders; + numlists++; + } + break; + case HEADER_CONNECT: + if(data->set.sep_headers) + h[0] = data->set.proxyheaders; + else + h[0] = data->set.headers; + break; + } +#else + (void)is_connect; + h[0] = data->set.headers; +#endif + + /* loop through one or two lists */ + for(i = 0; i < numlists; i++) { + for(headers = h[i]; headers; headers = headers->next) { + const char *name, *value; + size_t namelen, valuelen; + + /* There are 2 quirks in place for custom headers: + * 1. setting only 'name:' to suppress a header from being sent + * 2. setting only 'name;' to send an empty (illegal) header + */ + ptr = strchr(headers->data, ':'); + if(ptr) { + name = headers->data; + namelen = ptr - headers->data; + ptr++; /* pass the colon */ + while(*ptr && ISSPACE(*ptr)) + ptr++; + if(*ptr) { + value = ptr; + valuelen = strlen(value); + } + else { + /* quirk #1, suppress this header */ + continue; + } + } + else { + ptr = strchr(headers->data, ';'); + + if(!ptr) { + /* neither : nor ; in provided header value. We seem + * to ignore this silently */ + continue; + } + + name = headers->data; + namelen = ptr - headers->data; + ptr++; /* pass the semicolon */ + while(*ptr && ISSPACE(*ptr)) + ptr++; + if(!*ptr) { + /* quirk #2, send an empty header */ + value = ""; + valuelen = 0; + } + else { + /* this may be used for something else in the future, + * ignore this for now */ + continue; + } + } + + DEBUGASSERT(name && value); + if(data->state.aptr.host && + /* a Host: header was sent already, do not pass on any custom Host: + header as that will produce *two* in the same request! */ + hd_name_eq(name, namelen, STRCONST("Host:"))) + ; + else if(data->state.httpreq == HTTPREQ_POST_FORM && + /* this header (extended by formdata.c) is sent later */ + hd_name_eq(name, namelen, STRCONST("Content-Type:"))) + ; + else if(data->state.httpreq == HTTPREQ_POST_MIME && + /* this header is sent later */ + hd_name_eq(name, namelen, STRCONST("Content-Type:"))) + ; + else if(data->req.authneg && + /* while doing auth neg, do not allow the custom length since + we will force length zero then */ + hd_name_eq(name, namelen, STRCONST("Content-Length:"))) + ; + else if(data->state.aptr.te && + /* when asking for Transfer-Encoding, do not pass on a custom + Connection: */ + hd_name_eq(name, namelen, STRCONST("Connection:"))) + ; + else if((conn->httpversion >= 20) && + hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:"))) + /* HTTP/2 does not support chunked requests */ + ; + else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) || + hd_name_eq(name, namelen, STRCONST("Cookie:"))) && + /* be careful of sending this potentially sensitive header to + other hosts */ + !Curl_auth_allowed_to_host(data)) + ; + else { + CURLcode result; + + result = Curl_dynhds_add(hds, name, namelen, value, valuelen); + if(result) + return result; + } + } + } + + return CURLE_OK; +} + +CURLcode Curl_add_custom_headers(struct Curl_easy *data, + bool is_connect, +#ifndef USE_HYPER + struct dynbuf *req +#else + void *req +#endif + ) +{ + struct connectdata *conn = data->conn; + char *ptr; + struct curl_slist *h[2]; + struct curl_slist *headers; + int numlists = 1; /* by default */ + int i; + +#ifndef CURL_DISABLE_PROXY + enum proxy_use proxy; + + if(is_connect) + proxy = HEADER_CONNECT; + else + proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy? + HEADER_PROXY:HEADER_SERVER; + + switch(proxy) { + case HEADER_SERVER: + h[0] = data->set.headers; + break; + case HEADER_PROXY: + h[0] = data->set.headers; + if(data->set.sep_headers) { + h[1] = data->set.proxyheaders; + numlists++; + } + break; + case HEADER_CONNECT: + if(data->set.sep_headers) + h[0] = data->set.proxyheaders; + else + h[0] = data->set.headers; + break; + } +#else + (void)is_connect; + h[0] = data->set.headers; +#endif + + /* loop through one or two lists */ + for(i = 0; i < numlists; i++) { + headers = h[i]; + + while(headers) { + char *semicolonp = NULL; + ptr = strchr(headers->data, ':'); + if(!ptr) { + char *optr; + /* no colon, semicolon? */ + ptr = strchr(headers->data, ';'); + if(ptr) { + optr = ptr; + ptr++; /* pass the semicolon */ + while(*ptr && ISSPACE(*ptr)) + ptr++; + + if(*ptr) { + /* this may be used for something else in the future */ + optr = NULL; + } + else { + if(*(--ptr) == ';') { + /* copy the source */ + semicolonp = strdup(headers->data); + if(!semicolonp) { +#ifndef USE_HYPER + Curl_dyn_free(req); +#endif + return CURLE_OUT_OF_MEMORY; + } + /* put a colon where the semicolon is */ + semicolonp[ptr - headers->data] = ':'; + /* point at the colon */ + optr = &semicolonp [ptr - headers->data]; + } + } + ptr = optr; + } + } + if(ptr && (ptr != headers->data)) { + /* we require a colon for this to be a true header */ + + ptr++; /* pass the colon */ + while(*ptr && ISSPACE(*ptr)) + ptr++; + + if(*ptr || semicolonp) { + /* only send this if the contents was non-blank or done special */ + CURLcode result = CURLE_OK; + char *compare = semicolonp ? semicolonp : headers->data; + + if(data->state.aptr.host && + /* a Host: header was sent already, do not pass on any custom + Host: header as that will produce *two* in the same + request! */ + checkprefix("Host:", compare)) + ; + else if(data->state.httpreq == HTTPREQ_POST_FORM && + /* this header (extended by formdata.c) is sent later */ + checkprefix("Content-Type:", compare)) + ; + else if(data->state.httpreq == HTTPREQ_POST_MIME && + /* this header is sent later */ + checkprefix("Content-Type:", compare)) + ; + else if(data->req.authneg && + /* while doing auth neg, do not allow the custom length since + we will force length zero then */ + checkprefix("Content-Length:", compare)) + ; + else if(data->state.aptr.te && + /* when asking for Transfer-Encoding, do not pass on a custom + Connection: */ + checkprefix("Connection:", compare)) + ; + else if((conn->httpversion >= 20) && + checkprefix("Transfer-Encoding:", compare)) + /* HTTP/2 does not support chunked requests */ + ; + else if((checkprefix("Authorization:", compare) || + checkprefix("Cookie:", compare)) && + /* be careful of sending this potentially sensitive header to + other hosts */ + !Curl_auth_allowed_to_host(data)) + ; + else { +#ifdef USE_HYPER + result = Curl_hyper_header(data, req, compare); +#else + result = Curl_dyn_addf(req, "%s\r\n", compare); +#endif + } + if(semicolonp) + free(semicolonp); + if(result) + return result; + } + } + headers = headers->next; + } + } + + return CURLE_OK; +} + +#ifndef CURL_DISABLE_PARSEDATE +CURLcode Curl_add_timecondition(struct Curl_easy *data, +#ifndef USE_HYPER + struct dynbuf *req +#else + void *req +#endif + ) +{ + const struct tm *tm; + struct tm keeptime; + CURLcode result; + char datestr[80]; + const char *condp; + size_t len; + + if(data->set.timecondition == CURL_TIMECOND_NONE) + /* no condition was asked for */ + return CURLE_OK; + + result = Curl_gmtime(data->set.timevalue, &keeptime); + if(result) { + failf(data, "Invalid TIMEVALUE"); + return result; + } + tm = &keeptime; + + switch(data->set.timecondition) { + default: + DEBUGF(infof(data, "invalid time condition")); + return CURLE_BAD_FUNCTION_ARGUMENT; + + case CURL_TIMECOND_IFMODSINCE: + condp = "If-Modified-Since"; + len = 17; + break; + case CURL_TIMECOND_IFUNMODSINCE: + condp = "If-Unmodified-Since"; + len = 19; + break; + case CURL_TIMECOND_LASTMOD: + condp = "Last-Modified"; + len = 13; + break; + } + + if(Curl_checkheaders(data, condp, len)) { + /* A custom header was specified; it will be sent instead. */ + return CURLE_OK; + } + + /* The If-Modified-Since header family should have their times set in + * GMT as RFC2616 defines: "All HTTP date/time stamps MUST be + * represented in Greenwich Mean Time (GMT), without exception. For the + * purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal + * Time)." (see page 20 of RFC2616). + */ + + /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ + msnprintf(datestr, sizeof(datestr), + "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", + condp, + Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], + tm->tm_mday, + Curl_month[tm->tm_mon], + tm->tm_year + 1900, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + +#ifndef USE_HYPER + result = Curl_dyn_add(req, datestr); +#else + result = Curl_hyper_header(data, req, datestr); +#endif + + return result; +} +#else +/* disabled */ +CURLcode Curl_add_timecondition(struct Curl_easy *data, + struct dynbuf *req) +{ + (void)data; + (void)req; + return CURLE_OK; +} +#endif + +void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, + const char **method, Curl_HttpReq *reqp) +{ + Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq; + const char *request; + if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && + data->state.upload) + httpreq = HTTPREQ_PUT; + + /* Now set the 'request' pointer to the proper request string */ + if(data->set.str[STRING_CUSTOMREQUEST]) + request = data->set.str[STRING_CUSTOMREQUEST]; + else { + if(data->req.no_body) + request = "HEAD"; + else { + DEBUGASSERT((httpreq >= HTTPREQ_GET) && (httpreq <= HTTPREQ_HEAD)); + switch(httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + request = "POST"; + break; + case HTTPREQ_PUT: + request = "PUT"; + break; + default: /* this should never happen */ + case HTTPREQ_GET: + request = "GET"; + break; + case HTTPREQ_HEAD: + request = "HEAD"; + break; + } + } + } + *method = request; + *reqp = httpreq; +} + +CURLcode Curl_http_useragent(struct Curl_easy *data) +{ + /* The User-Agent string might have been allocated in url.c already, because + it might have been used in the proxy connect, but if we have got a header + with the user-agent string specified, we erase the previously made string + here. */ + if(Curl_checkheaders(data, STRCONST("User-Agent"))) { + free(data->state.aptr.uagent); + data->state.aptr.uagent = NULL; + } + return CURLE_OK; +} + + +CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) +{ + const char *ptr; + struct dynamically_allocated_data *aptr = &data->state.aptr; + if(!data->state.this_is_a_follow) { + /* Free to avoid leaking memory on multiple requests */ + free(data->state.first_host); + + data->state.first_host = strdup(conn->host.name); + if(!data->state.first_host) + return CURLE_OUT_OF_MEMORY; + + data->state.first_remote_port = conn->remote_port; + data->state.first_remote_protocol = conn->handler->protocol; + } + Curl_safefree(aptr->host); + + ptr = Curl_checkheaders(data, STRCONST("Host")); + if(ptr && (!data->state.this_is_a_follow || + strcasecompare(data->state.first_host, conn->host.name))) { +#if !defined(CURL_DISABLE_COOKIES) + /* If we have a given custom Host: header, we extract the hostname in + order to possibly use it for cookie reasons later on. We only allow the + custom Host: header if this is NOT a redirect, as setting Host: in the + redirected request is being out on thin ice. Except if the hostname + is the same as the first one! */ + char *cookiehost = Curl_copy_header_value(ptr); + if(!cookiehost) + return CURLE_OUT_OF_MEMORY; + if(!*cookiehost) + /* ignore empty data */ + free(cookiehost); + else { + /* If the host begins with '[', we start searching for the port after + the bracket has been closed */ + if(*cookiehost == '[') { + char *closingbracket; + /* since the 'cookiehost' is an allocated memory area that will be + freed later we cannot simply increment the pointer */ + memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1); + closingbracket = strchr(cookiehost, ']'); + if(closingbracket) + *closingbracket = 0; + } + else { + int startsearch = 0; + char *colon = strchr(cookiehost + startsearch, ':'); + if(colon) + *colon = 0; /* The host must not include an embedded port number */ + } + Curl_safefree(aptr->cookiehost); + aptr->cookiehost = cookiehost; + } +#endif + + if(!strcasecompare("Host:", ptr)) { + aptr->host = aprintf("Host:%s\r\n", &ptr[5]); + if(!aptr->host) + return CURLE_OUT_OF_MEMORY; + } + } + else { + /* When building Host: headers, we must put the hostname within + [brackets] if the hostname is a plain IPv6-address. RFC2732-style. */ + const char *host = conn->host.name; + + if(((conn->given->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS)) && + (conn->remote_port == PORT_HTTPS)) || + ((conn->given->protocol&(CURLPROTO_HTTP|CURLPROTO_WS)) && + (conn->remote_port == PORT_HTTP)) ) + /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include + the port number in the host string */ + aptr->host = aprintf("Host: %s%s%s\r\n", conn->bits.ipv6_ip?"[":"", + host, conn->bits.ipv6_ip?"]":""); + else + aptr->host = aprintf("Host: %s%s%s:%d\r\n", conn->bits.ipv6_ip?"[":"", + host, conn->bits.ipv6_ip?"]":"", + conn->remote_port); + + if(!aptr->host) + /* without Host: we cannot make a nice request */ + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; +} + +/* + * Append the request-target to the HTTP request + */ +CURLcode Curl_http_target(struct Curl_easy *data, + struct connectdata *conn, + struct dynbuf *r) +{ + CURLcode result = CURLE_OK; + const char *path = data->state.up.path; + const char *query = data->state.up.query; + + if(data->set.str[STRING_TARGET]) { + path = data->set.str[STRING_TARGET]; + query = NULL; + } + +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + /* Using a proxy but does not tunnel through it */ + + /* The path sent to the proxy is in fact the entire URL. But if the remote + host is a IDN-name, we must make sure that the request we produce only + uses the encoded hostname! */ + + /* and no fragment part */ + CURLUcode uc; + char *url; + CURLU *h = curl_url_dup(data->state.uh); + if(!h) + return CURLE_OUT_OF_MEMORY; + + if(conn->host.dispname != conn->host.name) { + uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0); + if(uc) { + curl_url_cleanup(h); + return CURLE_OUT_OF_MEMORY; + } + } + uc = curl_url_set(h, CURLUPART_FRAGMENT, NULL, 0); + if(uc) { + curl_url_cleanup(h); + return CURLE_OUT_OF_MEMORY; + } + + if(strcasecompare("http", data->state.up.scheme)) { + /* when getting HTTP, we do not want the userinfo the URL */ + uc = curl_url_set(h, CURLUPART_USER, NULL, 0); + if(uc) { + curl_url_cleanup(h); + return CURLE_OUT_OF_MEMORY; + } + uc = curl_url_set(h, CURLUPART_PASSWORD, NULL, 0); + if(uc) { + curl_url_cleanup(h); + return CURLE_OUT_OF_MEMORY; + } + } + /* Extract the URL to use in the request. */ + uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT); + if(uc) { + curl_url_cleanup(h); + return CURLE_OUT_OF_MEMORY; + } + + curl_url_cleanup(h); + + /* target or URL */ + result = Curl_dyn_add(r, data->set.str[STRING_TARGET]? + data->set.str[STRING_TARGET]:url); + free(url); + if(result) + return (result); + + if(strcasecompare("ftp", data->state.up.scheme)) { + if(data->set.proxy_transfer_mode) { + /* when doing ftp, append ;type= if not present */ + char *type = strstr(path, ";type="); + if(type && type[6] && type[7] == 0) { + switch(Curl_raw_toupper(type[6])) { + case 'A': + case 'D': + case 'I': + break; + default: + type = NULL; + } + } + if(!type) { + result = Curl_dyn_addf(r, ";type=%c", + data->state.prefer_ascii ? 'a' : 'i'); + if(result) + return result; + } + } + } + } + + else +#else + (void)conn; /* not used in disabled-proxy builds */ +#endif + { + result = Curl_dyn_add(r, path); + if(result) + return result; + if(query) + result = Curl_dyn_addf(r, "?%s", query); + } + + return result; +} + +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) +static CURLcode set_post_reader(struct Curl_easy *data, Curl_HttpReq httpreq) +{ + CURLcode result; + + switch(httpreq) { +#ifndef CURL_DISABLE_MIME + case HTTPREQ_POST_MIME: + data->state.mimepost = &data->set.mimepost; + break; +#endif +#ifndef CURL_DISABLE_FORM_API + case HTTPREQ_POST_FORM: + /* Convert the form structure into a mime structure, then keep + the conversion */ + if(!data->state.formp) { + data->state.formp = calloc(1, sizeof(curl_mimepart)); + if(!data->state.formp) + return CURLE_OUT_OF_MEMORY; + Curl_mime_cleanpart(data->state.formp); + result = Curl_getformdata(data, data->state.formp, data->set.httppost, + data->state.fread_func); + if(result) { + Curl_safefree(data->state.formp); + return result; + } + data->state.mimepost = data->state.formp; + } + break; +#endif + default: + data->state.mimepost = NULL; + break; + } + + switch(httpreq) { + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + /* This is form posting using mime data. */ +#ifndef CURL_DISABLE_MIME + if(data->state.mimepost) { + const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type")); + + /* Read and seek body only. */ + data->state.mimepost->flags |= MIME_BODY_ONLY; + + /* Prepare the mime structure headers & set content type. */ + + if(cthdr) + for(cthdr += 13; *cthdr == ' '; cthdr++) + ; + else if(data->state.mimepost->kind == MIMEKIND_MULTIPART) + cthdr = "multipart/form-data"; + + curl_mime_headers(data->state.mimepost, data->set.headers, 0); + result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr, + NULL, MIMESTRATEGY_FORM); + if(result) + return result; + curl_mime_headers(data->state.mimepost, NULL, 0); + result = Curl_creader_set_mime(data, data->state.mimepost); + if(result) + return result; + } + else +#endif + { + result = Curl_creader_set_null(data); + } + data->state.infilesize = Curl_creader_total_length(data); + return result; + + default: + return Curl_creader_set_null(data); + } + /* never reached */ +} +#endif + +static CURLcode set_reader(struct Curl_easy *data, Curl_HttpReq httpreq) +{ + CURLcode result = CURLE_OK; + curl_off_t postsize = data->state.infilesize; + + DEBUGASSERT(data->conn); + + if(data->req.authneg) { + return Curl_creader_set_null(data); + } + + switch(httpreq) { + case HTTPREQ_PUT: /* Let's PUT the data to the server! */ + if(!postsize) + result = Curl_creader_set_null(data); + else + result = Curl_creader_set_fread(data, postsize); + return result; + +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + return set_post_reader(data, httpreq); +#endif + + case HTTPREQ_POST: + /* this is the simple POST, using x-www-form-urlencoded style */ + /* the size of the post body */ + if(!postsize) { + result = Curl_creader_set_null(data); + } + else if(data->set.postfields) { + if(postsize > 0) + result = Curl_creader_set_buf(data, data->set.postfields, + (size_t)postsize); + else + result = Curl_creader_set_null(data); + } + else { + /* we read the bytes from the callback. In case "chunked" encoding + * is forced by the application, we disregard `postsize`. This is + * a backward compatibility decision to earlier versions where + * chunking disregarded this. See issue #13229. */ + bool chunked = FALSE; + char *ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding")); + if(ptr) { + /* Some kind of TE is requested, check if 'chunked' is chosen */ + chunked = Curl_compareheader(ptr, STRCONST("Transfer-Encoding:"), + STRCONST("chunked")); + } + result = Curl_creader_set_fread(data, chunked? -1 : postsize); + } + return result; + + default: + /* HTTP GET/HEAD download, has no body, needs no Content-Length */ + data->state.infilesize = 0; + return Curl_creader_set_null(data); + } + /* not reached */ +} + +static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq) +{ + if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) && + data->state.resume_from) { + /********************************************************************** + * Resuming upload in HTTP means that we PUT or POST and that we have + * got a resume_from value set. The resume value has already created + * a Range: header that will be passed along. We need to "fast forward" + * the file the given number of bytes and decrease the assume upload + * file size before we continue this venture in the dark lands of HTTP. + * Resuming mime/form posting at an offset > 0 has no sense and is ignored. + *********************************************************************/ + + if(data->state.resume_from < 0) { + /* + * This is meant to get the size of the present remote-file by itself. + * We do not support this now. Bail out! + */ + data->state.resume_from = 0; + } + + if(data->state.resume_from && !data->req.authneg) { + /* only act on the first request */ + CURLcode result; + result = Curl_creader_resume_from(data, data->state.resume_from); + if(result) { + failf(data, "Unable to resume from offset %" CURL_FORMAT_CURL_OFF_T, + data->state.resume_from); + return result; + } + } + } + return CURLE_OK; +} + +CURLcode Curl_http_req_set_reader(struct Curl_easy *data, + Curl_HttpReq httpreq, + const char **tep) +{ + CURLcode result = CURLE_OK; + const char *ptr; + + result = set_reader(data, httpreq); + if(result) + return result; + + result = http_resume(data, httpreq); + if(result) + return result; + + ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding")); + if(ptr) { + /* Some kind of TE is requested, check if 'chunked' is chosen */ + data->req.upload_chunky = + Curl_compareheader(ptr, + STRCONST("Transfer-Encoding:"), STRCONST("chunked")); + if(data->req.upload_chunky && + Curl_use_http_1_1plus(data, data->conn) && + (data->conn->httpversion >= 20)) { + infof(data, "suppressing chunked transfer encoding on connection " + "using HTTP version 2 or higher"); + data->req.upload_chunky = FALSE; + } + } + else { + curl_off_t req_clen = Curl_creader_total_length(data); + + if(req_clen < 0) { + /* indeterminate request content length */ + if(Curl_use_http_1_1plus(data, data->conn)) { + /* On HTTP/1.1, enable chunked, on HTTP/2 and later we do not + * need it */ + data->req.upload_chunky = (data->conn->httpversion < 20); + } + else { + failf(data, "Chunky upload is not supported by HTTP 1.0"); + return CURLE_UPLOAD_FAILED; + } + } + else { + /* else, no chunky upload */ + data->req.upload_chunky = FALSE; + } + + if(data->req.upload_chunky) + *tep = "Transfer-Encoding: chunked\r\n"; + } + return result; +} + +static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, + bool *announced_exp100) +{ + CURLcode result; + char *ptr; + + *announced_exp100 = FALSE; + /* Avoid Expect: 100-continue if Upgrade: is used */ + if(data->req.upgr101 != UPGR101_INIT) + return CURLE_OK; + + /* For really small puts we do not use Expect: headers at all, and for + the somewhat bigger ones we allow the app to disable it. Just make + sure that the expect100header is always set to the preferred value + here. */ + ptr = Curl_checkheaders(data, STRCONST("Expect")); + if(ptr) { + *announced_exp100 = + Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); + } + else if(!data->state.disableexpect && + Curl_use_http_1_1plus(data, data->conn) && + (data->conn->httpversion < 20)) { + /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an + Expect: 100-continue to the headers which actually speeds up post + operations (as there is one packet coming back from the web server) */ + curl_off_t client_len = Curl_creader_client_length(data); + if(client_len > EXPECT_100_THRESHOLD || client_len < 0) { + result = Curl_dyn_addn(r, STRCONST("Expect: 100-continue\r\n")); + if(result) + return result; + *announced_exp100 = TRUE; + } + } + return CURLE_OK; +} + +CURLcode Curl_http_req_complete(struct Curl_easy *data, + struct dynbuf *r, Curl_HttpReq httpreq) +{ + CURLcode result = CURLE_OK; + curl_off_t req_clen; + bool announced_exp100 = FALSE; + + DEBUGASSERT(data->conn); +#ifndef USE_HYPER + if(data->req.upload_chunky) { + result = Curl_httpchunk_add_reader(data); + if(result) + return result; + } +#endif + + /* Get the request body length that has been set up */ + req_clen = Curl_creader_total_length(data); + switch(httpreq) { + case HTTPREQ_PUT: + case HTTPREQ_POST: +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: +#endif + /* We only set Content-Length and allow a custom Content-Length if + we do not upload data chunked, as RFC2616 forbids us to set both + kinds of headers (Transfer-Encoding: chunked and Content-Length). + We do not override a custom "Content-Length" header, but during + authentication negotiation that header is suppressed. + */ + if(req_clen >= 0 && !data->req.upload_chunky && + (data->req.authneg || + !Curl_checkheaders(data, STRCONST("Content-Length")))) { + /* we allow replacing this header if not during auth negotiation, + although it is not very wise to actually set your own */ + result = Curl_dyn_addf(r, + "Content-Length: %" CURL_FORMAT_CURL_OFF_T + "\r\n", req_clen); + } + if(result) + goto out; + +#ifndef CURL_DISABLE_MIME + /* Output mime-generated headers. */ + if(data->state.mimepost && + ((httpreq == HTTPREQ_POST_FORM) || (httpreq == HTTPREQ_POST_MIME))) { + struct curl_slist *hdr; + + for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) { + result = Curl_dyn_addf(r, "%s\r\n", hdr->data); + if(result) + goto out; + } + } +#endif + if(httpreq == HTTPREQ_POST) { + if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { + result = Curl_dyn_addn(r, STRCONST("Content-Type: application/" + "x-www-form-urlencoded\r\n")); + if(result) + goto out; + } + } + result = addexpect(data, r, &announced_exp100); + if(result) + goto out; + break; + default: + break; + } + + /* end of headers */ + result = Curl_dyn_addn(r, STRCONST("\r\n")); + if(!result) { + Curl_pgrsSetUploadSize(data, req_clen); + if(announced_exp100) + result = http_exp100_add_reader(data); + } + +out: + if(!result) { + /* setup variables for the upcoming transfer */ + Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE); + } + return result; +} + +#if !defined(CURL_DISABLE_COOKIES) + +CURLcode Curl_http_cookies(struct Curl_easy *data, + struct connectdata *conn, + struct dynbuf *r) +{ + CURLcode result = CURLE_OK; + char *addcookies = NULL; + bool linecap = FALSE; + if(data->set.str[STRING_COOKIE] && + !Curl_checkheaders(data, STRCONST("Cookie"))) + addcookies = data->set.str[STRING_COOKIE]; + + if(data->cookies || addcookies) { + struct Cookie *co = NULL; /* no cookies from start */ + int count = 0; + + if(data->cookies && data->state.cookie_engine) { + const char *host = data->state.aptr.cookiehost ? + data->state.aptr.cookiehost : conn->host.name; + const bool secure_context = + conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || + strcasecompare("localhost", host) || + !strcmp(host, "127.0.0.1") || + !strcmp(host, "::1") ? TRUE : FALSE; + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + co = Curl_cookie_getlist(data, data->cookies, host, data->state.up.path, + secure_context); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + if(co) { + struct Cookie *store = co; + size_t clen = 8; /* hold the size of the generated Cookie: header */ + /* now loop through all cookies that matched */ + while(co) { + if(co->value) { + size_t add; + if(!count) { + result = Curl_dyn_addn(r, STRCONST("Cookie: ")); + if(result) + break; + } + add = strlen(co->name) + strlen(co->value) + 1; + if(clen + add >= MAX_COOKIE_HEADER_LEN) { + infof(data, "Restricted outgoing cookies due to header size, " + "'%s' not sent", co->name); + linecap = TRUE; + break; + } + result = Curl_dyn_addf(r, "%s%s=%s", count?"; ":"", + co->name, co->value); + if(result) + break; + clen += add + (count ? 2 : 0); + count++; + } + co = co->next; /* next cookie please */ + } + Curl_cookie_freelist(store); + } + if(addcookies && !result && !linecap) { + if(!count) + result = Curl_dyn_addn(r, STRCONST("Cookie: ")); + if(!result) { + result = Curl_dyn_addf(r, "%s%s", count?"; ":"", addcookies); + count++; + } + } + if(count && !result) + result = Curl_dyn_addn(r, STRCONST("\r\n")); + + if(result) + return result; + } + return result; +} +#endif + +CURLcode Curl_http_range(struct Curl_easy *data, + Curl_HttpReq httpreq) +{ + if(data->state.use_range) { + /* + * A range is selected. We use different headers whether we are downloading + * or uploading and we always let customized headers override our internal + * ones if any such are specified. + */ + if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) && + !Curl_checkheaders(data, STRCONST("Range"))) { + /* if a line like this was already allocated, free the previous one */ + free(data->state.aptr.rangeline); + data->state.aptr.rangeline = aprintf("Range: bytes=%s\r\n", + data->state.range); + } + else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) && + !Curl_checkheaders(data, STRCONST("Content-Range"))) { + curl_off_t req_clen = Curl_creader_total_length(data); + /* if a line like this was already allocated, free the previous one */ + free(data->state.aptr.rangeline); + + if(data->set.set_resume_from < 0) { + /* Upload resume was asked for, but we do not know the size of the + remote part so we tell the server (and act accordingly) that we + upload the whole file (again) */ + data->state.aptr.rangeline = + aprintf("Content-Range: bytes 0-%" CURL_FORMAT_CURL_OFF_T + "/%" CURL_FORMAT_CURL_OFF_T "\r\n", + req_clen - 1, req_clen); + + } + else if(data->state.resume_from) { + /* This is because "resume" was selected */ + /* TODO: not sure if we want to send this header during authentication + * negotiation, but test1084 checks for it. In which case we have a + * "null" client reader installed that gives an unexpected length. */ + curl_off_t total_len = data->req.authneg? + data->state.infilesize : + (data->state.resume_from + req_clen); + data->state.aptr.rangeline = + aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T + "/%" CURL_FORMAT_CURL_OFF_T "\r\n", + data->state.range, total_len-1, total_len); + } + else { + /* Range was selected and then we just pass the incoming range and + append total size */ + data->state.aptr.rangeline = + aprintf("Content-Range: bytes %s/%" CURL_FORMAT_CURL_OFF_T "\r\n", + data->state.range, req_clen); + } + if(!data->state.aptr.rangeline) + return CURLE_OUT_OF_MEMORY; + } + } + return CURLE_OK; +} + +CURLcode Curl_http_firstwrite(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct SingleRequest *k = &data->req; + + if(data->req.newurl) { + if(conn->bits.close) { + /* Abort after the headers if "follow Location" is set + and we are set to close anyway. */ + k->keepon &= ~KEEP_RECV; + k->done = TRUE; + return CURLE_OK; + } + /* We have a new URL to load, but since we want to be able to reuse this + connection properly, we read the full response in "ignore more" */ + k->ignorebody = TRUE; + infof(data, "Ignoring the response-body"); + } + if(data->state.resume_from && !k->content_range && + (data->state.httpreq == HTTPREQ_GET) && + !k->ignorebody) { + + if(k->size == data->state.resume_from) { + /* The resume point is at the end of file, consider this fine even if it + does not allow resume from here. */ + infof(data, "The entire document is already downloaded"); + streamclose(conn, "already downloaded"); + /* Abort download */ + k->keepon &= ~KEEP_RECV; + k->done = TRUE; + return CURLE_OK; + } + + /* we wanted to resume a download, although the server does not seem to + * support this and we did this with a GET (if it was not a GET we did a + * POST or PUT resume) */ + failf(data, "HTTP server does not seem to support " + "byte ranges. Cannot resume."); + return CURLE_RANGE_ERROR; + } + + if(data->set.timecondition && !data->state.range) { + /* A time condition has been set AND no ranges have been requested. This + seems to be what chapter 13.3.4 of RFC 2616 defines to be the correct + action for an HTTP/1.1 client */ + + if(!Curl_meets_timecondition(data, k->timeofdoc)) { + k->done = TRUE; + /* We are simulating an HTTP 304 from server so we return + what should have been returned from the server */ + data->info.httpcode = 304; + infof(data, "Simulate an HTTP 304 response"); + /* we abort the transfer before it is completed == we ruin the + reuse ability. Close the connection */ + streamclose(conn, "Simulated 304 handling"); + return CURLE_OK; + } + } /* we have a time condition */ + + return CURLE_OK; +} + +#ifdef HAVE_LIBZ +CURLcode Curl_transferencode(struct Curl_easy *data) +{ + if(!Curl_checkheaders(data, STRCONST("TE")) && + data->set.http_transfer_encoding) { + /* When we are to insert a TE: header in the request, we must also insert + TE in a Connection: header, so we need to merge the custom provided + Connection: header and prevent the original to get sent. Note that if + the user has inserted his/her own TE: header we do not do this magic + but then assume that the user will handle it all! */ + char *cptr = Curl_checkheaders(data, STRCONST("Connection")); +#define TE_HEADER "TE: gzip\r\n" + + Curl_safefree(data->state.aptr.te); + + if(cptr) { + cptr = Curl_copy_header_value(cptr); + if(!cptr) + return CURLE_OUT_OF_MEMORY; + } + + /* Create the (updated) Connection: header */ + data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER, + cptr ? cptr : "", (cptr && *cptr) ? ", ":""); + + free(cptr); + if(!data->state.aptr.te) + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; +} +#endif + +#ifndef USE_HYPER +/* + * Curl_http() gets called from the generic multi_do() function when an HTTP + * request is to be performed. This creates and sends a properly constructed + * HTTP request. + */ +CURLcode Curl_http(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + Curl_HttpReq httpreq; + const char *te = ""; /* transfer-encoding */ + const char *request; + const char *httpstring; + struct dynbuf req; + char *altused = NULL; + const char *p_accept; /* Accept: string */ + + /* Always consider the DO phase done after this function call, even if there + may be parts of the request that are not yet sent, since we can deal with + the rest of the request in the PERFORM phase. */ + *done = TRUE; + + switch(conn->alpn) { + case CURL_HTTP_VERSION_3: + DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET)); + break; + case CURL_HTTP_VERSION_2: +#ifndef CURL_DISABLE_PROXY + if(!Curl_conn_is_http2(data, conn, FIRSTSOCKET) && + conn->bits.proxy && !conn->bits.tunnel_proxy + ) { + result = Curl_http2_switch(data, conn, FIRSTSOCKET); + if(result) + goto fail; + } + else +#endif + DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET)); + break; + case CURL_HTTP_VERSION_1_1: + /* continue with HTTP/1.x when explicitly requested */ + break; + default: + /* Check if user wants to use HTTP/2 with clear TCP */ + if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) { + DEBUGF(infof(data, "HTTP/2 over clean TCP")); + result = Curl_http2_switch(data, conn, FIRSTSOCKET); + if(result) + goto fail; + } + break; + } + + /* Add collecting of headers written to client. For a new connection, + * we might have done that already, but reuse + * or multiplex needs it here as well. */ + result = Curl_headers_init(data); + if(result) + goto fail; + + result = Curl_http_host(data, conn); + if(result) + goto fail; + + result = Curl_http_useragent(data); + if(result) + goto fail; + + Curl_http_method(data, conn, &request, &httpreq); + + /* setup the authentication headers */ + { + char *pq = NULL; + if(data->state.up.query) { + pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); + if(!pq) + return CURLE_OUT_OF_MEMORY; + } + result = Curl_http_output_auth(data, conn, request, httpreq, + (pq ? pq : data->state.up.path), FALSE); + free(pq); + if(result) + goto fail; + } + + Curl_safefree(data->state.aptr.ref); + if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) { + data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); + if(!data->state.aptr.ref) + return CURLE_OUT_OF_MEMORY; + } + + if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && + data->set.str[STRING_ENCODING]) { + Curl_safefree(data->state.aptr.accept_encoding); + data->state.aptr.accept_encoding = + aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); + if(!data->state.aptr.accept_encoding) + return CURLE_OUT_OF_MEMORY; + } + else + Curl_safefree(data->state.aptr.accept_encoding); + +#ifdef HAVE_LIBZ + /* we only consider transfer-encoding magic if libz support is built-in */ + result = Curl_transferencode(data); + if(result) + goto fail; +#endif + + result = Curl_http_req_set_reader(data, httpreq, &te); + if(result) + goto fail; + + p_accept = Curl_checkheaders(data, + STRCONST("Accept"))?NULL:"Accept: */*\r\n"; + + result = Curl_http_range(data, httpreq); + if(result) + goto fail; + + httpstring = get_http_string(data, conn); + + /* initialize a dynamic send-buffer */ + Curl_dyn_init(&req, DYN_HTTP_REQUEST); + + /* make sure the header buffer is reset - if there are leftovers from a + previous transfer */ + Curl_dyn_reset(&data->state.headerb); + + /* add the main request stuff */ + /* GET/HEAD/POST/PUT */ + result = Curl_dyn_addf(&req, "%s ", request); + if(!result) + result = Curl_http_target(data, conn, &req); + if(result) { + Curl_dyn_free(&req); + goto fail; + } + +#ifndef CURL_DISABLE_ALTSVC + if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) { + altused = aprintf("Alt-Used: %s:%d\r\n", + conn->conn_to_host.name, conn->conn_to_port); + if(!altused) { + Curl_dyn_free(&req); + return CURLE_OUT_OF_MEMORY; + } + } +#endif + result = + Curl_dyn_addf(&req, + " HTTP/%s\r\n" /* HTTP version */ + "%s" /* host */ + "%s" /* proxyuserpwd */ + "%s" /* userpwd */ + "%s" /* range */ + "%s" /* user agent */ + "%s" /* accept */ + "%s" /* TE: */ + "%s" /* accept-encoding */ + "%s" /* referer */ + "%s" /* Proxy-Connection */ + "%s" /* transfer-encoding */ + "%s",/* Alt-Used */ + + httpstring, + (data->state.aptr.host?data->state.aptr.host:""), +#ifndef CURL_DISABLE_PROXY + data->state.aptr.proxyuserpwd? + data->state.aptr.proxyuserpwd:"", +#else + "", +#endif + data->state.aptr.userpwd?data->state.aptr.userpwd:"", + (data->state.use_range && data->state.aptr.rangeline)? + data->state.aptr.rangeline:"", + (data->set.str[STRING_USERAGENT] && + *data->set.str[STRING_USERAGENT] && + data->state.aptr.uagent)? + data->state.aptr.uagent:"", + p_accept?p_accept:"", + data->state.aptr.te?data->state.aptr.te:"", + (data->set.str[STRING_ENCODING] && + *data->set.str[STRING_ENCODING] && + data->state.aptr.accept_encoding)? + data->state.aptr.accept_encoding:"", + (data->state.referer && data->state.aptr.ref)? + data->state.aptr.ref:"" /* Referer: */, +#ifndef CURL_DISABLE_PROXY + (conn->bits.httpproxy && + !conn->bits.tunnel_proxy && + !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && + !Curl_checkProxyheaders(data, + conn, + STRCONST("Proxy-Connection")))? + "Proxy-Connection: Keep-Alive\r\n":"", +#else + "", +#endif + te, + altused ? altused : "" + ); + + /* clear userpwd and proxyuserpwd to avoid reusing old credentials + * from reused connections */ + Curl_safefree(data->state.aptr.userpwd); +#ifndef CURL_DISABLE_PROXY + Curl_safefree(data->state.aptr.proxyuserpwd); +#endif + free(altused); + + if(result) { + Curl_dyn_free(&req); + goto fail; + } + + if(!(conn->handler->flags&PROTOPT_SSL) && + conn->httpversion < 20 && + (data->state.httpwant == CURL_HTTP_VERSION_2)) { + /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done + over SSL */ + result = Curl_http2_request_upgrade(&req, data); + if(result) { + Curl_dyn_free(&req); + return result; + } + } + + result = Curl_http_cookies(data, conn, &req); +#ifdef USE_WEBSOCKETS + if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) + result = Curl_ws_request(data, &req); +#endif + if(!result) + result = Curl_add_timecondition(data, &req); + if(!result) + result = Curl_add_custom_headers(data, FALSE, &req); + + if(!result) { + /* req_send takes ownership of the 'req' memory on success */ + result = Curl_http_req_complete(data, &req, httpreq); + if(!result) + result = Curl_req_send(data, &req); + } + Curl_dyn_free(&req); + if(result) + goto fail; + + if((conn->httpversion >= 20) && data->req.upload_chunky) + /* upload_chunky was set above to set up the request in a chunky fashion, + but is disabled here again to avoid that the chunked encoded version is + actually used when sending the request body over h2 */ + data->req.upload_chunky = FALSE; +fail: + if(CURLE_TOO_LARGE == result) + failf(data, "HTTP request too large"); + return result; +} + +#endif /* USE_HYPER */ + +typedef enum { + STATUS_UNKNOWN, /* not enough data to tell yet */ + STATUS_DONE, /* a status line was read */ + STATUS_BAD /* not a status line */ +} statusline; + + +/* Check a string for a prefix. Check no more than 'len' bytes */ +static bool checkprefixmax(const char *prefix, const char *buffer, size_t len) +{ + size_t ch = CURLMIN(strlen(prefix), len); + return curl_strnequal(prefix, buffer, ch); +} + +/* + * checkhttpprefix() + * + * Returns TRUE if member of the list matches prefix of string + */ +static statusline +checkhttpprefix(struct Curl_easy *data, + const char *s, size_t len) +{ + struct curl_slist *head = data->set.http200aliases; + statusline rc = STATUS_BAD; + statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN; + + while(head) { + if(checkprefixmax(head->data, s, len)) { + rc = onmatch; + break; + } + head = head->next; + } + + if((rc != STATUS_DONE) && (checkprefixmax("HTTP/", s, len))) + rc = onmatch; + + return rc; +} + +#ifndef CURL_DISABLE_RTSP +static statusline +checkrtspprefix(struct Curl_easy *data, + const char *s, size_t len) +{ + statusline result = STATUS_BAD; + statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN; + (void)data; /* unused */ + if(checkprefixmax("RTSP/", s, len)) + result = onmatch; + + return result; +} +#endif /* CURL_DISABLE_RTSP */ + +static statusline +checkprotoprefix(struct Curl_easy *data, struct connectdata *conn, + const char *s, size_t len) +{ +#ifndef CURL_DISABLE_RTSP + if(conn->handler->protocol & CURLPROTO_RTSP) + return checkrtspprefix(data, s, len); +#else + (void)conn; +#endif /* CURL_DISABLE_RTSP */ + + return checkhttpprefix(data, s, len); +} + +/* HTTP header has field name `n` (a string constant) */ +#define HD_IS(hd, hdlen, n) \ + (((hdlen) >= (sizeof(n)-1)) && curl_strnequal((n), (hd), (sizeof(n)-1))) + +#define HD_VAL(hd, hdlen, n) \ + ((((hdlen) >= (sizeof(n)-1)) && \ + curl_strnequal((n), (hd), (sizeof(n)-1)))? (hd + (sizeof(n)-1)) : NULL) + +/* HTTP header has field name `n` (a string constant) and contains `v` + * (a string constant) in its value(s) */ +#define HD_IS_AND_SAYS(hd, hdlen, n, v) \ + (HD_IS(hd, hdlen, n) && \ + ((hdlen) > ((sizeof(n)-1) + (sizeof(v)-1))) && \ + Curl_compareheader(hd, STRCONST(n), STRCONST(v))) + +/* + * Curl_http_header() parses a single response header. + */ +CURLcode Curl_http_header(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + struct connectdata *conn = data->conn; + CURLcode result; + struct SingleRequest *k = &data->req; + const char *v; + + switch(hd[0]) { + case 'a': + case 'A': +#ifndef CURL_DISABLE_ALTSVC + v = (data->asi && + ((data->conn->handler->flags & PROTOPT_SSL) || +#ifdef DEBUGBUILD + /* allow debug builds to circumvent the HTTPS restriction */ + getenv("CURL_ALTSVC_HTTP") +#else + 0 +#endif + ))? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL; + if(v) { + /* the ALPN of the current request */ + enum alpnid id = (conn->httpversion == 30)? ALPN_h3 : + (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; + return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name, + curlx_uitous((unsigned int)conn->remote_port)); + } +#endif + break; + case 'c': + case 'C': + /* Check for Content-Length: header lines to get size */ + v = (!k->http_bodyless && !data->set.ignorecl)? + HD_VAL(hd, hdlen, "Content-Length:") : NULL; + if(v) { + curl_off_t contentlength; + CURLofft offt = curlx_strtoofft(v, NULL, 10, &contentlength); + + if(offt == CURL_OFFT_OK) { + k->size = contentlength; + k->maxdownload = k->size; + } + else if(offt == CURL_OFFT_FLOW) { + /* out of range */ + if(data->set.max_filesize) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + streamclose(conn, "overflow content-length"); + infof(data, "Overflow Content-Length: value"); + } + else { + /* negative or just rubbish - bad HTTP */ + failf(data, "Invalid Content-Length: value"); + return CURLE_WEIRD_SERVER_REPLY; + } + return CURLE_OK; + } + v = (!k->http_bodyless && data->set.str[STRING_ENCODING])? + HD_VAL(hd, hdlen, "Content-Encoding:") : NULL; + if(v) { + /* + * Process Content-Encoding. Look for the values: identity, + * gzip, deflate, compress, x-gzip and x-compress. x-gzip and + * x-compress are the same as gzip and compress. (Sec 3.5 RFC + * 2616). zlib cannot handle compress. However, errors are + * handled further down when the response body is processed + */ + return Curl_build_unencoding_stack(data, v, FALSE); + } + /* check for Content-Type: header lines to get the MIME-type */ + v = HD_VAL(hd, hdlen, "Content-Type:"); + if(v) { + char *contenttype = Curl_copy_header_value(hd); + if(!contenttype) + return CURLE_OUT_OF_MEMORY; + if(!*contenttype) + /* ignore empty data */ + free(contenttype); + else { + Curl_safefree(data->info.contenttype); + data->info.contenttype = contenttype; + } + return CURLE_OK; + } + if(HD_IS_AND_SAYS(hd, hdlen, "Connection:", "close")) { + /* + * [RFC 2616, section 8.1.2.1] + * "Connection: close" is HTTP/1.1 language and means that + * the connection will close when this request has been + * served. + */ + streamclose(conn, "Connection: close used"); + return CURLE_OK; + } + if((conn->httpversion == 10) && + HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) { + /* + * An HTTP/1.0 reply with the 'Connection: keep-alive' line + * tells us the connection will be kept alive for our + * pleasure. Default action for 1.0 is to close. + * + * [RFC2068, section 19.7.1] */ + connkeep(conn, "Connection keep-alive"); + infof(data, "HTTP/1.0 connection set to keep alive"); + return CURLE_OK; + } + v = !k->http_bodyless? HD_VAL(hd, hdlen, "Content-Range:") : NULL; + if(v) { + /* Content-Range: bytes [num]- + Content-Range: bytes: [num]- + Content-Range: [num]- + Content-Range: [asterisk]/[total] + + The second format was added since Sun's webserver + JavaWebServer/1.1.1 obviously sends the header this way! + The third added since some servers use that! + The fourth means the requested range was unsatisfied. + */ + + const char *ptr = v; + + /* Move forward until first digit or asterisk */ + while(*ptr && !ISDIGIT(*ptr) && *ptr != '*') + ptr++; + + /* if it truly stopped on a digit */ + if(ISDIGIT(*ptr)) { + if(!curlx_strtoofft(ptr, NULL, 10, &k->offset)) { + if(data->state.resume_from == k->offset) + /* we asked for a resume and we got it */ + k->content_range = TRUE; + } + } + else if(k->httpcode < 300) + data->state.resume_from = 0; /* get everything */ + } + break; + case 'l': + case 'L': + v = (!k->http_bodyless && + (data->set.timecondition || data->set.get_filetime))? + HD_VAL(hd, hdlen, "Last-Modified:") : NULL; + if(v) { + k->timeofdoc = Curl_getdate_capped(v); + if(data->set.get_filetime) + data->info.filetime = k->timeofdoc; + return CURLE_OK; + } + if((k->httpcode >= 300 && k->httpcode < 400) && + HD_IS(hd, hdlen, "Location:") && + !data->req.location) { + /* this is the URL that the server advises us to use instead */ + char *location = Curl_copy_header_value(hd); + if(!location) + return CURLE_OUT_OF_MEMORY; + if(!*location) + /* ignore empty data */ + free(location); + else { + data->req.location = location; + + if(data->set.http_follow_location) { + DEBUGASSERT(!data->req.newurl); + data->req.newurl = strdup(data->req.location); /* clone */ + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + + /* some cases of POST and PUT etc needs to rewind the data + stream at this point */ + result = http_perhapsrewind(data, conn); + if(result) + return result; + + /* mark the next request as a followed location: */ + data->state.this_is_a_follow = TRUE; + } + } + } + break; + case 'p': + case 'P': +#ifndef CURL_DISABLE_PROXY + v = HD_VAL(hd, hdlen, "Proxy-Connection:"); + if(v) { + if((conn->httpversion == 10) && conn->bits.httpproxy && + HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) { + /* + * When an HTTP/1.0 reply comes when using a proxy, the + * 'Proxy-Connection: keep-alive' line tells us the + * connection will be kept alive for our pleasure. + * Default action for 1.0 is to close. + */ + connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */ + infof(data, "HTTP/1.0 proxy connection set to keep alive"); + } + else if((conn->httpversion == 11) && conn->bits.httpproxy && + HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) { + /* + * We get an HTTP/1.1 response from a proxy and it says it will + * close down after this transfer. + */ + connclose(conn, "Proxy-Connection: asked to close after done"); + infof(data, "HTTP/1.1 proxy connection set close"); + } + return CURLE_OK; + } +#endif + if((407 == k->httpcode) && HD_IS(hd, hdlen, "Proxy-authenticate:")) { + char *auth = Curl_copy_header_value(hd); + if(!auth) + return CURLE_OUT_OF_MEMORY; + result = Curl_http_input_auth(data, TRUE, auth); + free(auth); + return result; + } +#ifdef USE_SPNEGO + if(HD_IS(hd, hdlen, "Persistent-Auth:")) { + struct negotiatedata *negdata = &conn->negotiate; + struct auth *authp = &data->state.authhost; + if(authp->picked == CURLAUTH_NEGOTIATE) { + char *persistentauth = Curl_copy_header_value(hd); + if(!persistentauth) + return CURLE_OUT_OF_MEMORY; + negdata->noauthpersist = checkprefix("false", persistentauth)? + TRUE:FALSE; + negdata->havenoauthpersist = TRUE; + infof(data, "Negotiate: noauthpersist -> %d, header part: %s", + negdata->noauthpersist, persistentauth); + free(persistentauth); + } + } +#endif + break; + case 'r': + case 'R': + v = HD_VAL(hd, hdlen, "Retry-After:"); + if(v) { + /* Retry-After = HTTP-date / delay-seconds */ + curl_off_t retry_after = 0; /* zero for unknown or "now" */ + /* Try it as a decimal number, if it works it is not a date */ + (void)curlx_strtoofft(v, NULL, 10, &retry_after); + if(!retry_after) { + time_t date = Curl_getdate_capped(v); + if(-1 != date) + /* convert date to number of seconds into the future */ + retry_after = date - time(NULL); + } + data->info.retry_after = retry_after; /* store it */ + return CURLE_OK; + } + break; + case 's': + case 'S': +#if !defined(CURL_DISABLE_COOKIES) + v = (data->cookies && data->state.cookie_engine)? + HD_VAL(hd, hdlen, "Set-Cookie:") : NULL; + if(v) { + /* If there is a custom-set Host: name, use it here, or else use + * real peer hostname. */ + const char *host = data->state.aptr.cookiehost? + data->state.aptr.cookiehost:conn->host.name; + const bool secure_context = + conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || + strcasecompare("localhost", host) || + !strcmp(host, "127.0.0.1") || + !strcmp(host, "::1") ? TRUE : FALSE; + + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, + CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host, + data->state.up.path, secure_context); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + return CURLE_OK; + } +#endif +#ifndef CURL_DISABLE_HSTS + /* If enabled, the header is incoming and this is over HTTPS */ + v = (data->hsts && + ((conn->handler->flags & PROTOPT_SSL) || +#ifdef DEBUGBUILD + /* allow debug builds to circumvent the HTTPS restriction */ + getenv("CURL_HSTS_HTTP") +#else + 0 +#endif + ) + )? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL; + if(v) { + CURLcode check = + Curl_hsts_parse(data->hsts, conn->host.name, v); + if(check) + infof(data, "Illegal STS header skipped"); +#ifdef DEBUGBUILD + else + infof(data, "Parsed STS header fine (%zu entries)", + data->hsts->list.size); +#endif + } +#endif + break; + case 't': + case 'T': + /* RFC 9112, ch. 6.1 + * "Transfer-Encoding MAY be sent in a response to a HEAD request or + * in a 304 (Not Modified) response (Section 15.4.5 of [HTTP]) to a + * GET request, neither of which includes a message body, to indicate + * that the origin server would have applied a transfer coding to the + * message body if the request had been an unconditional GET." + * + * Read: in these cases the 'Transfer-Encoding' does not apply + * to any data following the response headers. Do not add any decoders. + */ + v = (!k->http_bodyless && + (data->state.httpreq != HTTPREQ_HEAD) && + (k->httpcode != 304))? + HD_VAL(hd, hdlen, "Transfer-Encoding:") : NULL; + if(v) { + /* One or more encodings. We check for chunked and/or a compression + algorithm. */ + result = Curl_build_unencoding_stack(data, v, TRUE); + if(result) + return result; + if(!k->chunk && data->set.http_transfer_encoding) { + /* if this is not chunked, only close can signal the end of this + * transfer as Content-Length is said not to be trusted for + * transfer-encoding! */ + connclose(conn, "HTTP/1.1 transfer-encoding without chunks"); + k->ignore_cl = TRUE; + } + return CURLE_OK; + } + break; + case 'w': + case 'W': + if((401 == k->httpcode) && HD_IS(hd, hdlen, "WWW-Authenticate:")) { + char *auth = Curl_copy_header_value(hd); + if(!auth) + return CURLE_OUT_OF_MEMORY; + result = Curl_http_input_auth(data, FALSE, auth); + free(auth); + return result; + } + break; + } + + if(conn->handler->protocol & CURLPROTO_RTSP) { + result = Curl_rtsp_parseheader(data, hd); + if(result) + return result; + } + return CURLE_OK; +} + +/* + * Called after the first HTTP response line (the status line) has been + * received and parsed. + */ +CURLcode Curl_http_statusline(struct Curl_easy *data, + struct connectdata *conn) +{ + struct SingleRequest *k = &data->req; + + switch(k->httpversion) { + case 10: + case 11: +#ifdef USE_HTTP2 + case 20: +#endif +#ifdef USE_HTTP3 + case 30: +#endif + /* no major version switch mid-connection */ + if(conn->httpversion && + (k->httpversion/10 != conn->httpversion/10)) { + failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)", + conn->httpversion/10, k->httpversion/10); + return CURLE_UNSUPPORTED_PROTOCOL; + } + break; + default: + failf(data, "Unsupported HTTP version (%u.%d) in response", + k->httpversion/10, k->httpversion%10); + return CURLE_UNSUPPORTED_PROTOCOL; + } + + data->info.httpcode = k->httpcode; + data->info.httpversion = k->httpversion; + conn->httpversion = (unsigned char)k->httpversion; + + if(!data->state.httpversion || data->state.httpversion > k->httpversion) + /* store the lowest server version we encounter */ + data->state.httpversion = (unsigned char)k->httpversion; + + /* + * This code executes as part of processing the header. As a + * result, it is not totally clear how to interpret the + * response code yet as that depends on what other headers may + * be present. 401 and 407 may be errors, but may be OK + * depending on how authentication is working. Other codes + * are definitely errors, so give up here. + */ + if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && + k->httpcode == 416) { + /* "Requested Range Not Satisfiable", just proceed and + pretend this is no error */ + k->ignorebody = TRUE; /* Avoid appending error msg to good data. */ + } + + if(k->httpversion == 10) { + /* Default action for HTTP/1.0 must be to close, unless + we get one of those fancy headers that tell us the + server keeps it open for us! */ + infof(data, "HTTP 1.0, assume close after body"); + connclose(conn, "HTTP/1.0 close after body"); + } + else if(k->httpversion == 20 || + (k->upgr101 == UPGR101_H2 && k->httpcode == 101)) { + DEBUGF(infof(data, "HTTP/2 found, allow multiplexing")); + /* HTTP/2 cannot avoid multiplexing since it is a core functionality + of the protocol */ + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + } + + k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200; + switch(k->httpcode) { + case 304: + /* (quote from RFC2616, section 10.3.5): The 304 response + * MUST NOT contain a message-body, and thus is always + * terminated by the first empty line after the header + * fields. */ + if(data->set.timecondition) + data->info.timecond = TRUE; + FALLTHROUGH(); + case 204: + /* (quote from RFC2616, section 10.2.5): The server has + * fulfilled the request but does not need to return an + * entity-body ... The 204 response MUST NOT include a + * message-body, and thus is always terminated by the first + * empty line after the header fields. */ + k->size = 0; + k->maxdownload = 0; + k->http_bodyless = TRUE; + break; + default: + break; + } + return CURLE_OK; +} + +/* Content-Length must be ignored if any Transfer-Encoding is present in the + response. Refer to RFC 7230 section 3.3.3 and RFC2616 section 4.4. This is + figured out here after all headers have been received but before the final + call to the user's header callback, so that a valid content length can be + retrieved by the user in the final call. */ +CURLcode Curl_http_size(struct Curl_easy *data) +{ + struct SingleRequest *k = &data->req; + if(data->req.ignore_cl || k->chunk) { + k->size = k->maxdownload = -1; + } + else if(k->size != -1) { + if(data->set.max_filesize && + k->size > data->set.max_filesize) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + Curl_pgrsSetDownloadSize(data, k->size); + k->maxdownload = k->size; + } + return CURLE_OK; +} + +static CURLcode verify_header(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + struct SingleRequest *k = &data->req; + char *ptr = memchr(hd, 0x00, hdlen); + if(ptr) { + /* this is bad, bail out */ + failf(data, "Nul byte in header"); + return CURLE_WEIRD_SERVER_REPLY; + } + if(k->headerline < 2) + /* the first "header" is the status-line and it has no colon */ + return CURLE_OK; + if(((hd[0] == ' ') || (hd[0] == '\t')) && k->headerline > 2) + /* line folding, cannot happen on line 2 */ + ; + else { + ptr = memchr(hd, ':', hdlen); + if(!ptr) { + /* this is bad, bail out */ + failf(data, "Header without colon"); + return CURLE_WEIRD_SERVER_REPLY; + } + } + return CURLE_OK; +} + +CURLcode Curl_bump_headersize(struct Curl_easy *data, + size_t delta, + bool connect_only) +{ + size_t bad = 0; + unsigned int max = MAX_HTTP_RESP_HEADER_SIZE; + if(delta < MAX_HTTP_RESP_HEADER_SIZE) { + data->info.header_size += (unsigned int)delta; + data->req.allheadercount += (unsigned int)delta; + if(!connect_only) + data->req.headerbytecount += (unsigned int)delta; + if(data->req.allheadercount > max) + bad = data->req.allheadercount; + else if(data->info.header_size > (max * 20)) { + bad = data->info.header_size; + max *= 20; + } + } + else + bad = data->req.allheadercount + delta; + if(bad) { + failf(data, "Too large response headers: %zu > %u", bad, max); + return CURLE_RECV_ERROR; + } + return CURLE_OK; +} + +static CURLcode http_write_header(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + CURLcode result; + int writetype; + + /* now, only output this if the header AND body are requested: + */ + Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen); + + writetype = CLIENTWRITE_HEADER | + ((data->req.httpcode/100 == 1) ? CLIENTWRITE_1XX : 0); + + result = Curl_client_write(data, writetype, hd, hdlen); + if(result) + return result; + + result = Curl_bump_headersize(data, hdlen, FALSE); + if(result) + return result; + + data->req.deductheadercount = (100 <= data->req.httpcode && + 199 >= data->req.httpcode)? + data->req.headerbytecount:0; + return result; +} + +static CURLcode http_on_response(struct Curl_easy *data, + const char *last_hd, size_t last_hd_len, + const char *buf, size_t blen, + size_t *pconsumed) +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + + (void)buf; /* not used without HTTP2 enabled */ + *pconsumed = 0; + + if(k->upgr101 == UPGR101_RECEIVED) { + /* supposedly upgraded to http2 now */ + if(conn->httpversion != 20) + infof(data, "Lying server, not serving HTTP/2"); + } + if(conn->httpversion < 20) { + conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; + } + + if(k->httpcode < 200 && last_hd) { + /* Intermediate responses might trigger processing of more + * responses, write the last header to the client before + * proceeding. */ + result = http_write_header(data, last_hd, last_hd_len); + last_hd = NULL; /* handled it */ + if(result) + goto out; + } + + if(k->httpcode < 100) { + failf(data, "Unsupported response code in HTTP response"); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; + } + else if(k->httpcode < 200) { + /* "A user agent MAY ignore unexpected 1xx status responses." + * By default, we expect to get more responses after this one. */ + k->header = TRUE; + k->headerline = 0; /* restart the header line counter */ + + switch(k->httpcode) { + case 100: + /* + * We have made an HTTP PUT or POST and this is 1.1-lingo + * that tells us that the server is OK with this and ready + * to receive the data. + */ + Curl_http_exp100_got100(data); + break; + case 101: + /* Switching Protocols only allowed from HTTP/1.1 */ + + if(conn->httpversion != 11) { + /* invalid for other HTTP versions */ + failf(data, "unexpected 101 response code"); + result = CURLE_WEIRD_SERVER_REPLY; + goto out; + } + if(k->upgr101 == UPGR101_H2) { + /* Switching to HTTP/2, where we will get more responses */ + infof(data, "Received 101, Switching to HTTP/2"); + k->upgr101 = UPGR101_RECEIVED; + /* We expect more response from HTTP/2 later */ + k->header = TRUE; + k->headerline = 0; /* restart the header line counter */ + /* Any remaining `buf` bytes are already HTTP/2 and passed to + * be processed. */ + result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); + if(result) + goto out; + *pconsumed += blen; + } +#ifdef USE_WEBSOCKETS + else if(k->upgr101 == UPGR101_WS) { + /* verify the response. Any passed `buf` bytes are already in + * WebSockets format and taken in by the protocol handler. */ + result = Curl_ws_accept(data, buf, blen); + if(result) + goto out; + *pconsumed += blen; /* ws accept handled the data */ + k->header = FALSE; /* we will not get more responses */ + if(data->set.connect_only) + k->keepon &= ~KEEP_RECV; /* read no more content */ + } +#endif + else { + /* We silently accept this as the final response. + * TODO: this looks, uhm, wrong. What are we switching to if we + * did not ask for an Upgrade? Maybe the application provided an + * `Upgrade: xxx` header? */ + k->header = FALSE; + } + break; + default: + /* The server may send us other 1xx responses, like informative + * 103. This have no influence on request processing and we expect + * to receive a final response eventually. */ + break; + } + goto out; + } + + /* k->httpcode >= 200, final response */ + k->header = FALSE; + + if(k->upgr101 == UPGR101_H2) { + /* A requested upgrade was denied, poke the multi handle to possibly + allow a pending pipewait to continue */ + Curl_multi_connchanged(data->multi); + } + + if((k->size == -1) && !k->chunk && !conn->bits.close && + (conn->httpversion == 11) && + !(conn->handler->protocol & CURLPROTO_RTSP) && + data->state.httpreq != HTTPREQ_HEAD) { + /* On HTTP 1.1, when connection is not to get closed, but no + Content-Length nor Transfer-Encoding chunked have been + received, according to RFC2616 section 4.4 point 5, we + assume that the server will close the connection to + signal the end of the document. */ + infof(data, "no chunk, no close, no size. Assume close to " + "signal end"); + streamclose(conn, "HTTP: No end-of-message indicator"); + } + + /* At this point we have some idea about the fate of the connection. + If we are closing the connection it may result auth failure. */ +#if defined(USE_NTLM) + if(conn->bits.close && + (((data->req.httpcode == 401) && + (conn->http_ntlm_state == NTLMSTATE_TYPE2)) || + ((data->req.httpcode == 407) && + (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) { + infof(data, "Connection closure while negotiating auth (HTTP 1.0?)"); + data->state.authproblem = TRUE; + } +#endif +#if defined(USE_SPNEGO) + if(conn->bits.close && + (((data->req.httpcode == 401) && + (conn->http_negotiate_state == GSS_AUTHRECV)) || + ((data->req.httpcode == 407) && + (conn->proxy_negotiate_state == GSS_AUTHRECV)))) { + infof(data, "Connection closure while negotiating auth (HTTP 1.0?)"); + data->state.authproblem = TRUE; + } + if((conn->http_negotiate_state == GSS_AUTHDONE) && + (data->req.httpcode != 401)) { + conn->http_negotiate_state = GSS_AUTHSUCC; + } + if((conn->proxy_negotiate_state == GSS_AUTHDONE) && + (data->req.httpcode != 407)) { + conn->proxy_negotiate_state = GSS_AUTHSUCC; + } +#endif + +#ifdef USE_WEBSOCKETS + /* All >=200 HTTP status codes are errors when wanting websockets */ + if(data->req.upgr101 == UPGR101_WS) { + failf(data, "Refused WebSockets upgrade: %d", k->httpcode); + result = CURLE_HTTP_RETURNED_ERROR; + goto out; + } +#endif + + /* Check if this response means the transfer errored. */ + if(http_should_fail(data, data->req.httpcode)) { + failf(data, "The requested URL returned error: %d", + k->httpcode); + result = CURLE_HTTP_RETURNED_ERROR; + goto out; + } + + /* Curl_http_auth_act() checks what authentication methods + * that are available and decides which one (if any) to + * use. It will set 'newurl' if an auth method was picked. */ + result = Curl_http_auth_act(data); + if(result) + goto out; + + if(k->httpcode >= 300) { + if((!data->req.authneg) && !conn->bits.close && + !Curl_creader_will_rewind(data)) { + /* + * General treatment of errors when about to send data. Including : + * "417 Expectation Failed", while waiting for 100-continue. + * + * The check for close above is done simply because of something + * else has already deemed the connection to get closed then + * something else should've considered the big picture and we + * avoid this check. + * + */ + + switch(data->state.httpreq) { + case HTTPREQ_PUT: + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + /* We got an error response. If this happened before the whole + * request body has been sent we stop sending and mark the + * connection for closure after we have read the entire response. + */ + if(!Curl_req_done_sending(data)) { + if((k->httpcode == 417) && Curl_http_exp100_is_selected(data)) { + /* 417 Expectation Failed - try again without the Expect + header */ + if(!k->writebytecount && http_exp100_is_waiting(data)) { + infof(data, "Got HTTP failure 417 while waiting for a 100"); + } + else { + infof(data, "Got HTTP failure 417 while sending data"); + streamclose(conn, + "Stop sending data before everything sent"); + result = http_perhapsrewind(data, conn); + if(result) + goto out; + } + data->state.disableexpect = TRUE; + DEBUGASSERT(!data->req.newurl); + data->req.newurl = strdup(data->state.url); + Curl_req_abort_sending(data); + } + else if(data->set.http_keep_sending_on_error) { + infof(data, "HTTP error before end of send, keep sending"); + http_exp100_send_anyway(data); + } + else { + infof(data, "HTTP error before end of send, stop sending"); + streamclose(conn, "Stop sending data before everything sent"); + result = Curl_req_abort_sending(data); + if(result) + goto out; + } + } + break; + + default: /* default label present to avoid compiler warnings */ + break; + } + } + + if(Curl_creader_will_rewind(data) && !Curl_req_done_sending(data)) { + /* We rewind before next send, continue sending now */ + infof(data, "Keep sending data to get tossed away"); + k->keepon |= KEEP_SEND; + } + + } + + /* This is the last response that we will got for the current request. + * Check on the body size and determine if the response is complete. + */ + result = Curl_http_size(data); + if(result) + goto out; + + /* If we requested a "no body", this is a good time to get + * out and return home. + */ + if(data->req.no_body) + k->download_done = TRUE; + + /* If max download size is *zero* (nothing) we already have + nothing and can safely return ok now! But for HTTP/2, we would + like to call http2_handle_stream_close to properly close a + stream. In order to do this, we keep reading until we + close the stream. */ + if(0 == k->maxdownload + && !Curl_conn_is_http2(data, conn, FIRSTSOCKET) + && !Curl_conn_is_http3(data, conn, FIRSTSOCKET)) + k->download_done = TRUE; + + /* final response without error, prepare to receive the body */ + result = Curl_http_firstwrite(data); + +out: + if(last_hd) { + /* if not written yet, write it now */ + CURLcode r2 = http_write_header(data, last_hd, last_hd_len); + if(!result) + result = r2; + } + return result; +} + +static CURLcode http_rw_hd(struct Curl_easy *data, + const char *hd, size_t hdlen, + const char *buf_remain, size_t blen, + size_t *pconsumed) +{ + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + int writetype; + + *pconsumed = 0; + if((0x0a == *hd) || (0x0d == *hd)) { + /* Empty header line means end of headers! */ + struct dynbuf last_header; + size_t consumed; + + Curl_dyn_init(&last_header, hdlen + 1); + result = Curl_dyn_addn(&last_header, hd, hdlen); + if(result) + return result; + + /* analyze the response to find out what to do. */ + /* Caveat: we clear anything in the header brigade, because a + * response might switch HTTP version which may call use recursively. + * Not nice, but that is currently the way of things. */ + Curl_dyn_reset(&data->state.headerb); + result = http_on_response(data, Curl_dyn_ptr(&last_header), + Curl_dyn_len(&last_header), + buf_remain, blen, &consumed); + *pconsumed += consumed; + Curl_dyn_free(&last_header); + return result; + } + + /* + * Checks for special headers coming up. + */ + + writetype = CLIENTWRITE_HEADER; + if(!k->headerline++) { + /* This is the first header, it MUST be the error code line + or else we consider this to be the body right away! */ + bool fine_statusline = FALSE; + + k->httpversion = 0; /* Do not know yet */ + if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) { + /* + * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 + * + * The response code is always a three-digit number in HTTP as the spec + * says. We allow any three-digit number here, but we cannot make + * guarantees on future behaviors since it is not within the protocol. + */ + const char *p = hd; + + while(*p && ISBLANK(*p)) + p++; + if(!strncmp(p, "HTTP/", 5)) { + p += 5; + switch(*p) { + case '1': + p++; + if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) { + if(ISBLANK(p[2])) { + k->httpversion = 10 + (p[1] - '0'); + p += 3; + if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { + k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + (p[2] - '0'); + p += 3; + if(ISSPACE(*p)) + fine_statusline = TRUE; + } + } + } + if(!fine_statusline) { + failf(data, "Unsupported HTTP/1 subversion in response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + break; + case '2': + case '3': + if(!ISBLANK(p[1])) + break; + k->httpversion = (*p - '0') * 10; + p += 2; + if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { + k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + (p[2] - '0'); + p += 3; + if(!ISSPACE(*p)) + break; + fine_statusline = TRUE; + } + break; + default: /* unsupported */ + failf(data, "Unsupported HTTP version in response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + } + + if(!fine_statusline) { + /* If user has set option HTTP200ALIASES, + compare header line against list of aliases + */ + statusline check = checkhttpprefix(data, hd, hdlen); + if(check == STATUS_DONE) { + fine_statusline = TRUE; + k->httpcode = 200; + k->httpversion = 10; + } + } + } + else if(data->conn->handler->protocol & CURLPROTO_RTSP) { + const char *p = hd; + while(*p && ISBLANK(*p)) + p++; + if(!strncmp(p, "RTSP/", 5)) { + p += 5; + if(ISDIGIT(*p)) { + p++; + if((p[0] == '.') && ISDIGIT(p[1])) { + if(ISBLANK(p[2])) { + p += 3; + if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { + k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + (p[2] - '0'); + p += 3; + if(ISSPACE(*p)) { + fine_statusline = TRUE; + k->httpversion = 11; /* RTSP acts like HTTP 1.1 */ + } + } + } + } + } + if(!fine_statusline) + return CURLE_WEIRD_SERVER_REPLY; + } + } + + if(fine_statusline) { + result = Curl_http_statusline(data, data->conn); + if(result) + return result; + writetype |= CLIENTWRITE_STATUS; + } + else { + k->header = FALSE; /* this is not a header line */ + return CURLE_WEIRD_SERVER_REPLY; + } + } + + result = verify_header(data, hd, hdlen); + if(result) + return result; + + result = Curl_http_header(data, hd, hdlen); + if(result) + return result; + + /* + * Taken in one (more) header. Write it to the client. + */ + Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen); + + if(k->httpcode/100 == 1) + writetype |= CLIENTWRITE_1XX; + result = Curl_client_write(data, writetype, hd, hdlen); + if(result) + return result; + + result = Curl_bump_headersize(data, hdlen, FALSE); + if(result) + return result; + + return CURLE_OK; +} + +/* + * Read any HTTP header lines from the server and pass them to the client app. + */ +static CURLcode http_parse_headers(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed) +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + char *end_ptr; + bool leftover_body = FALSE; + + /* header line within buffer loop */ + *pconsumed = 0; + while(blen && k->header) { + size_t consumed; + + end_ptr = memchr(buf, '\n', blen); + if(!end_ptr) { + /* Not a complete header line within buffer, append the data to + the end of the headerbuff. */ + result = Curl_dyn_addn(&data->state.headerb, buf, blen); + if(result) + return result; + *pconsumed += blen; + + if(!k->headerline) { + /* check if this looks like a protocol header */ + statusline st = + checkprotoprefix(data, conn, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); + + if(st == STATUS_BAD) { + /* this is not the beginning of a protocol first header line */ + k->header = FALSE; + streamclose(conn, "bad HTTP: No end-of-message indicator"); + if(conn->httpversion >= 10) { + failf(data, "Invalid status line"); + return CURLE_WEIRD_SERVER_REPLY; + } + if(!data->set.http09_allowed) { + failf(data, "Received HTTP/0.9 when not allowed"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + leftover_body = TRUE; + goto out; + } + } + goto out; /* read more and try again */ + } + + /* decrease the size of the remaining (supposed) header line */ + consumed = (end_ptr - buf) + 1; + result = Curl_dyn_addn(&data->state.headerb, buf, consumed); + if(result) + return result; + blen -= consumed; + buf += consumed; + *pconsumed += consumed; + + /**** + * We now have a FULL header line in 'headerb'. + *****/ + + if(!k->headerline) { + /* the first read header */ + statusline st = checkprotoprefix(data, conn, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); + if(st == STATUS_BAD) { + streamclose(conn, "bad HTTP: No end-of-message indicator"); + /* this is not the beginning of a protocol first header line */ + if(conn->httpversion >= 10) { + failf(data, "Invalid status line"); + return CURLE_WEIRD_SERVER_REPLY; + } + if(!data->set.http09_allowed) { + failf(data, "Received HTTP/0.9 when not allowed"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + k->header = FALSE; + leftover_body = TRUE; + goto out; + } + } + + result = http_rw_hd(data, Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb), + buf, blen, &consumed); + /* We are done with this line. We reset because response + * processing might switch to HTTP/2 and that might call us + * directly again. */ + Curl_dyn_reset(&data->state.headerb); + if(consumed) { + blen -= consumed; + buf += consumed; + *pconsumed += consumed; + } + if(result) + return result; + } + + /* We might have reached the end of the header part here, but + there might be a non-header part left in the end of the read + buffer. */ +out: + if(!k->header && !leftover_body) { + Curl_dyn_free(&data->state.headerb); + } + return CURLE_OK; +} + +CURLcode Curl_http_write_resp_hd(struct Curl_easy *data, + const char *hd, size_t hdlen, + bool is_eos) +{ + CURLcode result; + size_t consumed; + char tmp = 0; + + result = http_rw_hd(data, hd, hdlen, &tmp, 0, &consumed); + if(!result && is_eos) { + result = Curl_client_write(data, (CLIENTWRITE_BODY|CLIENTWRITE_EOS), + &tmp, 0); + } + return result; +} + +/* + * HTTP protocol `write_resp` implementation. Will parse headers + * when not done yet and otherwise return without consuming data. + */ +CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed) +{ + if(!data->req.header) { + *pconsumed = 0; + return CURLE_OK; + } + else { + CURLcode result; + + result = http_parse_headers(data, buf, blen, pconsumed); + if(!result && !data->req.header) { + if(!data->req.no_body && Curl_dyn_len(&data->state.headerb)) { + /* leftover from parsing something that turned out not + * to be a header, only happens if we allow for + * HTTP/0.9 like responses */ + result = Curl_client_write(data, CLIENTWRITE_BODY, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); + } + Curl_dyn_free(&data->state.headerb); + } + return result; + } +} + +CURLcode Curl_http_write_resp(struct Curl_easy *data, + const char *buf, size_t blen, + bool is_eos) +{ + CURLcode result; + size_t consumed; + int flags; + + result = Curl_http_write_resp_hds(data, buf, blen, &consumed); + if(result || data->req.done) + goto out; + + DEBUGASSERT(consumed <= blen); + blen -= consumed; + buf += consumed; + /* either all was consumed in header parsing, or we have data left + * and are done with headers, e.g. it is BODY data */ + DEBUGASSERT(!blen || !data->req.header); + if(!data->req.header && (blen || is_eos)) { + /* BODY data after header been parsed, write and consume */ + flags = CLIENTWRITE_BODY; + if(is_eos) + flags |= CLIENTWRITE_EOS; + result = Curl_client_write(data, flags, (char *)buf, blen); + } +out: + return result; +} + +/* Decode HTTP status code string. */ +CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len) +{ + CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; + int status = 0; + int i; + + if(len != 3) + goto out; + + for(i = 0; i < 3; ++i) { + char c = s[i]; + + if(c < '0' || c > '9') + goto out; + + status *= 10; + status += c - '0'; + } + result = CURLE_OK; +out: + *pstatus = result? -1 : status; + return result; +} + +CURLcode Curl_http_req_make(struct httpreq **preq, + const char *method, size_t m_len, + const char *scheme, size_t s_len, + const char *authority, size_t a_len, + const char *path, size_t p_len) +{ + struct httpreq *req; + CURLcode result = CURLE_OUT_OF_MEMORY; + + DEBUGASSERT(method); + if(m_len + 1 > sizeof(req->method)) + return CURLE_BAD_FUNCTION_ARGUMENT; + + req = calloc(1, sizeof(*req)); + if(!req) + goto out; + memcpy(req->method, method, m_len); + if(scheme) { + req->scheme = Curl_memdup0(scheme, s_len); + if(!req->scheme) + goto out; + } + if(authority) { + req->authority = Curl_memdup0(authority, a_len); + if(!req->authority) + goto out; + } + if(path) { + req->path = Curl_memdup0(path, p_len); + if(!req->path) + goto out; + } + Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST); + Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST); + result = CURLE_OK; + +out: + if(result && req) + Curl_http_req_free(req); + *preq = result? NULL : req; + return result; +} + +static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url) +{ + char *user, *pass, *host, *port; + struct dynbuf buf; + CURLUcode uc; + CURLcode result = CURLE_URL_MALFORMAT; + + user = pass = host = port = NULL; + Curl_dyn_init(&buf, DYN_HTTP_REQUEST); + + uc = curl_url_get(url, CURLUPART_HOST, &host, 0); + if(uc && uc != CURLUE_NO_HOST) + goto out; + if(!host) { + req->authority = NULL; + result = CURLE_OK; + goto out; + } + + uc = curl_url_get(url, CURLUPART_PORT, &port, CURLU_NO_DEFAULT_PORT); + if(uc && uc != CURLUE_NO_PORT) + goto out; + uc = curl_url_get(url, CURLUPART_USER, &user, 0); + if(uc && uc != CURLUE_NO_USER) + goto out; + if(user) { + uc = curl_url_get(url, CURLUPART_PASSWORD, &pass, 0); + if(uc && uc != CURLUE_NO_PASSWORD) + goto out; + } + + if(user) { + result = Curl_dyn_add(&buf, user); + if(result) + goto out; + if(pass) { + result = Curl_dyn_addf(&buf, ":%s", pass); + if(result) + goto out; + } + result = Curl_dyn_add(&buf, "@"); + if(result) + goto out; + } + result = Curl_dyn_add(&buf, host); + if(result) + goto out; + if(port) { + result = Curl_dyn_addf(&buf, ":%s", port); + if(result) + goto out; + } + req->authority = strdup(Curl_dyn_ptr(&buf)); + if(!req->authority) + goto out; + result = CURLE_OK; + +out: + free(user); + free(pass); + free(host); + free(port); + Curl_dyn_free(&buf); + return result; +} + +static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url) +{ + char *path, *query; + struct dynbuf buf; + CURLUcode uc; + CURLcode result = CURLE_URL_MALFORMAT; + + path = query = NULL; + Curl_dyn_init(&buf, DYN_HTTP_REQUEST); + + uc = curl_url_get(url, CURLUPART_PATH, &path, CURLU_PATH_AS_IS); + if(uc) + goto out; + uc = curl_url_get(url, CURLUPART_QUERY, &query, 0); + if(uc && uc != CURLUE_NO_QUERY) + goto out; + + if(!path && !query) { + req->path = NULL; + } + else if(path && !query) { + req->path = path; + path = NULL; + } + else { + if(path) { + result = Curl_dyn_add(&buf, path); + if(result) + goto out; + } + if(query) { + result = Curl_dyn_addf(&buf, "?%s", query); + if(result) + goto out; + } + req->path = strdup(Curl_dyn_ptr(&buf)); + if(!req->path) + goto out; + } + result = CURLE_OK; + +out: + free(path); + free(query); + Curl_dyn_free(&buf); + return result; +} + +CURLcode Curl_http_req_make2(struct httpreq **preq, + const char *method, size_t m_len, + CURLU *url, const char *scheme_default) +{ + struct httpreq *req; + CURLcode result = CURLE_OUT_OF_MEMORY; + CURLUcode uc; + + DEBUGASSERT(method); + if(m_len + 1 > sizeof(req->method)) + return CURLE_BAD_FUNCTION_ARGUMENT; + + req = calloc(1, sizeof(*req)); + if(!req) + goto out; + memcpy(req->method, method, m_len); + + uc = curl_url_get(url, CURLUPART_SCHEME, &req->scheme, 0); + if(uc && uc != CURLUE_NO_SCHEME) + goto out; + if(!req->scheme && scheme_default) { + req->scheme = strdup(scheme_default); + if(!req->scheme) + goto out; + } + + result = req_assign_url_authority(req, url); + if(result) + goto out; + result = req_assign_url_path(req, url); + if(result) + goto out; + + Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST); + Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST); + result = CURLE_OK; + +out: + if(result && req) + Curl_http_req_free(req); + *preq = result? NULL : req; + return result; +} + +void Curl_http_req_free(struct httpreq *req) +{ + if(req) { + free(req->scheme); + free(req->authority); + free(req->path); + Curl_dynhds_free(&req->headers); + Curl_dynhds_free(&req->trailers); + free(req); + } +} + +struct name_const { + const char *name; + size_t namelen; +}; + +static struct name_const H2_NON_FIELD[] = { + { STRCONST("Host") }, + { STRCONST("Upgrade") }, + { STRCONST("Connection") }, + { STRCONST("Keep-Alive") }, + { STRCONST("Proxy-Connection") }, + { STRCONST("Transfer-Encoding") }, +}; + +static bool h2_non_field(const char *name, size_t namelen) +{ + size_t i; + for(i = 0; i < sizeof(H2_NON_FIELD)/sizeof(H2_NON_FIELD[0]); ++i) { + if(namelen < H2_NON_FIELD[i].namelen) + return FALSE; + if(namelen == H2_NON_FIELD[i].namelen && + strcasecompare(H2_NON_FIELD[i].name, name)) + return TRUE; + } + return FALSE; +} + +CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, + struct httpreq *req, struct Curl_easy *data) +{ + const char *scheme = NULL, *authority = NULL; + struct dynhds_entry *e; + size_t i; + CURLcode result; + + DEBUGASSERT(req); + DEBUGASSERT(h2_headers); + + if(req->scheme) { + scheme = req->scheme; + } + else if(strcmp("CONNECT", req->method)) { + scheme = Curl_checkheaders(data, STRCONST(HTTP_PSEUDO_SCHEME)); + if(scheme) { + scheme += sizeof(HTTP_PSEUDO_SCHEME); + while(*scheme && ISBLANK(*scheme)) + scheme++; + infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme); + } + else { + scheme = (data->conn && data->conn->handler->flags & PROTOPT_SSL)? + "https" : "http"; + } + } + + if(req->authority) { + authority = req->authority; + } + else { + e = Curl_dynhds_get(&req->headers, STRCONST("Host")); + if(e) + authority = e->value; + } + + Curl_dynhds_reset(h2_headers); + Curl_dynhds_set_opts(h2_headers, DYNHDS_OPT_LOWERCASE); + result = Curl_dynhds_add(h2_headers, STRCONST(HTTP_PSEUDO_METHOD), + req->method, strlen(req->method)); + if(!result && scheme) { + result = Curl_dynhds_add(h2_headers, STRCONST(HTTP_PSEUDO_SCHEME), + scheme, strlen(scheme)); + } + if(!result && authority) { + result = Curl_dynhds_add(h2_headers, STRCONST(HTTP_PSEUDO_AUTHORITY), + authority, strlen(authority)); + } + if(!result && req->path) { + result = Curl_dynhds_add(h2_headers, STRCONST(HTTP_PSEUDO_PATH), + req->path, strlen(req->path)); + } + for(i = 0; !result && i < Curl_dynhds_count(&req->headers); ++i) { + e = Curl_dynhds_getn(&req->headers, i); + if(!h2_non_field(e->name, e->namelen)) { + result = Curl_dynhds_add(h2_headers, e->name, e->namelen, + e->value, e->valuelen); + } + } + + return result; +} + +CURLcode Curl_http_resp_make(struct http_resp **presp, + int status, + const char *description) +{ + struct http_resp *resp; + CURLcode result = CURLE_OUT_OF_MEMORY; + + resp = calloc(1, sizeof(*resp)); + if(!resp) + goto out; + + resp->status = status; + if(description) { + resp->description = strdup(description); + if(!resp->description) + goto out; + } + Curl_dynhds_init(&resp->headers, 0, DYN_HTTP_REQUEST); + Curl_dynhds_init(&resp->trailers, 0, DYN_HTTP_REQUEST); + result = CURLE_OK; + +out: + if(result && resp) + Curl_http_resp_free(resp); + *presp = result? NULL : resp; + return result; +} + +void Curl_http_resp_free(struct http_resp *resp) +{ + if(resp) { + free(resp->description); + Curl_dynhds_free(&resp->headers); + Curl_dynhds_free(&resp->trailers); + if(resp->prev) + Curl_http_resp_free(resp->prev); + free(resp); + } +} + +struct cr_exp100_ctx { + struct Curl_creader super; + struct curltime start; /* time started waiting */ + enum expect100 state; +}; + +/* Expect: 100-continue client reader, blocking uploads */ + +static void http_exp100_continue(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_exp100_ctx *ctx = reader->ctx; + if(ctx->state > EXP100_SEND_DATA) { + ctx->state = EXP100_SEND_DATA; + data->req.keepon |= KEEP_SEND; + data->req.keepon &= ~KEEP_SEND_TIMED; + Curl_expire_done(data, EXPIRE_100_TIMEOUT); + } +} + +static CURLcode cr_exp100_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *nread, bool *eos) +{ + struct cr_exp100_ctx *ctx = reader->ctx; + timediff_t ms; + + switch(ctx->state) { + case EXP100_SENDING_REQUEST: + /* We are now waiting for a reply from the server or + * a timeout on our side */ + DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE")); + ctx->state = EXP100_AWAITING_CONTINUE; + ctx->start = Curl_now(); + Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); + data->req.keepon &= ~KEEP_SEND; + data->req.keepon |= KEEP_SEND_TIMED; + *nread = 0; + *eos = FALSE; + return CURLE_OK; + case EXP100_FAILED: + DEBUGF(infof(data, "cr_exp100_read, expectation failed, error")); + *nread = 0; + *eos = FALSE; + return CURLE_READ_ERROR; + case EXP100_AWAITING_CONTINUE: + ms = Curl_timediff(Curl_now(), ctx->start); + if(ms < data->set.expect_100_timeout) { + DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired")); + data->req.keepon &= ~KEEP_SEND; + data->req.keepon |= KEEP_SEND_TIMED; + *nread = 0; + *eos = FALSE; + return CURLE_OK; + } + /* we have waited long enough, continue anyway */ + http_exp100_continue(data, reader); + infof(data, "Done waiting for 100-continue"); + FALLTHROUGH(); + default: + DEBUGF(infof(data, "cr_exp100_read, pass through")); + return Curl_creader_read(data, reader->next, buf, blen, nread, eos); + } +} + +static void cr_exp100_done(struct Curl_easy *data, + struct Curl_creader *reader, int premature) +{ + struct cr_exp100_ctx *ctx = reader->ctx; + ctx->state = premature? EXP100_FAILED : EXP100_SEND_DATA; + data->req.keepon &= ~KEEP_SEND_TIMED; + Curl_expire_done(data, EXPIRE_100_TIMEOUT); +} + +static const struct Curl_crtype cr_exp100 = { + "cr-exp100", + Curl_creader_def_init, + cr_exp100_read, + Curl_creader_def_close, + Curl_creader_def_needs_rewind, + Curl_creader_def_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_rewind, + Curl_creader_def_unpause, + Curl_creader_def_is_paused, + cr_exp100_done, + sizeof(struct cr_exp100_ctx) +}; + +static CURLcode http_exp100_add_reader(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &cr_exp100, + CURL_CR_PROTOCOL); + if(!result) + result = Curl_creader_add(data, reader); + if(!result) { + struct cr_exp100_ctx *ctx = reader->ctx; + ctx->state = EXP100_SENDING_REQUEST; + } + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + +void Curl_http_exp100_got100(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + if(r) + http_exp100_continue(data, r); +} + +static bool http_exp100_is_waiting(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + if(r) { + struct cr_exp100_ctx *ctx = r->ctx; + return (ctx->state == EXP100_AWAITING_CONTINUE); + } + return FALSE; +} + +static void http_exp100_send_anyway(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + if(r) + http_exp100_continue(data, r); +} + +bool Curl_http_exp100_is_selected(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + return r? TRUE : FALSE; +} + +#endif /* CURL_DISABLE_HTTP */ diff --git a/third_party/curl/lib/http.h b/third_party/curl/lib/http.h new file mode 100644 index 0000000000..bb5974d94d --- /dev/null +++ b/third_party/curl/lib/http.h @@ -0,0 +1,296 @@ +#ifndef HEADER_CURL_HTTP_H +#define HEADER_CURL_HTTP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if defined(USE_MSH3) && !defined(_WIN32) +#include +#endif + +#include "bufq.h" +#include "dynhds.h" +#include "ws.h" + +typedef enum { + HTTPREQ_GET, + HTTPREQ_POST, + HTTPREQ_POST_FORM, /* we make a difference internally */ + HTTPREQ_POST_MIME, /* we make a difference internally */ + HTTPREQ_PUT, + HTTPREQ_HEAD +} Curl_HttpReq; + +#ifndef CURL_DISABLE_HTTP + +#if defined(USE_HTTP3) +#include +#endif + +extern const struct Curl_handler Curl_handler_http; + +#ifdef USE_SSL +extern const struct Curl_handler Curl_handler_https; +#endif + +struct dynhds; + +CURLcode Curl_bump_headersize(struct Curl_easy *data, + size_t delta, + bool connect_only); + +/* Header specific functions */ +bool Curl_compareheader(const char *headerline, /* line to check */ + const char *header, /* header keyword _with_ colon */ + const size_t hlen, /* len of the keyword in bytes */ + const char *content, /* content string to find */ + const size_t clen); /* len of the content in bytes */ + +char *Curl_copy_header_value(const char *header); + +char *Curl_checkProxyheaders(struct Curl_easy *data, + const struct connectdata *conn, + const char *thisheader, + const size_t thislen); + +CURLcode Curl_add_timecondition(struct Curl_easy *data, +#ifndef USE_HYPER + struct dynbuf *req +#else + void *headers +#endif + ); +CURLcode Curl_add_custom_headers(struct Curl_easy *data, + bool is_connect, +#ifndef USE_HYPER + struct dynbuf *req +#else + void *headers +#endif + ); +CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, + bool is_connect, + struct dynhds *hds); + +void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, + const char **method, Curl_HttpReq *); +CURLcode Curl_http_useragent(struct Curl_easy *data); +CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn); +CURLcode Curl_http_target(struct Curl_easy *data, struct connectdata *conn, + struct dynbuf *req); +CURLcode Curl_http_statusline(struct Curl_easy *data, + struct connectdata *conn); +CURLcode Curl_http_header(struct Curl_easy *data, + const char *hd, size_t hdlen); +CURLcode Curl_transferencode(struct Curl_easy *data); +CURLcode Curl_http_req_set_reader(struct Curl_easy *data, + Curl_HttpReq httpreq, + const char **tep); +CURLcode Curl_http_req_complete(struct Curl_easy *data, + struct dynbuf *r, Curl_HttpReq httpreq); +bool Curl_use_http_1_1plus(const struct Curl_easy *data, + const struct connectdata *conn); +#ifndef CURL_DISABLE_COOKIES +CURLcode Curl_http_cookies(struct Curl_easy *data, + struct connectdata *conn, + struct dynbuf *r); +#else +#define Curl_http_cookies(a,b,c) CURLE_OK +#endif +CURLcode Curl_http_range(struct Curl_easy *data, + Curl_HttpReq httpreq); +CURLcode Curl_http_firstwrite(struct Curl_easy *data); + +/* protocol-specific functions set up to be called by the main engine */ +CURLcode Curl_http_setup_conn(struct Curl_easy *data, + struct connectdata *conn); +CURLcode Curl_http(struct Curl_easy *data, bool *done); +CURLcode Curl_http_done(struct Curl_easy *data, CURLcode, bool premature); +CURLcode Curl_http_connect(struct Curl_easy *data, bool *done); +int Curl_http_getsock_do(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t *socks); +CURLcode Curl_http_write_resp(struct Curl_easy *data, + const char *buf, size_t blen, + bool is_eos); +CURLcode Curl_http_write_resp_hd(struct Curl_easy *data, + const char *hd, size_t hdlen, + bool is_eos); + +/* These functions are in http.c */ +CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, + const char *auth); +CURLcode Curl_http_auth_act(struct Curl_easy *data); + +/* If only the PICKNONE bit is set, there has been a round-trip and we + selected to use no auth at all. Ie, we actively select no auth, as opposed + to not having one selected. The other CURLAUTH_* defines are present in the + public curl/curl.h header. */ +#define CURLAUTH_PICKNONE (1<<30) /* do not use auth */ + +/* MAX_INITIAL_POST_SIZE indicates the number of bytes that will make the POST + data get included in the initial data chunk sent to the server. If the + data is larger than this, it will automatically get split up in multiple + system calls. + + This value used to be fairly big (100K), but we must take into account that + if the server rejects the POST due for authentication reasons, this data + will always be unconditionally sent and thus it may not be larger than can + always be afforded to send twice. + + It must not be greater than 64K to work on VMS. +*/ +#ifndef MAX_INITIAL_POST_SIZE +#define MAX_INITIAL_POST_SIZE (64*1024) +#endif + +/* EXPECT_100_THRESHOLD is the request body size limit for when libcurl will + * automatically add an "Expect: 100-continue" header in HTTP requests. When + * the size is unknown, it will always add it. + * + */ +#ifndef EXPECT_100_THRESHOLD +#define EXPECT_100_THRESHOLD (1024*1024) +#endif + +/* MAX_HTTP_RESP_HEADER_SIZE is the maximum size of all response headers + combined that libcurl allows for a single HTTP response, any HTTP + version. This count includes CONNECT response headers. */ +#define MAX_HTTP_RESP_HEADER_SIZE (300*1024) + +bool Curl_http_exp100_is_selected(struct Curl_easy *data); +void Curl_http_exp100_got100(struct Curl_easy *data); + +#endif /* CURL_DISABLE_HTTP */ + +/**************************************************************************** + * HTTP unique setup + ***************************************************************************/ + +CURLcode Curl_http_size(struct Curl_easy *data); + +CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed); + +/** + * Curl_http_output_auth() setups the authentication headers for the + * host/proxy and the correct authentication + * method. data->state.authdone is set to TRUE when authentication is + * done. + * + * @param data all information about the current transfer + * @param conn all information about the current connection + * @param request pointer to the request keyword + * @param httpreq is the request type + * @param path pointer to the requested path + * @param proxytunnel boolean if this is the request setting up a "proxy + * tunnel" + * + * @returns CURLcode + */ +CURLcode +Curl_http_output_auth(struct Curl_easy *data, + struct connectdata *conn, + const char *request, + Curl_HttpReq httpreq, + const char *path, + bool proxytunnel); /* TRUE if this is the request setting + up the proxy tunnel */ + +/* Decode HTTP status code string. */ +CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len); + + +/** + * All about a core HTTP request, excluding body and trailers + */ +struct httpreq { + char method[24]; + char *scheme; + char *authority; + char *path; + struct dynhds headers; + struct dynhds trailers; +}; + +/** + * Create an HTTP request struct. + */ +CURLcode Curl_http_req_make(struct httpreq **preq, + const char *method, size_t m_len, + const char *scheme, size_t s_len, + const char *authority, size_t a_len, + const char *path, size_t p_len); + +CURLcode Curl_http_req_make2(struct httpreq **preq, + const char *method, size_t m_len, + CURLU *url, const char *scheme_default); + +void Curl_http_req_free(struct httpreq *req); + +#define HTTP_PSEUDO_METHOD ":method" +#define HTTP_PSEUDO_SCHEME ":scheme" +#define HTTP_PSEUDO_AUTHORITY ":authority" +#define HTTP_PSEUDO_PATH ":path" +#define HTTP_PSEUDO_STATUS ":status" + +/** + * Create the list of HTTP/2 headers which represent the request, + * using HTTP/2 pseudo headers preceding the `req->headers`. + * + * Applies the following transformations: + * - if `authority` is set, any "Host" header is removed. + * - if `authority` is unset and a "Host" header is present, use + * that as `authority` and remove "Host" + * - removes and Connection header fields as defined in rfc9113 ch. 8.2.2 + * - lower-cases the header field names + * + * @param h2_headers will contain the HTTP/2 headers on success + * @param req the request to transform + * @param data the handle to lookup defaults like ' :scheme' from + */ +CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, + struct httpreq *req, struct Curl_easy *data); + +/** + * All about a core HTTP response, excluding body and trailers + */ +struct http_resp { + int status; + char *description; + struct dynhds headers; + struct dynhds trailers; + struct http_resp *prev; +}; + +/** + * Create an HTTP response struct. + */ +CURLcode Curl_http_resp_make(struct http_resp **presp, + int status, + const char *description); + +void Curl_http_resp_free(struct http_resp *resp); + +#endif /* HEADER_CURL_HTTP_H */ diff --git a/third_party/curl/lib/http1.c b/third_party/curl/lib/http1.c new file mode 100644 index 0000000000..d7e21fdcec --- /dev/null +++ b/third_party/curl/lib/http1.c @@ -0,0 +1,346 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_HTTP + +#include "urldata.h" +#include +#include "http.h" +#include "http1.h" +#include "urlapi-int.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +#define H1_MAX_URL_LEN (8*1024) + +void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len) +{ + memset(parser, 0, sizeof(*parser)); + parser->max_line_len = max_line_len; + Curl_dyn_init(&parser->scratch, max_line_len); +} + +void Curl_h1_req_parse_free(struct h1_req_parser *parser) +{ + if(parser) { + Curl_http_req_free(parser->req); + Curl_dyn_free(&parser->scratch); + parser->req = NULL; + parser->done = FALSE; + } +} + +static CURLcode trim_line(struct h1_req_parser *parser, int options) +{ + DEBUGASSERT(parser->line); + if(parser->line_len) { + if(parser->line[parser->line_len - 1] == '\n') + --parser->line_len; + if(parser->line_len) { + if(parser->line[parser->line_len - 1] == '\r') + --parser->line_len; + else if(options & H1_PARSE_OPT_STRICT) + return CURLE_URL_MALFORMAT; + } + else if(options & H1_PARSE_OPT_STRICT) + return CURLE_URL_MALFORMAT; + } + else if(options & H1_PARSE_OPT_STRICT) + return CURLE_URL_MALFORMAT; + + if(parser->line_len > parser->max_line_len) { + return CURLE_URL_MALFORMAT; + } + return CURLE_OK; +} + +static ssize_t detect_line(struct h1_req_parser *parser, + const char *buf, const size_t buflen, + CURLcode *err) +{ + const char *line_end; + + DEBUGASSERT(!parser->line); + line_end = memchr(buf, '\n', buflen); + if(!line_end) { + *err = CURLE_AGAIN; + return -1; + } + parser->line = buf; + parser->line_len = line_end - buf + 1; + *err = CURLE_OK; + return (ssize_t)parser->line_len; +} + +static ssize_t next_line(struct h1_req_parser *parser, + const char *buf, const size_t buflen, int options, + CURLcode *err) +{ + ssize_t nread = 0; + + if(parser->line) { + parser->line = NULL; + parser->line_len = 0; + Curl_dyn_reset(&parser->scratch); + } + + nread = detect_line(parser, buf, buflen, err); + if(nread >= 0) { + if(Curl_dyn_len(&parser->scratch)) { + /* append detected line to scratch to have the complete line */ + *err = Curl_dyn_addn(&parser->scratch, parser->line, parser->line_len); + if(*err) + return -1; + parser->line = Curl_dyn_ptr(&parser->scratch); + parser->line_len = Curl_dyn_len(&parser->scratch); + } + *err = trim_line(parser, options); + if(*err) + return -1; + } + else if(*err == CURLE_AGAIN) { + /* no line end in `buf`, add it to our scratch */ + *err = Curl_dyn_addn(&parser->scratch, (const unsigned char *)buf, buflen); + nread = (*err)? -1 : (ssize_t)buflen; + } + return nread; +} + +static CURLcode start_req(struct h1_req_parser *parser, + const char *scheme_default, int options) +{ + const char *p, *m, *target, *hv, *scheme, *authority, *path; + size_t m_len, target_len, hv_len, scheme_len, authority_len, path_len; + size_t i; + CURLU *url = NULL; + CURLcode result = CURLE_URL_MALFORMAT; /* Use this as default fail */ + + DEBUGASSERT(!parser->req); + /* line must match: "METHOD TARGET HTTP_VERSION" */ + p = memchr(parser->line, ' ', parser->line_len); + if(!p || p == parser->line) + goto out; + + m = parser->line; + m_len = p - parser->line; + target = p + 1; + target_len = hv_len = 0; + hv = NULL; + + /* URL may contain spaces so scan backwards */ + for(i = parser->line_len; i > m_len; --i) { + if(parser->line[i] == ' ') { + hv = &parser->line[i + 1]; + hv_len = parser->line_len - i; + target_len = (hv - target) - 1; + break; + } + } + /* no SPACE found or empty TARGET or empty HTTP_VERSION */ + if(!target_len || !hv_len) + goto out; + + /* TODO: we do not check HTTP_VERSION for conformity, should + + do that when STRICT option is supplied. */ + (void)hv; + + /* The TARGET can be (rfc 9112, ch. 3.2): + * origin-form: path + optional query + * absolute-form: absolute URI + * authority-form: host+port for CONNECT + * asterisk-form: '*' for OPTIONS + * + * from TARGET, we derive `scheme` `authority` `path` + * origin-form -- -- TARGET + * absolute-form URL* URL* URL* + * authority-form -- TARGET -- + * asterisk-form -- -- TARGET + */ + scheme = authority = path = NULL; + scheme_len = authority_len = path_len = 0; + + if(target_len == 1 && target[0] == '*') { + /* asterisk-form */ + path = target; + path_len = target_len; + } + else if(!strncmp("CONNECT", m, m_len)) { + /* authority-form */ + authority = target; + authority_len = target_len; + } + else if(target[0] == '/') { + /* origin-form */ + path = target; + path_len = target_len; + } + else { + /* origin-form OR absolute-form */ + CURLUcode uc; + char tmp[H1_MAX_URL_LEN]; + + /* default, unless we see an absolute URL */ + path = target; + path_len = target_len; + + /* URL parser wants 0-termination */ + if(target_len >= sizeof(tmp)) + goto out; + memcpy(tmp, target, target_len); + tmp[target_len] = '\0'; + /* See if treating TARGET as an absolute URL makes sense */ + if(Curl_is_absolute_url(tmp, NULL, 0, FALSE)) { + unsigned int url_options; + + url = curl_url(); + if(!url) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + url_options = (CURLU_NON_SUPPORT_SCHEME| + CURLU_PATH_AS_IS| + CURLU_NO_DEFAULT_PORT); + if(!(options & H1_PARSE_OPT_STRICT)) + url_options |= CURLU_ALLOW_SPACE; + uc = curl_url_set(url, CURLUPART_URL, tmp, url_options); + if(uc) { + goto out; + } + } + + if(!url && (options & H1_PARSE_OPT_STRICT)) { + /* we should have an absolute URL or have seen `/` earlier */ + goto out; + } + } + + if(url) { + result = Curl_http_req_make2(&parser->req, m, m_len, url, scheme_default); + } + else { + if(!scheme && scheme_default) { + scheme = scheme_default; + scheme_len = strlen(scheme_default); + } + result = Curl_http_req_make(&parser->req, m, m_len, scheme, scheme_len, + authority, authority_len, path, path_len); + } + +out: + curl_url_cleanup(url); + return result; +} + +ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser, + const char *buf, size_t buflen, + const char *scheme_default, int options, + CURLcode *err) +{ + ssize_t nread = 0, n; + + *err = CURLE_OK; + while(!parser->done) { + n = next_line(parser, buf, buflen, options, err); + if(n < 0) { + if(*err != CURLE_AGAIN) { + nread = -1; + } + *err = CURLE_OK; + goto out; + } + + /* Consume this line */ + nread += (size_t)n; + buf += (size_t)n; + buflen -= (size_t)n; + + if(!parser->line) { + /* consumed bytes, but line not complete */ + if(!buflen) + goto out; + } + else if(!parser->req) { + *err = start_req(parser, scheme_default, options); + if(*err) { + nread = -1; + goto out; + } + } + else if(parser->line_len == 0) { + /* last, empty line, we are finished */ + if(!parser->req) { + *err = CURLE_URL_MALFORMAT; + nread = -1; + goto out; + } + parser->done = TRUE; + Curl_dyn_reset(&parser->scratch); + /* last chance adjustments */ + } + else { + *err = Curl_dynhds_h1_add_line(&parser->req->headers, + parser->line, parser->line_len); + if(*err) { + nread = -1; + goto out; + } + } + } + +out: + return nread; +} + +CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor, + struct dynbuf *dbuf) +{ + CURLcode result; + + result = Curl_dyn_addf(dbuf, "%s %s%s%s%s HTTP/1.%d\r\n", + req->method, + req->scheme? req->scheme : "", + req->scheme? "://" : "", + req->authority? req->authority : "", + req->path? req->path : "", + http_minor); + if(result) + goto out; + + result = Curl_dynhds_h1_dprint(&req->headers, dbuf); + if(result) + goto out; + + result = Curl_dyn_addn(dbuf, STRCONST("\r\n")); + +out: + return result; +} + +#endif /* !CURL_DISABLE_HTTP */ diff --git a/third_party/curl/lib/http1.h b/third_party/curl/lib/http1.h new file mode 100644 index 0000000000..2de302f1f6 --- /dev/null +++ b/third_party/curl/lib/http1.h @@ -0,0 +1,63 @@ +#ifndef HEADER_CURL_HTTP1_H +#define HEADER_CURL_HTTP1_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_HTTP +#include "bufq.h" +#include "http.h" + +#define H1_PARSE_OPT_NONE (0) +#define H1_PARSE_OPT_STRICT (1 << 0) + +#define H1_PARSE_DEFAULT_MAX_LINE_LEN DYN_HTTP_REQUEST + +struct h1_req_parser { + struct httpreq *req; + struct dynbuf scratch; + size_t scratch_skip; + const char *line; + size_t max_line_len; + size_t line_len; + bool done; +}; + +void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len); +void Curl_h1_req_parse_free(struct h1_req_parser *parser); + +ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser, + const char *buf, size_t buflen, + const char *scheme_default, int options, + CURLcode *err); + +CURLcode Curl_h1_req_dprint(const struct httpreq *req, + struct dynbuf *dbuf); + +CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor, + struct dynbuf *dbuf); + +#endif /* !CURL_DISABLE_HTTP */ +#endif /* HEADER_CURL_HTTP1_H */ diff --git a/third_party/curl/lib/http2.c b/third_party/curl/lib/http2.c new file mode 100644 index 0000000000..6a979c1364 --- /dev/null +++ b/third_party/curl/lib/http2.c @@ -0,0 +1,2919 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_NGHTTP2 +#include +#include +#include "urldata.h" +#include "bufq.h" +#include "hash.h" +#include "http1.h" +#include "http2.h" +#include "http.h" +#include "sendf.h" +#include "select.h" +#include "curl_base64.h" +#include "strcase.h" +#include "multiif.h" +#include "url.h" +#include "urlapi-int.h" +#include "cfilters.h" +#include "connect.h" +#include "rand.h" +#include "strtoofft.h" +#include "strdup.h" +#include "transfer.h" +#include "dynbuf.h" +#include "headers.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if (NGHTTP2_VERSION_NUM < 0x010c00) +#error too old nghttp2 version, upgrade! +#endif + +#ifdef CURL_DISABLE_VERBOSE_STRINGS +#define nghttp2_session_callbacks_set_error_callback(x,y) +#endif + +#if (NGHTTP2_VERSION_NUM >= 0x010c00) +#define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 +#endif + + +/* buffer dimensioning: + * use 16K as chunk size, as that fits H2 DATA frames well */ +#define H2_CHUNK_SIZE (16 * 1024) +/* this is how much we want "in flight" for a stream */ +#define H2_STREAM_WINDOW_SIZE (10 * 1024 * 1024) +/* on receiving from TLS, we prep for holding a full stream window */ +#define H2_NW_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) +/* on send into TLS, we just want to accumulate small frames */ +#define H2_NW_SEND_CHUNKS 1 +/* stream recv/send chunks are a result of window / chunk sizes */ +#define H2_STREAM_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) +/* keep smaller stream upload buffer (default h2 window size) to have + * our progress bars and "upload done" reporting closer to reality */ +#define H2_STREAM_SEND_CHUNKS ((64 * 1024) / H2_CHUNK_SIZE) +/* spare chunks we keep for a full window */ +#define H2_STREAM_POOL_SPARES (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) + +/* We need to accommodate the max number of streams with their window sizes on + * the overall connection. Streams might become PAUSED which will block their + * received QUOTA in the connection window. If we run out of space, the server + * is blocked from sending us any data. See #10988 for an issue with this. */ +#define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE) + +#define H2_SETTINGS_IV_LEN 3 +#define H2_BINSETTINGS_LEN 80 + +static size_t populate_settings(nghttp2_settings_entry *iv, + struct Curl_easy *data) +{ + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = Curl_multi_max_concurrent_streams(data->multi); + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = H2_STREAM_WINDOW_SIZE; + + iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[2].value = data->multi->push_cb != NULL; + + return 3; +} + +static ssize_t populate_binsettings(uint8_t *binsettings, + struct Curl_easy *data) +{ + nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; + size_t ivlen; + + ivlen = populate_settings(iv, data); + /* this returns number of bytes it wrote or a negative number on error. */ + return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, + iv, ivlen); +} + +struct cf_h2_ctx { + nghttp2_session *h2; + /* The easy handle used in the current filter call, cleared at return */ + struct cf_call_data call_data; + + struct bufq inbufq; /* network input */ + struct bufq outbufq; /* network output */ + struct bufc_pool stream_bufcp; /* spares for stream buffers */ + struct dynbuf scratch; /* scratch buffer for temp use */ + + struct Curl_hash streams; /* hash of `data->id` to `h2_stream_ctx` */ + size_t drain_total; /* sum of all stream's UrlState drain */ + uint32_t max_concurrent_streams; + uint32_t goaway_error; /* goaway error code from server */ + int32_t remote_max_sid; /* max id processed by server */ + int32_t local_max_sid; /* max id processed by us */ + BIT(conn_closed); + BIT(rcvd_goaway); + BIT(sent_goaway); + BIT(enable_push); + BIT(nw_out_blocked); +}; + +/* How to access `call_data` from a cf_h2 filter */ +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) \ + ((struct cf_h2_ctx *)(cf)->ctx)->call_data + +static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx) +{ + struct cf_call_data save = ctx->call_data; + + if(ctx->h2) { + nghttp2_session_del(ctx->h2); + } + Curl_bufq_free(&ctx->inbufq); + Curl_bufq_free(&ctx->outbufq); + Curl_bufcp_free(&ctx->stream_bufcp); + Curl_dyn_free(&ctx->scratch); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); + memset(ctx, 0, sizeof(*ctx)); + ctx->call_data = save; +} + +static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) +{ + if(ctx) { + cf_h2_ctx_clear(ctx); + free(ctx); + } +} + +static CURLcode h2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data); + +/** + * All about the H2 internals of a stream + */ +struct h2_stream_ctx { + struct bufq recvbuf; /* response buffer */ + struct bufq sendbuf; /* request buffer */ + struct h1_req_parser h1; /* parsing the request */ + struct dynhds resp_trailers; /* response trailer fields */ + size_t resp_hds_len; /* amount of response header bytes in recvbuf */ + size_t upload_blocked_len; + curl_off_t upload_left; /* number of request bytes left to upload */ + curl_off_t nrcvd_data; /* number of DATA bytes received */ + + char **push_headers; /* allocated array */ + size_t push_headers_used; /* number of entries filled in */ + size_t push_headers_alloc; /* number of entries allocated */ + + int status_code; /* HTTP response status code */ + uint32_t error; /* stream error code */ + CURLcode xfer_result; /* Result of writing out response */ + uint32_t local_window_size; /* the local recv window size */ + int32_t id; /* HTTP/2 protocol identifier for stream */ + BIT(resp_hds_complete); /* we have a complete, final response */ + BIT(closed); /* TRUE on stream close */ + BIT(reset); /* TRUE on stream reset */ + BIT(close_handled); /* TRUE if stream closure is handled by libcurl */ + BIT(bodystarted); + BIT(send_closed); /* transfer is done sending, we might have still + buffered data in stream->sendbuf to upload. */ +}; + +#define H2_STREAM_CTX(ctx,data) ((struct h2_stream_ctx *)(\ + data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + +static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx) +{ + struct h2_stream_ctx *stream; + + (void)ctx; + stream = calloc(1, sizeof(*stream)); + if(!stream) + return NULL; + + stream->id = -1; + Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, + H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); + Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST); + stream->resp_hds_len = 0; + stream->bodystarted = FALSE; + stream->status_code = -1; + stream->closed = FALSE; + stream->close_handled = FALSE; + stream->error = NGHTTP2_NO_ERROR; + stream->local_window_size = H2_STREAM_WINDOW_SIZE; + stream->upload_left = 0; + stream->nrcvd_data = 0; + return stream; +} + +static void free_push_headers(struct h2_stream_ctx *stream) +{ + size_t i; + for(i = 0; ipush_headers_used; i++) + free(stream->push_headers[i]); + Curl_safefree(stream->push_headers); + stream->push_headers_used = 0; +} + +static void h2_stream_ctx_free(struct h2_stream_ctx *stream) +{ + Curl_bufq_free(&stream->sendbuf); + Curl_h1_req_parse_free(&stream->h1); + Curl_dynhds_free(&stream->resp_trailers); + free_push_headers(stream); + free(stream); +} + +static void h2_stream_hash_free(void *stream) +{ + DEBUGASSERT(stream); + h2_stream_ctx_free((struct h2_stream_ctx *)stream); +} + +/* + * Mark this transfer to get "drained". + */ +static void drain_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h2_stream_ctx *stream) +{ + unsigned char bits; + + (void)cf; + bits = CURL_CSELECT_IN; + if(!stream->send_closed && + (stream->upload_left || stream->upload_blocked_len)) + bits |= CURL_CSELECT_OUT; + if(data->state.select_bits != bits) { + CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x", + stream->id, bits); + data->state.select_bits = bits; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } +} + +static CURLcode http2_data_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h2_stream_ctx **pstream) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; + + (void)cf; + DEBUGASSERT(data); + stream = H2_STREAM_CTX(ctx, data); + if(stream) { + *pstream = stream; + return CURLE_OK; + } + + stream = h2_stream_ctx_create(ctx); + if(!stream) + return CURLE_OUT_OF_MEMORY; + + if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + h2_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; + } + + *pstream = stream; + return CURLE_OK; +} + +static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + + DEBUGASSERT(ctx); + if(!stream) + return; + + if(ctx->h2) { + bool flush_egress = FALSE; + /* returns error if stream not known, which is fine here */ + (void)nghttp2_session_set_stream_user_data(ctx->h2, stream->id, NULL); + + if(!stream->closed && stream->id > 0) { + /* RST_STREAM */ + CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream", + stream->id); + stream->closed = TRUE; + stream->reset = TRUE; + stream->send_closed = TRUE; + nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, + stream->id, NGHTTP2_STREAM_CLOSED); + flush_egress = TRUE; + } + + if(flush_egress) + nghttp2_session_send(ctx->h2); + } + + Curl_hash_offt_remove(&ctx->streams, data->id); +} + +static int h2_client_new(struct Curl_cfilter *cf, + nghttp2_session_callbacks *cbs) +{ + struct cf_h2_ctx *ctx = cf->ctx; + nghttp2_option *o; + + int rc = nghttp2_option_new(&o); + if(rc) + return rc; + /* We handle window updates ourself to enforce buffer limits */ + nghttp2_option_set_no_auto_window_update(o, 1); +#if NGHTTP2_VERSION_NUM >= 0x013200 + /* with 1.50.0 */ + /* turn off RFC 9113 leading and trailing white spaces validation against + HTTP field value. */ + nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); +#endif + rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o); + nghttp2_option_del(o); + return rc; +} + +static ssize_t nw_in_reader(void *reader_ctx, + unsigned char *buf, size_t buflen, + CURLcode *err) +{ + struct Curl_cfilter *cf = reader_ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + return Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); +} + +static ssize_t nw_out_writer(void *writer_ctx, + const unsigned char *buf, size_t buflen, + CURLcode *err) +{ + struct Curl_cfilter *cf = writer_ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + if(data) { + ssize_t nwritten = Curl_conn_cf_send(cf->next, data, + (const char *)buf, buflen, err); + if(nwritten > 0) + CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten); + return nwritten; + } + return 0; +} + +static ssize_t send_callback(nghttp2_session *h2, + const uint8_t *mem, size_t length, int flags, + void *userp); +static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, + void *userp); +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, + void *userp); +#endif +static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const uint8_t *mem, size_t len, void *userp); +static int on_stream_close(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *userp); +static int on_begin_headers(nghttp2_session *session, + const nghttp2_frame *frame, void *userp); +static int on_header(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *userp); +static int error_callback(nghttp2_session *session, const char *msg, + size_t len, void *userp); + +/* + * Initialize the cfilter context + */ +static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool via_h1_upgrade) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; + CURLcode result = CURLE_OUT_OF_MEMORY; + int rc; + nghttp2_session_callbacks *cbs = NULL; + + DEBUGASSERT(!ctx->h2); + Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES); + Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0); + Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0); + Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free); + ctx->remote_max_sid = 2147483647; + + rc = nghttp2_session_callbacks_new(&cbs); + if(rc) { + failf(data, "Couldn't initialize nghttp2 callbacks"); + goto out; + } + + nghttp2_session_callbacks_set_send_callback(cbs, send_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send); +#endif + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + cbs, on_data_chunk_recv); + nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close); + nghttp2_session_callbacks_set_on_begin_headers_callback( + cbs, on_begin_headers); + nghttp2_session_callbacks_set_on_header_callback(cbs, on_header); + nghttp2_session_callbacks_set_error_callback(cbs, error_callback); + + /* The nghttp2 session is not yet setup, do it */ + rc = h2_client_new(cf, cbs); + if(rc) { + failf(data, "Couldn't initialize nghttp2"); + goto out; + } + ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; + + if(via_h1_upgrade) { + /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted + * in the H1 request and we upgrade from there. This stream + * is opened implicitly as #1. */ + uint8_t binsettings[H2_BINSETTINGS_LEN]; + ssize_t binlen; /* length of the binsettings data */ + + binlen = populate_binsettings(binsettings, data); + if(binlen <= 0) { + failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); + result = CURLE_FAILED_INIT; + goto out; + } + + result = http2_data_setup(cf, data, &stream); + if(result) + goto out; + DEBUGASSERT(stream); + stream->id = 1; + /* queue SETTINGS frame (again) */ + rc = nghttp2_session_upgrade2(ctx->h2, binsettings, (size_t)binlen, + data->state.httpreq == HTTPREQ_HEAD, + NULL); + if(rc) { + failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + + rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id, + data); + if(rc) { + infof(data, "http/2: failed to set user_data for stream %u", + stream->id); + DEBUGASSERT(0); + } + CURL_TRC_CF(data, cf, "created session via Upgrade"); + } + else { + nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; + size_t ivlen; + + ivlen = populate_settings(iv, data); + rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, + iv, ivlen); + if(rc) { + failf(data, "nghttp2_submit_settings() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + } + + rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, + HTTP2_HUGE_WINDOW_SIZE); + if(rc) { + failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + + /* all set, traffic will be send on connect */ + result = CURLE_OK; + CURL_TRC_CF(data, cf, "[0] created h2 session%s", + via_h1_upgrade? " (via h1 upgrade)" : ""); + +out: + if(cbs) + nghttp2_session_callbacks_del(cbs); + return result; +} + +/* + * Returns nonzero if current HTTP/2 session should be closed. + */ +static int should_close_session(struct cf_h2_ctx *ctx) +{ + return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) && + !nghttp2_session_want_write(ctx->h2); +} + +/* + * Processes pending input left in network input buffer. + * This function returns 0 if it succeeds, or -1 and error code will + * be assigned to *err. + */ +static int h2_process_pending_input(struct Curl_cfilter *cf, + struct Curl_easy *data, + CURLcode *err) +{ + struct cf_h2_ctx *ctx = cf->ctx; + const unsigned char *buf; + size_t blen; + ssize_t rv; + + while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { + + rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); + if(rv < 0) { + failf(data, + "process_pending_input: nghttp2_session_mem_recv() returned " + "%zd:%s", rv, nghttp2_strerror((int)rv)); + *err = CURLE_RECV_ERROR; + return -1; + } + Curl_bufq_skip(&ctx->inbufq, (size_t)rv); + if(Curl_bufq_is_empty(&ctx->inbufq)) { + break; + } + else { + CURL_TRC_CF(data, cf, "process_pending_input: %zu bytes left " + "in connection buffer", Curl_bufq_len(&ctx->inbufq)); + } + } + + if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { + /* No more requests are allowed in the current session, so + the connection may not be reused. This is set when a + GOAWAY frame has been received or when the limit of stream + identifiers has been reached. */ + connclose(cf->conn, "http/2: No new requests allowed"); + } + + return 0; +} + +/* + * The server may send us data at any point (e.g. PING frames). Therefore, + * we cannot assume that an HTTP/2 socket is dead just because it is readable. + * + * Check the lower filters first and, if successful, peek at the socket + * and distinguish between closed and data. + */ +static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, + bool *input_pending) +{ + struct cf_h2_ctx *ctx = cf->ctx; + bool alive = TRUE; + + *input_pending = FALSE; + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + return FALSE; + + if(*input_pending) { + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, + only "protocol frames" */ + CURLcode result; + ssize_t nread = -1; + + *input_pending = FALSE; + nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); + if(nread != -1) { + CURL_TRC_CF(data, cf, "%zd bytes stray data read before trying " + "h2 connection", nread); + if(h2_process_pending_input(cf, data, &result) < 0) + /* immediate error, considered dead */ + alive = FALSE; + else { + alive = !should_close_session(ctx); + } + } + else if(result != CURLE_AGAIN) { + /* the read failed so let's say this is dead anyway */ + alive = FALSE; + } + } + + return alive; +} + +static CURLcode http2_send_ping(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + int rc; + + rc = nghttp2_submit_ping(ctx->h2, 0, ZERO_NULL); + if(rc) { + failf(data, "nghttp2_submit_ping() failed: %s(%d)", + nghttp2_strerror(rc), rc); + return CURLE_HTTP2; + } + + rc = nghttp2_session_send(ctx->h2); + if(rc) { + failf(data, "nghttp2_session_send() failed: %s(%d)", + nghttp2_strerror(rc), rc); + return CURLE_SEND_ERROR; + } + return CURLE_OK; +} + +/* + * Store nghttp2 version info in this buffer. + */ +void Curl_http2_ver(char *p, size_t len) +{ + nghttp2_info *h2 = nghttp2_version(0); + (void)msnprintf(p, len, "nghttp2/%s", h2->version_str); +} + +static CURLcode nw_out_flush(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + ssize_t nwritten; + CURLcode result; + + (void)data; + if(Curl_bufq_is_empty(&ctx->outbufq)) + return CURLE_OK; + + nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result); + if(nwritten < 0) { + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", + Curl_bufq_len(&ctx->outbufq)); + ctx->nw_out_blocked = 1; + } + return result; + } + return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN; +} + +/* + * The implementation of nghttp2_send_callback type. Here we write |data| with + * size |length| to the network and return the number of bytes actually + * written. See the documentation of nghttp2_send_callback for the details. + */ +static ssize_t send_callback(nghttp2_session *h2, + const uint8_t *buf, size_t blen, int flags, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nwritten; + CURLcode result = CURLE_OK; + + (void)h2; + (void)flags; + DEBUGASSERT(data); + + nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, + nw_out_writer, cf, &result); + if(nwritten < 0) { + if(result == CURLE_AGAIN) { + ctx->nw_out_blocked = 1; + return NGHTTP2_ERR_WOULDBLOCK; + } + failf(data, "Failed sending HTTP2 data"); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + if(!nwritten) { + ctx->nw_out_blocked = 1; + return NGHTTP2_ERR_WOULDBLOCK; + } + return nwritten; +} + + +/* We pass a pointer to this struct in the push callback, but the contents of + the struct are hidden from the user. */ +struct curl_pushheaders { + struct Curl_easy *data; + struct h2_stream_ctx *stream; + const nghttp2_push_promise *frame; +}; + +/* + * push header access function. Only to be used from within the push callback + */ +char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) +{ + /* Verify that we got a good easy handle in the push header struct, mostly to + detect rubbish input fast(er). */ + if(!h || !GOOD_EASY_HANDLE(h->data)) + return NULL; + else { + if(h->stream && num < h->stream->push_headers_used) + return h->stream->push_headers[num]; + } + return NULL; +} + +/* + * push header access function. Only to be used from within the push callback + */ +char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) +{ + struct h2_stream_ctx *stream; + size_t len; + size_t i; + /* Verify that we got a good easy handle in the push header struct, + mostly to detect rubbish input fast(er). Also empty header name + is just a rubbish too. We have to allow ":" at the beginning of + the header, but header == ":" must be rejected. If we have ':' in + the middle of header, it could be matched in middle of the value, + this is because we do prefix match.*/ + if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] || + !strcmp(header, ":") || strchr(header + 1, ':')) + return NULL; + + stream = h->stream; + if(!stream) + return NULL; + + len = strlen(header); + for(i = 0; ipush_headers_used; i++) { + if(!strncmp(header, stream->push_headers[i], len)) { + /* sub-match, make sure that it is followed by a colon */ + if(stream->push_headers[i][len] != ':') + continue; + return &stream->push_headers[i][len + 1]; + } + } + return NULL; +} + +static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct Curl_easy *second = curl_easy_duphandle(data); + if(second) { + struct h2_stream_ctx *second_stream; + http2_data_setup(cf, second, &second_stream); + second->state.priority.weight = data->state.priority.weight; + } + return second; +} + +static int set_transfer_url(struct Curl_easy *data, + struct curl_pushheaders *hp) +{ + const char *v; + CURLUcode uc; + char *url = NULL; + int rc = 0; + CURLU *u = curl_url(); + + if(!u) + return 5; + + v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME); + if(v) { + uc = curl_url_set(u, CURLUPART_SCHEME, v, 0); + if(uc) { + rc = 1; + goto fail; + } + } + + v = curl_pushheader_byname(hp, HTTP_PSEUDO_AUTHORITY); + if(v) { + uc = Curl_url_set_authority(u, v); + if(uc) { + rc = 2; + goto fail; + } + } + + v = curl_pushheader_byname(hp, HTTP_PSEUDO_PATH); + if(v) { + uc = curl_url_set(u, CURLUPART_PATH, v, 0); + if(uc) { + rc = 3; + goto fail; + } + } + + uc = curl_url_get(u, CURLUPART_URL, &url, 0); + if(uc) + rc = 4; +fail: + curl_url_cleanup(u); + if(rc) + return rc; + + if(data->state.url_alloc) + free(data->state.url); + data->state.url_alloc = TRUE; + data->state.url = url; + return 0; +} + +static void discard_newhandle(struct Curl_cfilter *cf, + struct Curl_easy *newhandle) +{ + http2_data_done(cf, newhandle); + (void)Curl_close(&newhandle); +} + +static int push_promise(struct Curl_cfilter *cf, + struct Curl_easy *data, + const nghttp2_push_promise *frame) +{ + struct cf_h2_ctx *ctx = cf->ctx; + int rv; /* one of the CURL_PUSH_* defines */ + + CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received", + frame->promised_stream_id); + if(data->multi->push_cb) { + struct h2_stream_ctx *stream; + struct h2_stream_ctx *newstream; + struct curl_pushheaders heads; + CURLMcode rc; + CURLcode result; + /* clone the parent */ + struct Curl_easy *newhandle = h2_duphandle(cf, data); + if(!newhandle) { + infof(data, "failed to duplicate handle"); + rv = CURL_PUSH_DENY; /* FAIL HARD */ + goto fail; + } + + /* ask the application */ + CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application"); + + stream = H2_STREAM_CTX(ctx, data); + if(!stream) { + failf(data, "Internal NULL stream"); + discard_newhandle(cf, newhandle); + rv = CURL_PUSH_DENY; + goto fail; + } + + heads.data = data; + heads.stream = stream; + heads.frame = frame; + + rv = set_transfer_url(newhandle, &heads); + if(rv) { + discard_newhandle(cf, newhandle); + rv = CURL_PUSH_DENY; + goto fail; + } + + result = http2_data_setup(cf, newhandle, &newstream); + if(result) { + failf(data, "error setting up stream: %d", result); + discard_newhandle(cf, newhandle); + rv = CURL_PUSH_DENY; + goto fail; + } + DEBUGASSERT(stream); + + Curl_set_in_callback(data, true); + rv = data->multi->push_cb(data, newhandle, + stream->push_headers_used, &heads, + data->multi->push_userp); + Curl_set_in_callback(data, false); + + /* free the headers again */ + free_push_headers(stream); + + if(rv) { + DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); + /* denied, kill off the new handle again */ + discard_newhandle(cf, newhandle); + goto fail; + } + + newstream->id = frame->promised_stream_id; + newhandle->req.maxdownload = -1; + newhandle->req.size = -1; + + /* approved, add to the multi handle and immediately switch to PERFORM + state with the given connection !*/ + rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn); + if(rc) { + infof(data, "failed to add handle to multi"); + discard_newhandle(cf, newhandle); + rv = CURL_PUSH_DENY; + goto fail; + } + + rv = nghttp2_session_set_stream_user_data(ctx->h2, + newstream->id, + newhandle); + if(rv) { + infof(data, "failed to set user_data for stream %u", + newstream->id); + DEBUGASSERT(0); + rv = CURL_PUSH_DENY; + goto fail; + } + + /* success, remember max stream id processed */ + if(newstream->id > ctx->local_max_sid) + ctx->local_max_sid = newstream->id; + } + else { + CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it"); + rv = CURL_PUSH_DENY; + } +fail: + return rv; +} + +static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h2_stream_ctx *stream, + const char *buf, size_t blen, bool eos) +{ + + /* If we already encountered an error, skip further writes */ + if(!stream->xfer_result) { + stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); + if(stream->xfer_result) + CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers", + stream->id, stream->xfer_result, blen); + } +} + +static void h2_xfer_write_resp(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h2_stream_ctx *stream, + const char *buf, size_t blen, bool eos) +{ + + /* If we already encountered an error, skip further writes */ + if(!stream->xfer_result) + stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); + /* If the transfer write is errored, we do not want any more data */ + if(stream->xfer_result) { + struct cf_h2_ctx *ctx = cf->ctx; + CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of data, " + "RST-ing stream", + stream->id, stream->xfer_result, blen); + nghttp2_submit_rst_stream(ctx->h2, 0, stream->id, + (uint32_t)NGHTTP2_ERR_CALLBACK_FAILURE); + } +} + +static CURLcode on_stream_frame(struct Curl_cfilter *cf, + struct Curl_easy *data, + const nghttp2_frame *frame) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + int32_t stream_id = frame->hd.stream_id; + int rv; + + if(!stream) { + CURL_TRC_CF(data, cf, "[%d] No stream_ctx set", stream_id); + return CURLE_FAILED_INIT; + } + + switch(frame->hd.type) { + case NGHTTP2_DATA: + CURL_TRC_CF(data, cf, "[%d] DATA, window=%d/%d", + stream_id, + nghttp2_session_get_stream_effective_recv_data_length( + ctx->h2, stream->id), + nghttp2_session_get_stream_effective_local_window_size( + ctx->h2, stream->id)); + /* If !body started on this stream, then receiving DATA is illegal. */ + if(!stream->bodystarted) { + rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, + stream_id, NGHTTP2_PROTOCOL_ERROR); + + if(nghttp2_is_fatal(rv)) { + return CURLE_RECV_ERROR; + } + } + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + drain_stream(cf, data, stream); + } + break; + case NGHTTP2_HEADERS: + if(stream->bodystarted) { + /* Only valid HEADERS after body started is trailer HEADERS. We + buffer them in on_header callback. */ + break; + } + + /* nghttp2 guarantees that :status is received, and we store it to + stream->status_code. Fuzzing has proven this can still be reached + without status code having been set. */ + if(stream->status_code == -1) + return CURLE_RECV_ERROR; + + /* Only final status code signals the end of header */ + if(stream->status_code / 100 != 1) { + stream->bodystarted = TRUE; + stream->status_code = -1; + } + + h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); + + if(stream->status_code / 100 != 1) { + stream->resp_hds_complete = TRUE; + } + drain_stream(cf, data, stream); + break; + case NGHTTP2_PUSH_PROMISE: + rv = push_promise(cf, data, &frame->push_promise); + if(rv) { /* deny! */ + DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); + rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, + frame->push_promise.promised_stream_id, + NGHTTP2_CANCEL); + if(nghttp2_is_fatal(rv)) + return CURLE_SEND_ERROR; + else if(rv == CURL_PUSH_ERROROUT) { + CURL_TRC_CF(data, cf, "[%d] fail in PUSH_PROMISE received", + stream_id); + return CURLE_RECV_ERROR; + } + } + break; + case NGHTTP2_RST_STREAM: + stream->closed = TRUE; + if(frame->rst_stream.error_code) { + stream->reset = TRUE; + } + stream->send_closed = TRUE; + drain_stream(cf, data, stream); + break; + case NGHTTP2_WINDOW_UPDATE: + if(CURL_WANT_SEND(data)) { + drain_stream(cf, data, stream); + } + break; + default: + break; + } + return CURLE_OK; +} + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen) +{ + switch(frame->hd.type) { + case NGHTTP2_DATA: { + return msnprintf(buffer, blen, + "FRAME[DATA, len=%d, eos=%d, padlen=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM), + (int)frame->data.padlen); + } + case NGHTTP2_HEADERS: { + return msnprintf(buffer, blen, + "FRAME[HEADERS, len=%d, hend=%d, eos=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); + } + case NGHTTP2_PRIORITY: { + return msnprintf(buffer, blen, + "FRAME[PRIORITY, len=%d, flags=%d]", + (int)frame->hd.length, frame->hd.flags); + } + case NGHTTP2_RST_STREAM: { + return msnprintf(buffer, blen, + "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]", + (int)frame->hd.length, frame->hd.flags, + frame->rst_stream.error_code); + } + case NGHTTP2_SETTINGS: { + if(frame->hd.flags & NGHTTP2_FLAG_ACK) { + return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); + } + return msnprintf(buffer, blen, + "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); + } + case NGHTTP2_PUSH_PROMISE: { + return msnprintf(buffer, blen, + "FRAME[PUSH_PROMISE, len=%d, hend=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); + } + case NGHTTP2_PING: { + return msnprintf(buffer, blen, + "FRAME[PING, len=%d, ack=%d]", + (int)frame->hd.length, + frame->hd.flags&NGHTTP2_FLAG_ACK); + } + case NGHTTP2_GOAWAY: { + char scratch[128]; + size_t s_len = sizeof(scratch)/sizeof(scratch[0]); + size_t len = (frame->goaway.opaque_data_len < s_len)? + frame->goaway.opaque_data_len : s_len-1; + if(len) + memcpy(scratch, frame->goaway.opaque_data, len); + scratch[len] = '\0'; + return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', " + "last_stream=%d]", frame->goaway.error_code, + scratch, frame->goaway.last_stream_id); + } + case NGHTTP2_WINDOW_UPDATE: { + return msnprintf(buffer, blen, + "FRAME[WINDOW_UPDATE, incr=%d]", + frame->window_update.window_size_increment); + } + default: + return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", + frame->hd.type, (int)frame->hd.length, + frame->hd.flags); + } +} + +static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)session; + DEBUGASSERT(data); + if(data && Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); + } + return 0; +} +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + +static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf), *data_s; + int32_t stream_id = frame->hd.stream_id; + + DEBUGASSERT(data); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer); + } +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + + if(!stream_id) { + /* stream ID zero is for connection-oriented stuff */ + DEBUGASSERT(data); + switch(frame->hd.type) { + case NGHTTP2_SETTINGS: { + if(!(frame->hd.flags & NGHTTP2_FLAG_ACK)) { + uint32_t max_conn = ctx->max_concurrent_streams; + ctx->max_concurrent_streams = nghttp2_session_get_remote_settings( + session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); + ctx->enable_push = nghttp2_session_get_remote_settings( + session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; + CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d", + ctx->max_concurrent_streams); + CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s", + ctx->enable_push ? "TRUE" : "false"); + if(data && max_conn != ctx->max_concurrent_streams) { + /* only signal change if the value actually changed */ + CURL_TRC_CF(data, cf, "[0] notify MAX_CONCURRENT_STREAMS: %u", + ctx->max_concurrent_streams); + Curl_multi_connchanged(data->multi); + } + /* Since the initial stream window is 64K, a request might be on HOLD, + * due to exhaustion. The (initial) SETTINGS may announce a much larger + * window and *assume* that we treat this like a WINDOW_UPDATE. Some + * servers send an explicit WINDOW_UPDATE, but not all seem to do that. + * To be safe, we UNHOLD a stream in order not to stall. */ + if(CURL_WANT_SEND(data)) { + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + if(stream) + drain_stream(cf, data, stream); + } + } + break; + } + case NGHTTP2_GOAWAY: + ctx->rcvd_goaway = TRUE; + ctx->goaway_error = frame->goaway.error_code; + ctx->remote_max_sid = frame->goaway.last_stream_id; + if(data) { + infof(data, "received GOAWAY, error=%u, last_stream=%u", + ctx->goaway_error, ctx->remote_max_sid); + Curl_multi_connchanged(data->multi); + } + break; + default: + break; + } + return 0; + } + + data_s = nghttp2_session_get_stream_user_data(session, stream_id); + if(!data_s) { + CURL_TRC_CF(data, cf, "[%d] No Curl_easy associated", stream_id); + return 0; + } + + return on_stream_frame(cf, data_s, frame)? NGHTTP2_ERR_CALLBACK_FAILURE : 0; +} + +static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const uint8_t *mem, size_t len, void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; + struct Curl_easy *data_s; + (void)flags; + + DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ + DEBUGASSERT(CF_DATA_CURRENT(cf)); + + /* get the stream from the hash based on Stream ID */ + data_s = nghttp2_session_get_stream_user_data(session, stream_id); + if(!data_s) { + /* Receiving a Stream ID not in the hash should not happen - unless + we have aborted a transfer artificially and there were more data + in the pipeline. Silently ignore. */ + CURL_TRC_CF(CF_DATA_CURRENT(cf), cf, "[%d] Data for unknown", + stream_id); + /* consumed explicitly as no one will read it */ + nghttp2_session_consume(session, stream_id, len); + return 0; + } + + stream = H2_STREAM_CTX(ctx, data_s); + if(!stream) + return NGHTTP2_ERR_CALLBACK_FAILURE; + + h2_xfer_write_resp(cf, data_s, stream, (char *)mem, len, FALSE); + + nghttp2_session_consume(ctx->h2, stream_id, len); + stream->nrcvd_data += (curl_off_t)len; + + /* if we receive data for another handle, wake that up */ + drain_stream(cf, data_s, stream); + return 0; +} + +static int on_stream_close(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; + struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf); + struct h2_stream_ctx *stream; + int rv; + (void)session; + + DEBUGASSERT(call_data); + /* get the stream from the hash based on Stream ID, stream ID zero is for + connection-oriented stuff */ + data_s = stream_id? + nghttp2_session_get_stream_user_data(session, stream_id) : NULL; + if(!data_s) { + CURL_TRC_CF(call_data, cf, + "[%d] on_stream_close, no easy set on stream", stream_id); + return 0; + } + if(!GOOD_EASY_HANDLE(data_s)) { + /* nghttp2 still has an easy registered for the stream which has + * been freed be libcurl. This points to a code path that does not + * trigger DONE or DETACH events as it must. */ + CURL_TRC_CF(call_data, cf, + "[%d] on_stream_close, not a GOOD easy on stream", stream_id); + (void)nghttp2_session_set_stream_user_data(session, stream_id, 0); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + stream = H2_STREAM_CTX(ctx, data_s); + if(!stream) { + CURL_TRC_CF(data_s, cf, + "[%d] on_stream_close, GOOD easy but no stream", stream_id); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + stream->closed = TRUE; + stream->error = error_code; + if(stream->error) { + stream->reset = TRUE; + stream->send_closed = TRUE; + } + + if(stream->error) + CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)", + stream_id, nghttp2_http2_strerror(error_code), error_code); + else + CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id); + drain_stream(cf, data_s, stream); + + /* remove `data_s` from the nghttp2 stream */ + rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); + if(rv) { + infof(data_s, "http/2: failed to clear user_data for stream %u", + stream_id); + DEBUGASSERT(0); + } + return 0; +} + +static int on_begin_headers(nghttp2_session *session, + const nghttp2_frame *frame, void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; + struct Curl_easy *data_s = NULL; + + (void)cf; + data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if(!data_s) { + return 0; + } + + if(frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + stream = H2_STREAM_CTX(ctx, data_s); + if(!stream || !stream->bodystarted) { + return 0; + } + + return 0; +} + +/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */ +static int on_header(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; + struct Curl_easy *data_s; + int32_t stream_id = frame->hd.stream_id; + CURLcode result; + (void)flags; + + DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ + + /* get the stream from the hash based on Stream ID */ + data_s = nghttp2_session_get_stream_user_data(session, stream_id); + if(!data_s) + /* Receiving a Stream ID not in the hash should not happen, this is an + internal error more than anything else! */ + return NGHTTP2_ERR_CALLBACK_FAILURE; + + stream = H2_STREAM_CTX(ctx, data_s); + if(!stream) { + failf(data_s, "Internal NULL stream"); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + /* Store received PUSH_PROMISE headers to be used when the subsequent + PUSH_PROMISE callback comes */ + if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { + char *h; + + if(!strcmp(HTTP_PSEUDO_AUTHORITY, (const char *)name)) { + /* pseudo headers are lower case */ + int rc = 0; + char *check = aprintf("%s:%d", cf->conn->host.name, + cf->conn->remote_port); + if(!check) + /* no memory */ + return NGHTTP2_ERR_CALLBACK_FAILURE; + if(!strcasecompare(check, (const char *)value) && + ((cf->conn->remote_port != cf->conn->given->defport) || + !strcasecompare(cf->conn->host.name, (const char *)value))) { + /* This is push is not for the same authority that was asked for in + * the URL. RFC 7540 section 8.2 says: "A client MUST treat a + * PUSH_PROMISE for which the server is not authoritative as a stream + * error of type PROTOCOL_ERROR." + */ + (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + stream_id, NGHTTP2_PROTOCOL_ERROR); + rc = NGHTTP2_ERR_CALLBACK_FAILURE; + } + free(check); + if(rc) + return rc; + } + + if(!stream->push_headers) { + stream->push_headers_alloc = 10; + stream->push_headers = malloc(stream->push_headers_alloc * + sizeof(char *)); + if(!stream->push_headers) + return NGHTTP2_ERR_CALLBACK_FAILURE; + stream->push_headers_used = 0; + } + else if(stream->push_headers_used == + stream->push_headers_alloc) { + char **headp; + if(stream->push_headers_alloc > 1000) { + /* this is beyond crazy many headers, bail out */ + failf(data_s, "Too many PUSH_PROMISE headers"); + free_push_headers(stream); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + stream->push_headers_alloc *= 2; + headp = realloc(stream->push_headers, + stream->push_headers_alloc * sizeof(char *)); + if(!headp) { + free_push_headers(stream); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + stream->push_headers = headp; + } + h = aprintf("%s:%s", name, value); + if(h) + stream->push_headers[stream->push_headers_used++] = h; + return 0; + } + + if(stream->bodystarted) { + /* This is a trailer */ + CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s", + stream->id, (int)namelen, name, (int)valuelen, value); + result = Curl_dynhds_add(&stream->resp_trailers, + (const char *)name, namelen, + (const char *)value, valuelen); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + + return 0; + } + + if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && + memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { + /* nghttp2 guarantees :status is received first and only once. */ + char buffer[32]; + result = Curl_http_decode_status(&stream->status_code, + (const char *)value, valuelen); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r", + stream->status_code); + result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + Curl_dyn_reset(&ctx->scratch); + result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 ")); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, value, valuelen); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); + if(!result) + h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch), + Curl_dyn_len(&ctx->scratch), FALSE); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + /* if we receive data for another handle, wake that up */ + if(CF_DATA_CURRENT(cf) != data_s) + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + + CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d", + stream->id, stream->status_code); + return 0; + } + + /* nghttp2 guarantees that namelen > 0, and :status was already + received, and this is not pseudo-header field . */ + /* convert to an HTTP1-style header */ + Curl_dyn_reset(&ctx->scratch); + result = Curl_dyn_addn(&ctx->scratch, (const char *)name, namelen); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST(": ")); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, (const char *)value, valuelen); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n")); + if(!result) + h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch), + Curl_dyn_len(&ctx->scratch), FALSE); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + /* if we receive data for another handle, wake that up */ + if(CF_DATA_CURRENT(cf) != data_s) + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + + CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s", + stream->id, (int)namelen, name, (int)valuelen, value); + + return 0; /* 0 is successful */ +} + +static ssize_t req_body_read_callback(nghttp2_session *session, + int32_t stream_id, + uint8_t *buf, size_t length, + uint32_t *data_flags, + nghttp2_data_source *source, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; + struct Curl_easy *data_s; + struct h2_stream_ctx *stream = NULL; + CURLcode result; + ssize_t nread; + (void)source; + + (void)cf; + if(stream_id) { + /* get the stream from the hash based on Stream ID, stream ID zero is for + connection-oriented stuff */ + data_s = nghttp2_session_get_stream_user_data(session, stream_id); + if(!data_s) + /* Receiving a Stream ID not in the hash should not happen, this is an + internal error more than anything else! */ + return NGHTTP2_ERR_CALLBACK_FAILURE; + + stream = H2_STREAM_CTX(ctx, data_s); + if(!stream) + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + else + return NGHTTP2_ERR_INVALID_ARGUMENT; + + nread = Curl_bufq_read(&stream->sendbuf, buf, length, &result); + if(nread < 0) { + if(result != CURLE_AGAIN) + return NGHTTP2_ERR_CALLBACK_FAILURE; + nread = 0; + } + + if(nread > 0 && stream->upload_left != -1) + stream->upload_left -= nread; + + CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) left=%" + CURL_FORMAT_CURL_OFF_T " -> %zd, %d", + stream_id, length, stream->upload_left, nread, result); + + if(stream->upload_left == 0) + *data_flags = NGHTTP2_DATA_FLAG_EOF; + else if(nread == 0) + return NGHTTP2_ERR_DEFERRED; + + return nread; +} + +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) +static int error_callback(nghttp2_session *session, + const char *msg, + size_t len, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + (void)session; + failf(data, "%.*s", (int)len, msg); + return 0; +} +#endif + +/* + * Append headers to ask for an HTTP1.1 to HTTP2 upgrade. + */ +CURLcode Curl_http2_request_upgrade(struct dynbuf *req, + struct Curl_easy *data) +{ + CURLcode result; + char *base64; + size_t blen; + struct SingleRequest *k = &data->req; + uint8_t binsettings[H2_BINSETTINGS_LEN]; + ssize_t binlen; /* length of the binsettings data */ + + binlen = populate_binsettings(binsettings, data); + if(binlen <= 0) { + failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); + Curl_dyn_free(req); + return CURLE_FAILED_INIT; + } + + result = Curl_base64url_encode((const char *)binsettings, (size_t)binlen, + &base64, &blen); + if(result) { + Curl_dyn_free(req); + return result; + } + + result = Curl_dyn_addf(req, + "Connection: Upgrade, HTTP2-Settings\r\n" + "Upgrade: %s\r\n" + "HTTP2-Settings: %s\r\n", + NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); + free(base64); + + k->upgr101 = UPGR101_H2; + + return result; +} + +static CURLcode http2_data_done_send(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + + if(!ctx || !ctx->h2 || !stream) + goto out; + + CURL_TRC_CF(data, cf, "[%d] data done send", stream->id); + if(!stream->send_closed) { + stream->send_closed = TRUE; + if(stream->upload_left) { + /* we now know that everything that is buffered is all there is. */ + stream->upload_left = Curl_bufq_len(&stream->sendbuf); + /* resume sending here to trigger the callback to get called again so + that it can signal EOF to nghttp2 */ + (void)nghttp2_session_resume_data(ctx->h2, stream->id); + drain_stream(cf, data, stream); + } + } + +out: + return result; +} + +static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h2_stream_ctx *stream, + CURLcode *err) +{ + ssize_t rv = 0; + + if(stream->error == NGHTTP2_REFUSED_STREAM) { + CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " + "connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + data->state.refused_stream = TRUE; + *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ + return -1; + } + else if(stream->error != NGHTTP2_NO_ERROR) { + if(stream->resp_hds_complete && data->req.no_body) { + CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " + "not want a body anyway, ignore: %s (err %u)", + stream->id, nghttp2_http2_strerror(stream->error), + stream->error); + stream->close_handled = TRUE; + *err = CURLE_OK; + goto out; + } + failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", + stream->id, nghttp2_http2_strerror(stream->error), + stream->error); + *err = CURLE_HTTP2_STREAM; + return -1; + } + else if(stream->reset) { + failf(data, "HTTP/2 stream %u was reset", stream->id); + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2; + return -1; + } + + if(!stream->bodystarted) { + failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " + " all response header fields, treated as error", + stream->id); + *err = CURLE_HTTP2_STREAM; + return -1; + } + + if(Curl_dynhds_count(&stream->resp_trailers)) { + struct dynhds_entry *e; + struct dynbuf dbuf; + size_t i; + + *err = CURLE_OK; + Curl_dyn_init(&dbuf, DYN_TRAILERS); + for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) { + e = Curl_dynhds_getn(&stream->resp_trailers, i); + if(!e) + break; + Curl_dyn_reset(&dbuf); + *err = Curl_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a", + (int)e->namelen, e->name, + (int)e->valuelen, e->value); + if(*err) + break; + Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&dbuf), + Curl_dyn_len(&dbuf)); + *err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER, + Curl_dyn_ptr(&dbuf), Curl_dyn_len(&dbuf)); + if(*err) + break; + } + Curl_dyn_free(&dbuf); + if(*err) + goto out; + } + + stream->close_handled = TRUE; + *err = CURLE_OK; + rv = 0; + +out: + CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err); + return rv; +} + +static int sweight_wanted(const struct Curl_easy *data) +{ + /* 0 weight is not set by user and we take the nghttp2 default one */ + return data->set.priority.weight? + data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT; +} + +static int sweight_in_effect(const struct Curl_easy *data) +{ + /* 0 weight is not set by user and we take the nghttp2 default one */ + return data->state.priority.weight? + data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT; +} + +/* + * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight + * and dependency to the peer. It also stores the updated values in the state + * struct. + */ + +static void h2_pri_spec(struct cf_h2_ctx *ctx, + struct Curl_easy *data, + nghttp2_priority_spec *pri_spec) +{ + struct Curl_data_priority *prio = &data->set.priority; + struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent); + int32_t depstream_id = depstream? depstream->id:0; + nghttp2_priority_spec_init(pri_spec, depstream_id, + sweight_wanted(data), + data->set.priority.exclusive); + data->state.priority = *prio; +} + +/* + * Check if there is been an update in the priority / + * dependency settings and if so it submits a PRIORITY frame with the updated + * info. + * Flush any out data pending in the network buffer. + */ +static CURLcode h2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + int rv = 0; + + if(stream && stream->id > 0 && + ((sweight_wanted(data) != sweight_in_effect(data)) || + (data->set.priority.exclusive != data->state.priority.exclusive) || + (data->set.priority.parent != data->state.priority.parent)) ) { + /* send new weight and/or dependency */ + nghttp2_priority_spec pri_spec; + + h2_pri_spec(ctx, data, &pri_spec); + CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); + DEBUGASSERT(stream->id != -1); + rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, + stream->id, &pri_spec); + if(rv) + goto out; + } + + ctx->nw_out_blocked = 0; + while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2)) + rv = nghttp2_session_send(ctx->h2); + +out: + if(nghttp2_is_fatal(rv)) { + CURL_TRC_CF(data, cf, "nghttp2_session_send error (%s)%d", + nghttp2_strerror(rv), rv); + return CURLE_SEND_ERROR; + } + return nw_out_flush(cf, data); +} + +static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + struct h2_stream_ctx *stream, + char *buf, size_t len, CURLcode *err) +{ + struct cf_h2_ctx *ctx = cf->ctx; + ssize_t nread = -1; + + (void)buf; + *err = CURLE_AGAIN; + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%d] xfer write failed", stream->id); + *err = stream->xfer_result; + nread = -1; + } + else if(stream->closed) { + CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id); + nread = http2_handle_stream_close(cf, data, stream, err); + } + else if(stream->reset || + (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || + (ctx->rcvd_goaway && ctx->remote_max_sid < stream->id)) { + CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id); + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2; + nread = -1; + } + + if(nread < 0 && *err != CURLE_AGAIN) + CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d", + stream->id, len, nread, *err); + return nread; +} + +static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data, + size_t data_max_bytes) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; + CURLcode result = CURLE_OK; + ssize_t nread; + + /* Process network input buffer fist */ + if(!Curl_bufq_is_empty(&ctx->inbufq)) { + CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer", + Curl_bufq_len(&ctx->inbufq)); + if(h2_process_pending_input(cf, data, &result) < 0) + return result; + } + + /* Receive data from the "lower" filters, e.g. network until + * it is time to stop due to connection close or us not processing + * all network input */ + while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { + stream = H2_STREAM_CTX(ctx, data); + if(stream && (stream->closed || !data_max_bytes)) { + /* We would like to abort here and stop processing, so that + * the transfer loop can handle the data/close here. However, + * this may leave data in underlying buffers that will not + * be consumed. */ + if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data)) + drain_stream(cf, data, stream); + break; + } + + nread = Curl_bufq_sipn(&ctx->inbufq, 0, nw_in_reader, cf, &result); + if(nread < 0) { + if(result != CURLE_AGAIN) { + failf(data, "Failed receiving HTTP2 data: %d(%s)", result, + curl_easy_strerror(result)); + return result; + } + break; + } + else if(nread == 0) { + CURL_TRC_CF(data, cf, "[0] ingress: connection closed"); + ctx->conn_closed = TRUE; + break; + } + else { + CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", nread); + data_max_bytes = (data_max_bytes > (size_t)nread)? + (data_max_bytes - (size_t)nread) : 0; + } + + if(h2_process_pending_input(cf, data, &result)) + return result; + } + + if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { + connclose(cf->conn, "GOAWAY received"); + } + + return CURLE_OK; +} + +static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + ssize_t nread = -1; + CURLcode result; + struct cf_call_data save; + + if(!stream) { + /* Abnormal call sequence: either this transfer has never opened a stream + * (unlikely) or the transfer has been done, cleaned up its resources, but + * a read() is called anyway. It is not clear what the calling sequence + * is for such a case. */ + failf(data, "[%zd-%zd], http/2 recv on a transfer never opened " + "or already cleared", (ssize_t)data->id, + (ssize_t)cf->conn->connection_id); + *err = CURLE_HTTP2; + return -1; + } + + CF_DATA_SAVE(save, cf, data); + + nread = stream_recv(cf, data, stream, buf, len, err); + if(nread < 0 && *err != CURLE_AGAIN) + goto out; + + if(nread < 0) { + *err = h2_progress_ingress(cf, data, len); + if(*err) + goto out; + + nread = stream_recv(cf, data, stream, buf, len, err); + } + + if(nread > 0) { + size_t data_consumed = (size_t)nread; + /* Now that we transferred this to the upper layer, we report + * the actual amount of DATA consumed to the H2 session, so + * that it adjusts stream flow control */ + if(stream->resp_hds_len >= data_consumed) { + stream->resp_hds_len -= data_consumed; /* no DATA */ + } + else { + if(stream->resp_hds_len) { + data_consumed -= stream->resp_hds_len; + stream->resp_hds_len = 0; + } + if(data_consumed) { + nghttp2_session_consume(ctx->h2, stream->id, data_consumed); + } + } + + if(stream->closed) { + CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id); + drain_stream(cf, data, stream); + } + } + +out: + result = h2_progress_egress(cf, data); + if(result == CURLE_AGAIN) { + /* pending data to send, need to be called again. Ideally, we would + * monitor the socket for POLLOUT, but we might not be in SENDING + * transfer state any longer and are unable to make this happen. + */ + drain_stream(cf, data, stream); + } + else if(result) { + *err = result; + nread = -1; + } + CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, " + "window=%d/%d, connection %d/%d", + stream->id, len, nread, *err, + nghttp2_session_get_stream_effective_recv_data_length( + ctx->h2, stream->id), + nghttp2_session_get_stream_effective_local_window_size( + ctx->h2, stream->id), + nghttp2_session_get_local_window_size(ctx->h2), + HTTP2_HUGE_WINDOW_SIZE); + + CF_DATA_RESTORE(cf, save); + return nread; +} + +static ssize_t h2_submit(struct h2_stream_ctx **pstream, + struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, + size_t *phdslen, CURLcode *err) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream = NULL; + struct dynhds h2_headers; + nghttp2_nv *nva = NULL; + const void *body = NULL; + size_t nheader, bodylen, i; + nghttp2_data_provider data_prd; + int32_t stream_id; + nghttp2_priority_spec pri_spec; + ssize_t nwritten; + + *phdslen = 0; + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + + *err = http2_data_setup(cf, data, &stream); + if(*err) { + nwritten = -1; + goto out; + } + + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); + if(nwritten < 0) + goto out; + *phdslen = (size_t)nwritten; + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); + + *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); + if(*err) { + nwritten = -1; + goto out; + } + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); + + nva = Curl_dynhds_to_nva(&h2_headers, &nheader); + if(!nva) { + *err = CURLE_OUT_OF_MEMORY; + nwritten = -1; + goto out; + } + + h2_pri_spec(ctx, data, &pri_spec); + if(!nghttp2_session_check_request_allowed(ctx->h2)) + CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: + if(data->state.infilesize != -1) + stream->upload_left = data->state.infilesize; + else + /* data sending without specifying the data amount up front */ + stream->upload_left = -1; /* unknown */ + + data_prd.read_callback = req_body_read_callback; + data_prd.source.ptr = NULL; + stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader, + &data_prd, data); + break; + default: + stream->upload_left = 0; /* no request body */ + stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader, + NULL, data); + } + + if(stream_id < 0) { + CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", + nghttp2_strerror(stream_id), stream_id); + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + +#define MAX_ACC 60000 /* <64KB to account for some overhead */ + if(Curl_trc_is_verbose(data)) { + size_t acc = 0; + + infof(data, "[HTTP/2] [%d] OPENED stream for %s", + stream_id, data->state.url); + for(i = 0; i < nheader; ++i) { + acc += nva[i].namelen + nva[i].valuelen; + + infof(data, "[HTTP/2] [%d] [%.*s: %.*s]", stream_id, + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + + if(acc > MAX_ACC) { + infof(data, "[HTTP/2] Warning: The cumulative length of all " + "headers exceeds %d bytes and that could cause the " + "stream to be rejected.", MAX_ACC); + } + } + + stream->id = stream_id; + stream->local_window_size = H2_STREAM_WINDOW_SIZE; + if(data->set.max_recv_speed) { + /* We are asked to only receive `max_recv_speed` bytes per second. + * Let's limit our stream window size around that, otherwise the server + * will send in large bursts only. We make the window 50% larger to + * allow for data in flight and avoid stalling. */ + curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1); + n += CURLMAX((n/2), 1); + if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) && + n < (UINT_MAX / H2_CHUNK_SIZE)) { + stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE; + } + } + + body = (const char *)buf + nwritten; + bodylen = len - nwritten; + + if(bodylen) { + /* We have request body to send in DATA frame */ + ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err); + if(n < 0) { + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + nwritten += n; + } + +out: + CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d", + stream? stream->id : -1, nwritten, *err); + Curl_safefree(nva); + *pstream = stream; + Curl_dynhds_free(&h2_headers); + return nwritten; +} + +static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + struct cf_call_data save; + int rv; + ssize_t nwritten; + size_t hdslen = 0; + CURLcode result; + int blocked = 0, was_blocked = 0; + + CF_DATA_SAVE(save, cf, data); + + if(stream && stream->id != -1) { + if(stream->upload_blocked_len) { + /* the data in `buf` has already been submitted or added to the + * buffers, but have been EAGAINed on the last invocation. */ + /* TODO: this assertion triggers in OSSFuzz runs and it is not + * clear why. Disable for now to let OSSFuzz continue its tests. */ + DEBUGASSERT(len >= stream->upload_blocked_len); + if(len < stream->upload_blocked_len) { + /* Did we get called again with a smaller `len`? This should not + * happen. We are not prepared to handle that. */ + failf(data, "HTTP/2 send again with decreased length (%zd vs %zd)", + len, stream->upload_blocked_len); + *err = CURLE_HTTP2; + nwritten = -1; + goto out; + } + nwritten = (ssize_t)stream->upload_blocked_len; + stream->upload_blocked_len = 0; + was_blocked = 1; + } + else if(stream->closed) { + if(stream->resp_hds_complete) { + /* Server decided to close the stream after having sent us a findl + * response. This is valid if it is not interested in the request + * body. This happens on 30x or 40x responses. + * We silently discard the data sent, since this is not a transport + * error situation. */ + CURL_TRC_CF(data, cf, "[%d] discarding data" + "on closed stream with response", stream->id); + *err = CURLE_OK; + nwritten = (ssize_t)len; + goto out; + } + infof(data, "stream %u closed", stream->id); + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + else { + /* If stream_id != -1, we have dispatched request HEADERS and + * optionally request body, and now are going to send or sending + * more request body in DATA frame */ + nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); + if(nwritten < 0 && *err != CURLE_AGAIN) + goto out; + } + + if(!Curl_bufq_is_empty(&stream->sendbuf)) { + /* req body data is buffered, resume the potentially suspended stream */ + rv = nghttp2_session_resume_data(ctx->h2, stream->id); + if(nghttp2_is_fatal(rv)) { + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + } + } + else { + nwritten = h2_submit(&stream, cf, data, buf, len, &hdslen, err); + if(nwritten < 0) { + goto out; + } + DEBUGASSERT(stream); + DEBUGASSERT(hdslen <= (size_t)nwritten); + } + + /* Call the nghttp2 send loop and flush to write ALL buffered data, + * headers and/or request body completely out to the network */ + result = h2_progress_egress(cf, data); + /* if the stream has been closed in egress handling (nghttp2 does that + * when it does not like the headers, for example */ + if(stream && stream->closed && !was_blocked) { + infof(data, "stream %u closed", stream->id); + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + else if(result == CURLE_AGAIN) { + blocked = 1; + } + else if(result) { + *err = result; + nwritten = -1; + goto out; + } + else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) { + /* although we wrote everything that nghttp2 wants to send now, + * there is data left in our stream send buffer unwritten. This may + * be due to the stream's HTTP/2 flow window being exhausted. */ + blocked = 1; + } + + if(stream && blocked && nwritten > 0) { + /* Unable to send all data, due to connection blocked or H2 window + * exhaustion. Data is left in our stream buffer, or nghttp2's internal + * frame buffer or our network out buffer. */ + size_t rwin = (size_t)nghttp2_session_get_stream_remote_window_size( + ctx->h2, stream->id); + /* At the start of a stream, we are called with request headers + * and, possibly, parts of the body. Later, only body data. + * If we cannot send pure body data, we EAGAIN. If there had been + * header, we return that *they* have been written and remember the + * block on the data length only. */ + stream->upload_blocked_len = ((size_t)nwritten) - hdslen; + CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu " + "hds_len=%zu blocked_len=%zu", + stream->id, len, + nghttp2_session_get_remote_window_size(ctx->h2), rwin, + hdslen, stream->upload_blocked_len); + if(hdslen) { + *err = CURLE_OK; + nwritten = hdslen; + } + else { + *err = CURLE_AGAIN; + nwritten = -1; + goto out; + } + } + else if(should_close_session(ctx)) { + /* nghttp2 thinks this session is done. If the stream has not been + * closed, this is an error state for out transfer */ + if(stream->closed) { + nwritten = http2_handle_stream_close(cf, data, stream, err); + } + else { + CURL_TRC_CF(data, cf, "send: nothing to do in this session"); + *err = CURLE_HTTP2; + nwritten = -1; + } + } + +out: + if(stream) { + CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, " + "upload_left=%" CURL_FORMAT_CURL_OFF_T ", " + "h2 windows %d-%d (stream-conn), " + "buffers %zu-%zu (stream-conn)", + stream->id, len, nwritten, *err, + stream->upload_left, + nghttp2_session_get_stream_remote_window_size( + ctx->h2, stream->id), + nghttp2_session_get_remote_window_size(ctx->h2), + Curl_bufq_len(&stream->sendbuf), + Curl_bufq_len(&ctx->outbufq)); + } + else { + CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, " + "connection-window=%d, nw_send_buffer(%zu)", + len, nwritten, *err, + nghttp2_session_get_remote_window_size(ctx->h2), + Curl_bufq_len(&ctx->outbufq)); + } + CF_DATA_RESTORE(cf, save); + return nwritten; +} + +static void cf_h2_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct cf_call_data save; + curl_socket_t sock; + bool want_recv, want_send; + + if(!ctx->h2) + return; + + sock = Curl_conn_cf_get_socket(cf, data); + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + if(want_recv || want_send) { + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + bool c_exhaust, s_exhaust; + + CF_DATA_SAVE(save, cf, data); + c_exhaust = want_send && !nghttp2_session_get_remote_window_size(ctx->h2); + s_exhaust = want_send && stream && stream->id >= 0 && + !nghttp2_session_get_stream_remote_window_size(ctx->h2, + stream->id); + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + (!c_exhaust && nghttp2_session_want_write(ctx->h2)); + + Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } + else if(ctx->sent_goaway && !cf->shutdown) { + /* shutdown in progress */ + CF_DATA_SAVE(save, cf, data); + want_send = nghttp2_session_want_write(ctx->h2); + want_recv = nghttp2_session_want_read(ctx->h2); + Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } +} + +static CURLcode cf_h2_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_h2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct cf_call_data save; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* Connect the lower filters first */ + if(!cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + } + + *done = FALSE; + + CF_DATA_SAVE(save, cf, data); + if(!ctx->h2) { + result = cf_h2_ctx_init(cf, data, FALSE); + if(result) + goto out; + } + + result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE); + if(result) + goto out; + + /* Send out our SETTINGS and ACKs and such. If that blocks, we + * have it buffered and can count this filter as being connected */ + result = h2_progress_egress(cf, data); + if(result == CURLE_AGAIN) + result = CURLE_OK; + else if(result) + goto out; + + *done = TRUE; + cf->connected = TRUE; + result = CURLE_OK; + +out: + CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done); + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + + if(ctx) { + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + cf_h2_ctx_clear(ctx); + CF_DATA_RESTORE(cf, save); + cf->connected = FALSE; + } + if(cf->next) + cf->next->cft->do_close(cf->next, data); +} + +static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + + (void)data; + if(ctx) { + cf_h2_ctx_free(ctx); + cf->ctx = NULL; + } +} + +static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct cf_call_data save; + CURLcode result; + int rv; + + if(!cf->connected || !ctx->h2 || cf->shutdown || ctx->conn_closed) { + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + + if(!ctx->sent_goaway) { + rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE, + ctx->local_max_sid, 0, + (const uint8_t *)"shutown", sizeof("shutown")); + if(rv) { + failf(data, "nghttp2_submit_goaway() failed: %s(%d)", + nghttp2_strerror(rv), rv); + result = CURLE_SEND_ERROR; + goto out; + } + ctx->sent_goaway = TRUE; + } + /* GOAWAY submitted, process egress and ingress until nghttp2 is done. */ + result = CURLE_OK; + if(nghttp2_session_want_write(ctx->h2)) + result = h2_progress_egress(cf, data); + if(!result && nghttp2_session_want_read(ctx->h2)) + result = h2_progress_ingress(cf, data, 0); + + *done = (ctx->conn_closed || + (!result && !nghttp2_session_want_write(ctx->h2) && + !nghttp2_session_want_read(ctx->h2))); + +out: + CF_DATA_RESTORE(cf, save); + cf->shutdown = (result || *done); + return result; +} + +static CURLcode http2_data_pause(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool pause) +{ +#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + + DEBUGASSERT(data); + if(ctx && ctx->h2 && stream) { + uint32_t window = pause? 0 : stream->local_window_size; + + int rv = (int)nghttp2_session_set_local_window_size(ctx->h2, + NGHTTP2_FLAG_NONE, + stream->id, + (int32_t)window); + if(rv) { + failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", + nghttp2_strerror(rv), rv); + return CURLE_HTTP2; + } + + if(!pause) + drain_stream(cf, data, stream); + + /* attempt to send the window update */ + (void)h2_progress_egress(cf, data); + + if(!pause) { + /* Unpausing a h2 transfer, requires it to be run again. The server + * may send new DATA on us increasing the flow window, and it may + * not. We may have already buffered and exhausted the new window + * by operating on things in flight during the handling of other + * transfers. */ + drain_stream(cf, data, stream); + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u", + window, stream->id)); + +#ifdef DEBUGBUILD + { + /* read out the stream local window again */ + uint32_t window2 = (uint32_t) + nghttp2_session_get_stream_local_window_size(ctx->h2, + stream->id); + DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u", + window2, stream->id)); + } +#endif + } +#endif + return CURLE_OK; +} + +static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + CURLcode result = CURLE_OK; + struct cf_call_data save; + + (void)arg2; + + CF_DATA_SAVE(save, cf, data); + switch(event) { + case CF_CTRL_DATA_SETUP: + break; + case CF_CTRL_DATA_PAUSE: + result = http2_data_pause(cf, data, (arg1 != 0)); + break; + case CF_CTRL_DATA_DONE_SEND: + result = http2_data_done_send(cf, data); + break; + case CF_CTRL_DATA_DETACH: + http2_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE: + http2_data_done(cf, data); + break; + default: + break; + } + CF_DATA_RESTORE(cf, save); + return result; +} + +static bool cf_h2_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + + if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq) + || (stream && !Curl_bufq_is_empty(&stream->sendbuf)))) + return TRUE; + return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +static bool cf_h2_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_h2_ctx *ctx = cf->ctx; + CURLcode result; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending)); + CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d", + result, *input_pending); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode result; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + result = http2_send_ping(cf, data); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode cf_h2_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct cf_call_data save; + size_t effective_max; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: + DEBUGASSERT(pres1); + + CF_DATA_SAVE(save, cf, data); + if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { + /* the limit is what we have in use right now */ + effective_max = CONN_INUSE(cf->conn); + } + else { + effective_max = ctx->max_concurrent_streams; + } + *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max; + CF_DATA_RESTORE(cf, save); + return CURLE_OK; + case CF_QUERY_STREAM_ERROR: { + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + *pres1 = stream? (int)stream->error : 0; + return CURLE_OK; + } + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +struct Curl_cftype Curl_cft_nghttp2 = { + "HTTP/2", + CF_TYPE_MULTIPLEX, + CURL_LOG_LVL_NONE, + cf_h2_destroy, + cf_h2_connect, + cf_h2_close, + cf_h2_shutdown, + Curl_cf_def_get_host, + cf_h2_adjust_pollset, + cf_h2_data_pending, + cf_h2_send, + cf_h2_recv, + cf_h2_cntrl, + cf_h2_is_alive, + cf_h2_keep_alive, + cf_h2_query, +}; + +static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + bool via_h1_upgrade) +{ + struct Curl_cfilter *cf = NULL; + struct cf_h2_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + + DEBUGASSERT(data->conn); + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) + goto out; + + result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx); + if(result) + goto out; + + ctx = NULL; + Curl_conn_cf_add(data, conn, sockindex, cf); + result = cf_h2_ctx_init(cf, data, via_h1_upgrade); + +out: + if(result) + cf_h2_ctx_free(ctx); + *pcf = result? NULL : cf; + return result; +} + +static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool via_h1_upgrade) +{ + struct Curl_cfilter *cf_h2 = NULL; + struct cf_h2_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + + (void)data; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) + goto out; + + result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx); + if(result) + goto out; + + ctx = NULL; + Curl_conn_cf_insert_after(cf, cf_h2); + result = cf_h2_ctx_init(cf_h2, data, via_h1_upgrade); + +out: + if(result) + cf_h2_ctx_free(ctx); + return result; +} + +static bool Curl_cf_is_http2(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_nghttp2) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + } + return FALSE; +} + +bool Curl_conn_is_http2(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ + return conn? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE; +} + +bool Curl_http2_may_switch(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + (void)sockindex; + if(!Curl_conn_is_http2(data, conn, sockindex) && + data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + /* We do not support HTTP/2 proxies yet. Also it is debatable + whether or not this setting should apply to HTTP/2 proxies. */ + infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); + return FALSE; + } +#endif + return TRUE; + } + return FALSE; +} + +CURLcode Curl_http2_switch(struct Curl_easy *data, + struct connectdata *conn, int sockindex) +{ + struct Curl_cfilter *cf; + CURLcode result; + + DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); + DEBUGF(infof(data, "switching to HTTP/2")); + + result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE); + if(result) + return result; + + conn->httpversion = 20; /* we know we are on HTTP/2 now */ + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + Curl_multi_connchanged(data->multi); + + if(cf->next) { + bool done; + return Curl_conn_cf_connect(cf, data, FALSE, &done); + } + return CURLE_OK; +} + +CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct Curl_cfilter *cf_h2; + CURLcode result; + + DEBUGASSERT(!Curl_cf_is_http2(cf, data)); + + result = http2_cfilter_insert_after(cf, data, FALSE); + if(result) + return result; + + cf_h2 = cf->next; + cf->conn->httpversion = 20; /* we know we are on HTTP/2 now */ + cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; + Curl_multi_connchanged(data->multi); + + if(cf_h2->next) { + bool done; + return Curl_conn_cf_connect(cf_h2, data, FALSE, &done); + } + return CURLE_OK; +} + +CURLcode Curl_http2_upgrade(struct Curl_easy *data, + struct connectdata *conn, int sockindex, + const char *mem, size_t nread) +{ + struct Curl_cfilter *cf; + struct cf_h2_ctx *ctx; + CURLcode result; + + DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); + DEBUGF(infof(data, "upgrading to HTTP/2")); + DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED); + + result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE); + if(result) + return result; + + DEBUGASSERT(cf->cft == &Curl_cft_nghttp2); + ctx = cf->ctx; + + if(nread > 0) { + /* Remaining data from the protocol switch reply is already using + * the switched protocol, ie. HTTP/2. We add that to the network + * inbufq. */ + ssize_t copied; + + copied = Curl_bufq_write(&ctx->inbufq, + (const unsigned char *)mem, nread, &result); + if(copied < 0) { + failf(data, "error on copying HTTP Upgrade response: %d", result); + return CURLE_RECV_ERROR; + } + if((size_t)copied < nread) { + failf(data, "connection buffer size could not take all data " + "from HTTP Upgrade response header: copied=%zd, datalen=%zu", + copied, nread); + return CURLE_HTTP2; + } + infof(data, "Copied HTTP/2 data in stream buffer to connection buffer" + " after upgrade: len=%zu", nread); + } + + conn->httpversion = 20; /* we know we are on HTTP/2 now */ + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + Curl_multi_connchanged(data->multi); + + if(cf->next) { + bool done; + return Curl_conn_cf_connect(cf, data, FALSE, &done); + } + return CURLE_OK; +} + +/* Only call this function for a transfer that already got an HTTP/2 + CURLE_HTTP2_STREAM error! */ +bool Curl_h2_http_1_1_error(struct Curl_easy *data) +{ + if(Curl_conn_is_http2(data, data->conn, FIRSTSOCKET)) { + int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET); + return (err == NGHTTP2_HTTP_1_1_REQUIRED); + } + return FALSE; +} + +#else /* !USE_NGHTTP2 */ + +/* Satisfy external references even if http2 is not compiled in. */ +#include + +char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) +{ + (void) h; + (void) num; + return NULL; +} + +char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) +{ + (void) h; + (void) header; + return NULL; +} + +#endif /* USE_NGHTTP2 */ diff --git a/third_party/curl/lib/http2.h b/third_party/curl/lib/http2.h new file mode 100644 index 0000000000..80e183480a --- /dev/null +++ b/third_party/curl/lib/http2.h @@ -0,0 +1,77 @@ +#ifndef HEADER_CURL_HTTP2_H +#define HEADER_CURL_HTTP2_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_NGHTTP2 +#include "http.h" + +/* value for MAX_CONCURRENT_STREAMS we use until we get an updated setting + from the peer */ +#define DEFAULT_MAX_CONCURRENT_STREAMS 100 + +/* + * Store nghttp2 version info in this buffer. + */ +void Curl_http2_ver(char *p, size_t len); + +CURLcode Curl_http2_request_upgrade(struct dynbuf *req, + struct Curl_easy *data); + +/* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */ +bool Curl_h2_http_1_1_error(struct Curl_easy *data); + +bool Curl_conn_is_http2(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); +bool Curl_http2_may_switch(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + +CURLcode Curl_http2_switch(struct Curl_easy *data, + struct connectdata *conn, int sockindex); + +CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data); + +CURLcode Curl_http2_upgrade(struct Curl_easy *data, + struct connectdata *conn, int sockindex, + const char *ptr, size_t nread); + +extern struct Curl_cftype Curl_cft_nghttp2; + +#else /* USE_NGHTTP2 */ + +#define Curl_cf_is_http2(a,b) FALSE +#define Curl_conn_is_http2(a,b,c) FALSE +#define Curl_http2_may_switch(a,b,c) FALSE + +#define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_switch(a,b,c) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_upgrade(a,b,c,d,e) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_h2_http_1_1_error(x) 0 +#endif + +#endif /* HEADER_CURL_HTTP2_H */ diff --git a/third_party/curl/lib/http_aws_sigv4.c b/third_party/curl/lib/http_aws_sigv4.c new file mode 100644 index 0000000000..9e4f720165 --- /dev/null +++ b/third_party/curl/lib/http_aws_sigv4.c @@ -0,0 +1,849 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) + +#include "urldata.h" +#include "strcase.h" +#include "strdup.h" +#include "http_aws_sigv4.h" +#include "curl_sha256.h" +#include "transfer.h" +#include "parsedate.h" +#include "sendf.h" +#include "escape.h" + +#include + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#include "slist.h" + +#define HMAC_SHA256(k, kl, d, dl, o) \ + do { \ + result = Curl_hmacit(Curl_HMAC_SHA256, \ + (unsigned char *)k, \ + kl, \ + (unsigned char *)d, \ + dl, o); \ + if(result) { \ + goto fail; \ + } \ + } while(0) + +#define TIMESTAMP_SIZE 17 + +/* hex-encoded with trailing null */ +#define SHA256_HEX_LENGTH (2 * SHA256_DIGEST_LENGTH + 1) + +static void sha256_to_hex(char *dst, unsigned char *sha) +{ + Curl_hexencode(sha, SHA256_DIGEST_LENGTH, + (unsigned char *)dst, SHA256_HEX_LENGTH); +} + +static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr) +{ + char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr)); + + if(tmp) + return tmp; + return Curl_checkheaders(data, STRCONST("Date")); +} + +/* remove whitespace, and lowercase all headers */ +static void trim_headers(struct curl_slist *head) +{ + struct curl_slist *l; + for(l = head; l; l = l->next) { + char *value; /* to read from */ + char *store; + size_t colon = strcspn(l->data, ":"); + Curl_strntolower(l->data, l->data, colon); + + value = &l->data[colon]; + if(!*value) + continue; + ++value; + store = value; + + /* skip leading whitespace */ + while(*value && ISBLANK(*value)) + value++; + + while(*value) { + int space = 0; + while(*value && ISBLANK(*value)) { + value++; + space++; + } + if(space) { + /* replace any number of consecutive whitespace with a single space, + unless at the end of the string, then nothing */ + if(*value) + *store++ = ' '; + } + else + *store++ = *value++; + } + *store = 0; /* null terminate */ + } +} + +/* maximum length for the aws sivg4 parts */ +#define MAX_SIGV4_LEN 64 +#define MAX_SIGV4_LEN_TXT "64" + +#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date")) + +#define MAX_HOST_LEN 255 +/* FQDN + host: */ +#define FULL_HOST_LEN (MAX_HOST_LEN + sizeof("host:")) + +/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */ +#define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1) + +/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */ +static CURLcode make_headers(struct Curl_easy *data, + const char *hostname, + char *timestamp, + char *provider1, + char **date_header, + char *content_sha256_header, + struct dynbuf *canonical_headers, + struct dynbuf *signed_headers) +{ + char date_hdr_key[DATE_HDR_KEY_LEN]; + char date_full_hdr[DATE_FULL_HDR_LEN]; + struct curl_slist *head = NULL; + struct curl_slist *tmp_head = NULL; + CURLcode ret = CURLE_OUT_OF_MEMORY; + struct curl_slist *l; + int again = 1; + + /* provider1 mid */ + Curl_strntolower(provider1, provider1, strlen(provider1)); + provider1[0] = Curl_raw_toupper(provider1[0]); + + msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1); + + /* provider1 lowercase */ + Curl_strntolower(provider1, provider1, 1); /* first byte only */ + msnprintf(date_full_hdr, DATE_FULL_HDR_LEN, + "x-%s-date:%s", provider1, timestamp); + + if(!Curl_checkheaders(data, STRCONST("Host"))) { + char full_host[FULL_HOST_LEN + 1]; + + if(data->state.aptr.host) { + size_t pos; + + if(strlen(data->state.aptr.host) > FULL_HOST_LEN) { + ret = CURLE_URL_MALFORMAT; + goto fail; + } + strcpy(full_host, data->state.aptr.host); + /* remove /r/n as the separator for canonical request must be '\n' */ + pos = strcspn(full_host, "\n\r"); + full_host[pos] = 0; + } + else { + if(strlen(hostname) > MAX_HOST_LEN) { + ret = CURLE_URL_MALFORMAT; + goto fail; + } + msnprintf(full_host, FULL_HOST_LEN, "host:%s", hostname); + } + + head = curl_slist_append(NULL, full_host); + if(!head) + goto fail; + } + + + if(*content_sha256_header) { + tmp_head = curl_slist_append(head, content_sha256_header); + if(!tmp_head) + goto fail; + head = tmp_head; + } + + /* copy user headers to our header list. the logic is based on how http.c + handles user headers. + + user headers in format 'name:' with no value are used to signal that an + internal header of that name should be removed. those user headers are not + added to this list. + + user headers in format 'name;' with no value are used to signal that a + header of that name with no value should be sent. those user headers are + added to this list but in the format that they will be sent, ie the + semi-colon is changed to a colon for format 'name:'. + + user headers with a value of whitespace only, or without a colon or + semi-colon, are not added to this list. + */ + for(l = data->set.headers; l; l = l->next) { + char *dupdata, *ptr; + char *sep = strchr(l->data, ':'); + if(!sep) + sep = strchr(l->data, ';'); + if(!sep || (*sep == ':' && !*(sep + 1))) + continue; + for(ptr = sep + 1; ISSPACE(*ptr); ++ptr) + ; + if(!*ptr && ptr != sep + 1) /* a value of whitespace only */ + continue; + dupdata = strdup(l->data); + if(!dupdata) + goto fail; + dupdata[sep - l->data] = ':'; + tmp_head = Curl_slist_append_nodup(head, dupdata); + if(!tmp_head) { + free(dupdata); + goto fail; + } + head = tmp_head; + } + + trim_headers(head); + + *date_header = find_date_hdr(data, date_hdr_key); + if(!*date_header) { + tmp_head = curl_slist_append(head, date_full_hdr); + if(!tmp_head) + goto fail; + head = tmp_head; + *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp); + } + else { + char *value; + char *endp; + value = strchr(*date_header, ':'); + if(!value) { + *date_header = NULL; + goto fail; + } + ++value; + while(ISBLANK(*value)) + ++value; + endp = value; + while(*endp && ISALNUM(*endp)) + ++endp; + /* 16 bytes => "19700101T000000Z" */ + if((endp - value) == TIMESTAMP_SIZE - 1) { + memcpy(timestamp, value, TIMESTAMP_SIZE - 1); + timestamp[TIMESTAMP_SIZE - 1] = 0; + } + else + /* bad timestamp length */ + timestamp[0] = 0; + *date_header = NULL; + } + + /* alpha-sort in a case sensitive manner */ + do { + again = 0; + for(l = head; l; l = l->next) { + struct curl_slist *next = l->next; + + if(next && strcmp(l->data, next->data) > 0) { + char *tmp = l->data; + + l->data = next->data; + next->data = tmp; + again = 1; + } + } + } while(again); + + for(l = head; l; l = l->next) { + char *tmp; + + if(Curl_dyn_add(canonical_headers, l->data)) + goto fail; + if(Curl_dyn_add(canonical_headers, "\n")) + goto fail; + + tmp = strchr(l->data, ':'); + if(tmp) + *tmp = 0; + + if(l != head) { + if(Curl_dyn_add(signed_headers, ";")) + goto fail; + } + if(Curl_dyn_add(signed_headers, l->data)) + goto fail; + } + + ret = CURLE_OK; +fail: + curl_slist_free_all(head); + + return ret; +} + +#define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256")) +/* add 2 for ": " between header name and value */ +#define CONTENT_SHA256_HDR_LEN (CONTENT_SHA256_KEY_LEN + 2 + \ + SHA256_HEX_LENGTH) + +/* try to parse a payload hash from the content-sha256 header */ +static char *parse_content_sha_hdr(struct Curl_easy *data, + const char *provider1, + size_t *value_len) +{ + char key[CONTENT_SHA256_KEY_LEN]; + size_t key_len; + char *value; + size_t len; + + key_len = msnprintf(key, sizeof(key), "x-%s-content-sha256", provider1); + + value = Curl_checkheaders(data, key, key_len); + if(!value) + return NULL; + + value = strchr(value, ':'); + if(!value) + return NULL; + ++value; + + while(*value && ISBLANK(*value)) + ++value; + + len = strlen(value); + while(len > 0 && ISBLANK(value[len-1])) + --len; + + *value_len = len; + return value; +} + +static CURLcode calc_payload_hash(struct Curl_easy *data, + unsigned char *sha_hash, char *sha_hex) +{ + const char *post_data = data->set.postfields; + size_t post_data_len = 0; + CURLcode result; + + if(post_data) { + if(data->set.postfieldsize < 0) + post_data_len = strlen(post_data); + else + post_data_len = (size_t)data->set.postfieldsize; + } + result = Curl_sha256it(sha_hash, (const unsigned char *) post_data, + post_data_len); + if(!result) + sha256_to_hex(sha_hex, sha_hash); + return result; +} + +#define S3_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD" + +static CURLcode calc_s3_payload_hash(struct Curl_easy *data, + Curl_HttpReq httpreq, char *provider1, + unsigned char *sha_hash, + char *sha_hex, char *header) +{ + bool empty_method = (httpreq == HTTPREQ_GET || httpreq == HTTPREQ_HEAD); + /* The request method or filesize indicate no request payload */ + bool empty_payload = (empty_method || data->set.filesize == 0); + /* The POST payload is in memory */ + bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields); + CURLcode ret = CURLE_OUT_OF_MEMORY; + + if(empty_payload || post_payload) { + /* Calculate a real hash when we know the request payload */ + ret = calc_payload_hash(data, sha_hash, sha_hex); + if(ret) + goto fail; + } + else { + /* Fall back to s3's UNSIGNED-PAYLOAD */ + size_t len = sizeof(S3_UNSIGNED_PAYLOAD) - 1; + DEBUGASSERT(len < SHA256_HEX_LENGTH); /* 16 < 65 */ + memcpy(sha_hex, S3_UNSIGNED_PAYLOAD, len); + sha_hex[len] = 0; + } + + /* format the required content-sha256 header */ + msnprintf(header, CONTENT_SHA256_HDR_LEN, + "x-%s-content-sha256: %s", provider1, sha_hex); + + ret = CURLE_OK; +fail: + return ret; +} + +struct pair { + const char *p; + size_t len; +}; + +static int compare_func(const void *a, const void *b) +{ + const struct pair *aa = a; + const struct pair *bb = b; + /* If one element is empty, the other is always sorted higher */ + if(aa->len == 0) + return -1; + if(bb->len == 0) + return 1; + return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len); +} + +#define MAX_QUERYPAIRS 64 + +/** + * found_equals have a double meaning, + * detect if an equal have been found when called from canon_query, + * and mark that this function is called to compute the path, + * if found_equals is NULL. + */ +static CURLcode canon_string(const char *q, size_t len, + struct dynbuf *dq, bool *found_equals) +{ + CURLcode result = CURLE_OK; + + for(; len && !result; q++, len--) { + if(ISALNUM(*q)) + result = Curl_dyn_addn(dq, q, 1); + else { + switch(*q) { + case '-': + case '.': + case '_': + case '~': + /* allowed as-is */ + result = Curl_dyn_addn(dq, q, 1); + break; + case '%': + /* uppercase the following if hexadecimal */ + if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) { + char tmp[3]="%"; + tmp[1] = Curl_raw_toupper(q[1]); + tmp[2] = Curl_raw_toupper(q[2]); + result = Curl_dyn_addn(dq, tmp, 3); + q += 2; + len -= 2; + } + else + /* '%' without a following two-digit hex, encode it */ + result = Curl_dyn_addn(dq, "%25", 3); + break; + default: { + const char hex[] = "0123456789ABCDEF"; + char out[3]={'%'}; + + if(!found_equals) { + /* if found_equals is NULL assuming, been in path */ + if(*q == '/') { + /* allowed as if */ + result = Curl_dyn_addn(dq, q, 1); + break; + } + } + else { + /* allowed as-is */ + if(*q == '=') { + result = Curl_dyn_addn(dq, q, 1); + *found_equals = true; + break; + } + } + /* URL encode */ + out[1] = hex[((unsigned char)*q)>>4]; + out[2] = hex[*q & 0xf]; + result = Curl_dyn_addn(dq, out, 3); + break; + } + } + } + } + return result; +} + + +static CURLcode canon_query(struct Curl_easy *data, + const char *query, struct dynbuf *dq) +{ + CURLcode result = CURLE_OK; + int entry = 0; + int i; + const char *p = query; + struct pair array[MAX_QUERYPAIRS]; + struct pair *ap = &array[0]; + if(!query) + return result; + + /* sort the name=value pairs first */ + do { + char *amp; + entry++; + ap->p = p; + amp = strchr(p, '&'); + if(amp) + ap->len = amp - p; /* excluding the ampersand */ + else { + ap->len = strlen(p); + break; + } + ap++; + p = amp + 1; + } while(entry < MAX_QUERYPAIRS); + if(entry == MAX_QUERYPAIRS) { + /* too many query pairs for us */ + failf(data, "aws-sigv4: too many query pairs in URL"); + return CURLE_URL_MALFORMAT; + } + + qsort(&array[0], entry, sizeof(struct pair), compare_func); + + ap = &array[0]; + for(i = 0; !result && (i < entry); i++, ap++) { + const char *q = ap->p; + bool found_equals = false; + if(!ap->len) + continue; + result = canon_string(q, ap->len, dq, &found_equals); + if(!result && !found_equals) { + /* queries without value still need an equals */ + result = Curl_dyn_addn(dq, "=", 1); + } + if(!result && i < entry - 1) { + /* insert ampersands between query pairs */ + result = Curl_dyn_addn(dq, "&", 1); + } + } + return result; +} + + +CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + struct connectdata *conn = data->conn; + size_t len; + const char *arg; + char provider0[MAX_SIGV4_LEN + 1]=""; + char provider1[MAX_SIGV4_LEN + 1]=""; + char region[MAX_SIGV4_LEN + 1]=""; + char service[MAX_SIGV4_LEN + 1]=""; + bool sign_as_s3 = false; + const char *hostname = conn->host.name; + time_t clock; + struct tm tm; + char timestamp[TIMESTAMP_SIZE]; + char date[9]; + struct dynbuf canonical_headers; + struct dynbuf signed_headers; + struct dynbuf canonical_query; + struct dynbuf canonical_path; + char *date_header = NULL; + Curl_HttpReq httpreq; + const char *method = NULL; + char *payload_hash = NULL; + size_t payload_hash_len = 0; + unsigned char sha_hash[SHA256_DIGEST_LENGTH]; + char sha_hex[SHA256_HEX_LENGTH]; + char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */ + char *canonical_request = NULL; + char *request_type = NULL; + char *credential_scope = NULL; + char *str_to_sign = NULL; + const char *user = data->state.aptr.user ? data->state.aptr.user : ""; + char *secret = NULL; + unsigned char sign0[SHA256_DIGEST_LENGTH] = {0}; + unsigned char sign1[SHA256_DIGEST_LENGTH] = {0}; + char *auth_headers = NULL; + + DEBUGASSERT(!proxy); + (void)proxy; + + if(Curl_checkheaders(data, STRCONST("Authorization"))) { + /* Authorization already present, Bailing out */ + return CURLE_OK; + } + + /* we init those buffers here, so goto fail will free initialized dynbuf */ + Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); + Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); + Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); + Curl_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER); + + /* + * Parameters parsing + * Google and Outscale use the same OSC or GOOG, + * but Amazon uses AWS and AMZ for header arguments. + * AWS is the default because most of non-amazon providers + * are still using aws:amz as a prefix. + */ + arg = data->set.str[STRING_AWS_SIGV4] ? + data->set.str[STRING_AWS_SIGV4] : "aws:amz"; + + /* provider1[:provider2[:region[:service]]] + + No string can be longer than N bytes of non-whitespace + */ + (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]" + ":%" MAX_SIGV4_LEN_TXT "[^:]" + ":%" MAX_SIGV4_LEN_TXT "[^:]" + ":%" MAX_SIGV4_LEN_TXT "s", + provider0, provider1, region, service); + if(!provider0[0]) { + failf(data, "first aws-sigv4 provider cannot be empty"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto fail; + } + else if(!provider1[0]) + strcpy(provider1, provider0); + + if(!service[0]) { + char *hostdot = strchr(hostname, '.'); + if(!hostdot) { + failf(data, "aws-sigv4: service missing in parameters and hostname"); + result = CURLE_URL_MALFORMAT; + goto fail; + } + len = hostdot - hostname; + if(len > MAX_SIGV4_LEN) { + failf(data, "aws-sigv4: service too long in hostname"); + result = CURLE_URL_MALFORMAT; + goto fail; + } + memcpy(service, hostname, len); + service[len] = '\0'; + + infof(data, "aws_sigv4: picked service %s from host", service); + + if(!region[0]) { + const char *reg = hostdot + 1; + const char *hostreg = strchr(reg, '.'); + if(!hostreg) { + failf(data, "aws-sigv4: region missing in parameters and hostname"); + result = CURLE_URL_MALFORMAT; + goto fail; + } + len = hostreg - reg; + if(len > MAX_SIGV4_LEN) { + failf(data, "aws-sigv4: region too long in hostname"); + result = CURLE_URL_MALFORMAT; + goto fail; + } + memcpy(region, reg, len); + region[len] = '\0'; + infof(data, "aws_sigv4: picked region %s from host", region); + } + } + + Curl_http_method(data, conn, &method, &httpreq); + + /* AWS S3 requires a x-amz-content-sha256 header, and supports special + * values like UNSIGNED-PAYLOAD */ + sign_as_s3 = (strcasecompare(provider0, "aws") && + strcasecompare(service, "s3")); + + payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len); + + if(!payload_hash) { + if(sign_as_s3) + result = calc_s3_payload_hash(data, httpreq, provider1, sha_hash, + sha_hex, content_sha256_hdr); + else + result = calc_payload_hash(data, sha_hash, sha_hex); + if(result) + goto fail; + + payload_hash = sha_hex; + /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */ + payload_hash_len = strlen(sha_hex); + } + +#ifdef DEBUGBUILD + { + char *force_timestamp = getenv("CURL_FORCETIME"); + if(force_timestamp) + clock = 0; + else + clock = time(NULL); + } +#else + clock = time(NULL); +#endif + result = Curl_gmtime(clock, &tm); + if(result) { + goto fail; + } + if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + result = make_headers(data, hostname, timestamp, provider1, + &date_header, content_sha256_hdr, + &canonical_headers, &signed_headers); + if(result) + goto fail; + + if(*content_sha256_hdr) { + /* make_headers() needed this without the \r\n for canonicalization */ + size_t hdrlen = strlen(content_sha256_hdr); + DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr)); + memcpy(content_sha256_hdr + hdrlen, "\r\n", 3); + } + + memcpy(date, timestamp, sizeof(date)); + date[sizeof(date) - 1] = 0; + + result = canon_query(data, data->state.up.query, &canonical_query); + if(result) + goto fail; + + result = canon_string(data->state.up.path, strlen(data->state.up.path), + &canonical_path, NULL); + if(result) + goto fail; + result = CURLE_OUT_OF_MEMORY; + + canonical_request = + curl_maprintf("%s\n" /* HTTPRequestMethod */ + "%s\n" /* CanonicalURI */ + "%s\n" /* CanonicalQueryString */ + "%s\n" /* CanonicalHeaders */ + "%s\n" /* SignedHeaders */ + "%.*s", /* HashedRequestPayload in hex */ + method, + Curl_dyn_ptr(&canonical_path), + Curl_dyn_ptr(&canonical_query) ? + Curl_dyn_ptr(&canonical_query) : "", + Curl_dyn_ptr(&canonical_headers), + Curl_dyn_ptr(&signed_headers), + (int)payload_hash_len, payload_hash); + if(!canonical_request) + goto fail; + + DEBUGF(infof(data, "Canonical request: %s", canonical_request)); + + /* provider 0 lowercase */ + Curl_strntolower(provider0, provider0, strlen(provider0)); + request_type = curl_maprintf("%s4_request", provider0); + if(!request_type) + goto fail; + + credential_scope = curl_maprintf("%s/%s/%s/%s", + date, region, service, request_type); + if(!credential_scope) + goto fail; + + if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request, + strlen(canonical_request))) + goto fail; + + sha256_to_hex(sha_hex, sha_hash); + + /* provider 0 uppercase */ + Curl_strntoupper(provider0, provider0, strlen(provider0)); + + /* + * Google allows using RSA key instead of HMAC, so this code might change + * in the future. For now we only support HMAC. + */ + str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */ + "%s\n" /* RequestDateTime */ + "%s\n" /* CredentialScope */ + "%s", /* HashedCanonicalRequest in hex */ + provider0, + timestamp, + credential_scope, + sha_hex); + if(!str_to_sign) { + goto fail; + } + + /* provider 0 uppercase */ + secret = curl_maprintf("%s4%s", provider0, + data->state.aptr.passwd ? + data->state.aptr.passwd : ""); + if(!secret) + goto fail; + + HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0); + HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1); + HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), sign0); + HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1); + HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0); + + sha256_to_hex(sha_hex, sign0); + + /* provider 0 uppercase */ + auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 " + "Credential=%s/%s, " + "SignedHeaders=%s, " + "Signature=%s\r\n" + /* + * date_header is added here, only if it was not + * user-specified (using CURLOPT_HTTPHEADER). + * date_header includes \r\n + */ + "%s" + "%s", /* optional sha256 header includes \r\n */ + provider0, + user, + credential_scope, + Curl_dyn_ptr(&signed_headers), + sha_hex, + date_header ? date_header : "", + content_sha256_hdr); + if(!auth_headers) { + goto fail; + } + + Curl_safefree(data->state.aptr.userpwd); + data->state.aptr.userpwd = auth_headers; + data->state.authhost.done = TRUE; + result = CURLE_OK; + +fail: + Curl_dyn_free(&canonical_query); + Curl_dyn_free(&canonical_path); + Curl_dyn_free(&canonical_headers); + Curl_dyn_free(&signed_headers); + free(canonical_request); + free(request_type); + free(credential_scope); + free(str_to_sign); + free(secret); + free(date_header); + return result; +} + +#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */ diff --git a/third_party/curl/lib/http_aws_sigv4.h b/third_party/curl/lib/http_aws_sigv4.h new file mode 100644 index 0000000000..57cc5706eb --- /dev/null +++ b/third_party/curl/lib/http_aws_sigv4.h @@ -0,0 +1,31 @@ +#ifndef HEADER_CURL_HTTP_AWS_SIGV4_H +#define HEADER_CURL_HTTP_AWS_SIGV4_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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" + +/* this is for creating aws_sigv4 header output */ +CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy); + +#endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */ diff --git a/third_party/curl/lib/http_chunks.c b/third_party/curl/lib/http_chunks.c new file mode 100644 index 0000000000..001185da9a --- /dev/null +++ b/third_party/curl/lib/http_chunks.c @@ -0,0 +1,682 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_HTTP + +#include "urldata.h" /* it includes http_chunks.h */ +#include "curl_printf.h" +#include "curl_trc.h" +#include "sendf.h" /* for the client write stuff */ +#include "dynbuf.h" +#include "content_encoding.h" +#include "http.h" +#include "multiif.h" +#include "strtoofft.h" +#include "warnless.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Chunk format (simplified): + * + * [ chunk extension ] CRLF + * CRLF + * + * Highlights from RFC2616 section 3.6 say: + + The chunked encoding modifies the body of a message in order to + transfer it as a series of chunks, each with its own size indicator, + followed by an OPTIONAL trailer containing entity-header fields. This + allows dynamically produced content to be transferred along with the + information necessary for the recipient to verify that it has + received the full message. + + Chunked-Body = *chunk + last-chunk + trailer + CRLF + + chunk = chunk-size [ chunk-extension ] CRLF + chunk-data CRLF + chunk-size = 1*HEX + last-chunk = 1*("0") [ chunk-extension ] CRLF + + chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) + chunk-ext-name = token + chunk-ext-val = token | quoted-string + chunk-data = chunk-size(OCTET) + trailer = *(entity-header CRLF) + + The chunk-size field is a string of hex digits indicating the size of + the chunk. The chunked encoding is ended by any chunk whose size is + zero, followed by the trailer, which is terminated by an empty line. + + */ + +void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch, + bool ignore_body) +{ + (void)data; + ch->hexindex = 0; /* start at 0 */ + ch->state = CHUNK_HEX; /* we get hex first! */ + ch->last_code = CHUNKE_OK; + Curl_dyn_init(&ch->trailer, DYN_H1_TRAILER); + ch->ignore_body = ignore_body; +} + +void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch, + bool ignore_body) +{ + (void)data; + ch->hexindex = 0; /* start at 0 */ + ch->state = CHUNK_HEX; /* we get hex first! */ + ch->last_code = CHUNKE_OK; + Curl_dyn_reset(&ch->trailer); + ch->ignore_body = ignore_body; +} + +void Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch) +{ + (void)data; + Curl_dyn_free(&ch->trailer); +} + +bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch) +{ + (void)data; + return ch->state == CHUNK_DONE; +} + +static CURLcode httpchunk_readwrite(struct Curl_easy *data, + struct Curl_chunker *ch, + struct Curl_cwriter *cw_next, + const char *buf, size_t blen, + size_t *pconsumed) +{ + CURLcode result = CURLE_OK; + size_t piece; + + *pconsumed = 0; /* nothing's written yet */ + /* first check terminal states that will not progress anywhere */ + if(ch->state == CHUNK_DONE) + return CURLE_OK; + if(ch->state == CHUNK_FAILED) + return CURLE_RECV_ERROR; + + /* the original data is written to the client, but we go on with the + chunk read process, to properly calculate the content length */ + if(data->set.http_te_skip && !ch->ignore_body) { + if(cw_next) + result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY, buf, blen); + else + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen); + if(result) { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_PASSTHRU_ERROR; + return result; + } + } + + while(blen) { + switch(ch->state) { + case CHUNK_HEX: + if(ISXDIGIT(*buf)) { + if(ch->hexindex >= CHUNK_MAXNUM_LEN) { + failf(data, "chunk hex-length longer than %d", CHUNK_MAXNUM_LEN); + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_TOO_LONG_HEX; /* longer than we support */ + return CURLE_RECV_ERROR; + } + ch->hexbuffer[ch->hexindex++] = *buf; + buf++; + blen--; + (*pconsumed)++; + } + else { + if(0 == ch->hexindex) { + /* This is illegal data, we received junk where we expected + a hexadecimal digit. */ + failf(data, "chunk hex-length char not a hex digit: 0x%x", *buf); + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_ILLEGAL_HEX; + return CURLE_RECV_ERROR; + } + + /* blen and buf are unmodified */ + ch->hexbuffer[ch->hexindex] = 0; + if(curlx_strtoofft(ch->hexbuffer, NULL, 16, &ch->datasize)) { + failf(data, "chunk hex-length not valid: '%s'", ch->hexbuffer); + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_ILLEGAL_HEX; + return CURLE_RECV_ERROR; + } + ch->state = CHUNK_LF; /* now wait for the CRLF */ + } + break; + + case CHUNK_LF: + /* waiting for the LF after a chunk size */ + if(*buf == 0x0a) { + /* we are now expecting data to come, unless size was zero! */ + if(0 == ch->datasize) { + ch->state = CHUNK_TRAILER; /* now check for trailers */ + } + else { + ch->state = CHUNK_DATA; + CURL_TRC_WRITE(data, "http_chunked, chunk start of %" + CURL_FORMAT_CURL_OFF_T " bytes", ch->datasize); + } + } + + buf++; + blen--; + (*pconsumed)++; + break; + + case CHUNK_DATA: + /* We expect 'datasize' of data. We have 'blen' right now, it can be + more or less than 'datasize'. Get the smallest piece. + */ + piece = blen; + if(ch->datasize < (curl_off_t)blen) + piece = curlx_sotouz(ch->datasize); + + /* Write the data portion available */ + if(!data->set.http_te_skip && !ch->ignore_body) { + if(cw_next) + result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY, + buf, piece); + else + result = Curl_client_write(data, CLIENTWRITE_BODY, + (char *)buf, piece); + if(result) { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_PASSTHRU_ERROR; + return result; + } + } + + *pconsumed += piece; + ch->datasize -= piece; /* decrease amount left to expect */ + buf += piece; /* move read pointer forward */ + blen -= piece; /* decrease space left in this round */ + CURL_TRC_WRITE(data, "http_chunked, write %zu body bytes, %" + CURL_FORMAT_CURL_OFF_T " bytes in chunk remain", + piece, ch->datasize); + + if(0 == ch->datasize) + /* end of data this round, we now expect a trailing CRLF */ + ch->state = CHUNK_POSTLF; + break; + + case CHUNK_POSTLF: + if(*buf == 0x0a) { + /* The last one before we go back to hex state and start all over. */ + Curl_httpchunk_reset(data, ch, ch->ignore_body); + } + else if(*buf != 0x0d) { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_BAD_CHUNK; + return CURLE_RECV_ERROR; + } + buf++; + blen--; + (*pconsumed)++; + break; + + case CHUNK_TRAILER: + if((*buf == 0x0d) || (*buf == 0x0a)) { + char *tr = Curl_dyn_ptr(&ch->trailer); + /* this is the end of a trailer, but if the trailer was zero bytes + there was no trailer and we move on */ + + if(tr) { + size_t trlen; + result = Curl_dyn_addn(&ch->trailer, (char *)STRCONST("\x0d\x0a")); + if(result) { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_OUT_OF_MEMORY; + return result; + } + tr = Curl_dyn_ptr(&ch->trailer); + trlen = Curl_dyn_len(&ch->trailer); + if(!data->set.http_te_skip) { + if(cw_next) + result = Curl_cwriter_write(data, cw_next, + CLIENTWRITE_HEADER| + CLIENTWRITE_TRAILER, + tr, trlen); + else + result = Curl_client_write(data, + CLIENTWRITE_HEADER| + CLIENTWRITE_TRAILER, + tr, trlen); + if(result) { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_PASSTHRU_ERROR; + return result; + } + } + Curl_dyn_reset(&ch->trailer); + ch->state = CHUNK_TRAILER_CR; + if(*buf == 0x0a) + /* already on the LF */ + break; + } + else { + /* no trailer, we are on the final CRLF pair */ + ch->state = CHUNK_TRAILER_POSTCR; + break; /* do not advance the pointer */ + } + } + else { + result = Curl_dyn_addn(&ch->trailer, buf, 1); + if(result) { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_OUT_OF_MEMORY; + return result; + } + } + buf++; + blen--; + (*pconsumed)++; + break; + + case CHUNK_TRAILER_CR: + if(*buf == 0x0a) { + ch->state = CHUNK_TRAILER_POSTCR; + buf++; + blen--; + (*pconsumed)++; + } + else { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_BAD_CHUNK; + return CURLE_RECV_ERROR; + } + break; + + case CHUNK_TRAILER_POSTCR: + /* We enter this state when a CR should arrive so we expect to + have to first pass a CR before we wait for LF */ + if((*buf != 0x0d) && (*buf != 0x0a)) { + /* not a CR then it must be another header in the trailer */ + ch->state = CHUNK_TRAILER; + break; + } + if(*buf == 0x0d) { + /* skip if CR */ + buf++; + blen--; + (*pconsumed)++; + } + /* now wait for the final LF */ + ch->state = CHUNK_STOP; + break; + + case CHUNK_STOP: + if(*buf == 0x0a) { + blen--; + (*pconsumed)++; + /* Record the length of any data left in the end of the buffer + even if there is no more chunks to read */ + ch->datasize = blen; + ch->state = CHUNK_DONE; + CURL_TRC_WRITE(data, "http_chunk, response complete"); + return CURLE_OK; + } + else { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_BAD_CHUNK; + CURL_TRC_WRITE(data, "http_chunk error, expected 0x0a, seeing 0x%ux", + (unsigned int)*buf); + return CURLE_RECV_ERROR; + } + case CHUNK_DONE: + return CURLE_OK; + + case CHUNK_FAILED: + return CURLE_RECV_ERROR; + } + + } + return CURLE_OK; +} + +static const char *Curl_chunked_strerror(CHUNKcode code) +{ + switch(code) { + default: + return "OK"; + case CHUNKE_TOO_LONG_HEX: + return "Too long hexadecimal number"; + case CHUNKE_ILLEGAL_HEX: + return "Illegal or missing hexadecimal sequence"; + case CHUNKE_BAD_CHUNK: + return "Malformed encoding found"; + case CHUNKE_PASSTHRU_ERROR: + return "Error writing data to client"; + case CHUNKE_BAD_ENCODING: + return "Bad content-encoding found"; + case CHUNKE_OUT_OF_MEMORY: + return "Out of memory"; + } +} + +CURLcode Curl_httpchunk_read(struct Curl_easy *data, + struct Curl_chunker *ch, + char *buf, size_t blen, + size_t *pconsumed) +{ + return httpchunk_readwrite(data, ch, NULL, buf, blen, pconsumed); +} + +struct chunked_writer { + struct Curl_cwriter super; + struct Curl_chunker ch; +}; + +static CURLcode cw_chunked_init(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct chunked_writer *ctx = writer->ctx; + + data->req.chunk = TRUE; /* chunks coming our way. */ + Curl_httpchunk_init(data, &ctx->ch, FALSE); + return CURLE_OK; +} + +static void cw_chunked_close(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct chunked_writer *ctx = writer->ctx; + Curl_httpchunk_free(data, &ctx->ch); +} + +static CURLcode cw_chunked_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t blen) +{ + struct chunked_writer *ctx = writer->ctx; + CURLcode result; + size_t consumed; + + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, blen); + + consumed = 0; + result = httpchunk_readwrite(data, &ctx->ch, writer->next, buf, blen, + &consumed); + + if(result) { + if(CHUNKE_PASSTHRU_ERROR == ctx->ch.last_code) { + failf(data, "Failed reading the chunked-encoded stream"); + } + else { + failf(data, "%s in chunked-encoding", + Curl_chunked_strerror(ctx->ch.last_code)); + } + return result; + } + + blen -= consumed; + if(CHUNK_DONE == ctx->ch.state) { + /* chunks read successfully, download is complete */ + data->req.download_done = TRUE; + if(blen) { + infof(data, "Leftovers after chunking: %zu bytes", blen); + } + } + else if((type & CLIENTWRITE_EOS) && !data->req.no_body) { + failf(data, "transfer closed with outstanding read data remaining"); + return CURLE_PARTIAL_FILE; + } + + return CURLE_OK; +} + +/* HTTP chunked Transfer-Encoding decoder */ +const struct Curl_cwtype Curl_httpchunk_unencoder = { + "chunked", + NULL, + cw_chunked_init, + cw_chunked_write, + cw_chunked_close, + sizeof(struct chunked_writer) +}; + +/* max length of an HTTP chunk that we want to generate */ +#define CURL_CHUNKED_MINLEN (1024) +#define CURL_CHUNKED_MAXLEN (64 * 1024) + +struct chunked_reader { + struct Curl_creader super; + struct bufq chunkbuf; + BIT(read_eos); /* we read an EOS from the next reader */ + BIT(eos); /* we have returned an EOS */ +}; + +static CURLcode cr_chunked_init(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct chunked_reader *ctx = reader->ctx; + (void)data; + Curl_bufq_init2(&ctx->chunkbuf, CURL_CHUNKED_MAXLEN, 2, BUFQ_OPT_SOFT_LIMIT); + return CURLE_OK; +} + +static void cr_chunked_close(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct chunked_reader *ctx = reader->ctx; + (void)data; + Curl_bufq_free(&ctx->chunkbuf); +} + +static CURLcode add_last_chunk(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct chunked_reader *ctx = reader->ctx; + struct curl_slist *trailers = NULL, *tr; + CURLcode result; + size_t n; + int rc; + + if(!data->set.trailer_callback) { + CURL_TRC_READ(data, "http_chunk, added last, empty chunk"); + return Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n\r\n"), &n); + } + + result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n"), &n); + if(result) + goto out; + + Curl_set_in_callback(data, true); + rc = data->set.trailer_callback(&trailers, data->set.trailer_data); + Curl_set_in_callback(data, false); + + if(rc != CURL_TRAILERFUNC_OK) { + failf(data, "operation aborted by trailing headers callback"); + result = CURLE_ABORTED_BY_CALLBACK; + goto out; + } + + for(tr = trailers; tr; tr = tr->next) { + /* only add correctly formatted trailers */ + char *ptr = strchr(tr->data, ':'); + if(!ptr || *(ptr + 1) != ' ') { + infof(data, "Malformatted trailing header, skipping trailer"); + continue; + } + + result = Curl_bufq_cwrite(&ctx->chunkbuf, tr->data, + strlen(tr->data), &n); + if(!result) + result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n); + if(result) + goto out; + } + + result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n); + +out: + curl_slist_free_all(trailers); + CURL_TRC_READ(data, "http_chunk, added last chunk with trailers " + "from client -> %d", result); + return result; +} + +static CURLcode add_chunk(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen) +{ + struct chunked_reader *ctx = reader->ctx; + CURLcode result; + char tmp[CURL_CHUNKED_MINLEN]; + size_t nread; + bool eos; + + DEBUGASSERT(!ctx->read_eos); + blen = CURLMIN(blen, CURL_CHUNKED_MAXLEN); /* respect our buffer pref */ + if(blen < sizeof(tmp)) { + /* small read, make a chunk of decent size */ + buf = tmp; + blen = sizeof(tmp); + } + else { + /* larger read, make a chunk that will fit when read back */ + blen -= (8 + 2 + 2); /* deduct max overhead, 8 hex + 2*crlf */ + } + + result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); + if(result) + return result; + if(eos) + ctx->read_eos = TRUE; + + if(nread) { + /* actually got bytes, wrap them into the chunkbuf */ + char hd[11] = ""; + int hdlen; + size_t n; + + hdlen = msnprintf(hd, sizeof(hd), "%zx\r\n", nread); + if(hdlen <= 0) + return CURLE_READ_ERROR; + /* On a soft-limited bufq, we do not need to check that all was written */ + result = Curl_bufq_cwrite(&ctx->chunkbuf, hd, hdlen, &n); + if(!result) + result = Curl_bufq_cwrite(&ctx->chunkbuf, buf, nread, &n); + if(!result) + result = Curl_bufq_cwrite(&ctx->chunkbuf, "\r\n", 2, &n); + CURL_TRC_READ(data, "http_chunk, made chunk of %zu bytes -> %d", + nread, result); + if(result) + return result; + } + + if(ctx->read_eos) + return add_last_chunk(data, reader); + return CURLE_OK; +} + +static CURLcode cr_chunked_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct chunked_reader *ctx = reader->ctx; + CURLcode result = CURLE_READ_ERROR; + + *pnread = 0; + *peos = ctx->eos; + + if(!ctx->eos) { + if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) { + /* Still getting data form the next reader, buffer is empty */ + result = add_chunk(data, reader, buf, blen); + if(result) + return result; + } + + if(!Curl_bufq_is_empty(&ctx->chunkbuf)) { + result = Curl_bufq_cread(&ctx->chunkbuf, buf, blen, pnread); + if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) { + /* no more data, read all, done. */ + ctx->eos = TRUE; + *peos = TRUE; + } + return result; + } + } + /* We may get here, because we are done or because callbacks paused */ + DEBUGASSERT(ctx->eos || !ctx->read_eos); + return CURLE_OK; +} + +static curl_off_t cr_chunked_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + /* this reader changes length depending on input */ + (void)data; + (void)reader; + return -1; +} + +/* HTTP chunked Transfer-Encoding encoder */ +const struct Curl_crtype Curl_httpchunk_encoder = { + "chunked", + cr_chunked_init, + cr_chunked_read, + cr_chunked_close, + Curl_creader_def_needs_rewind, + cr_chunked_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_rewind, + Curl_creader_def_unpause, + Curl_creader_def_is_paused, + Curl_creader_def_done, + sizeof(struct chunked_reader) +}; + +CURLcode Curl_httpchunk_add_reader(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &Curl_httpchunk_encoder, + CURL_CR_TRANSFER_ENCODE); + if(!result) + result = Curl_creader_add(data, reader); + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + +#endif /* CURL_DISABLE_HTTP */ diff --git a/third_party/curl/lib/http_chunks.h b/third_party/curl/lib/http_chunks.h new file mode 100644 index 0000000000..34951ea0f4 --- /dev/null +++ b/third_party/curl/lib/http_chunks.h @@ -0,0 +1,145 @@ +#ifndef HEADER_CURL_HTTP_CHUNKS_H +#define HEADER_CURL_HTTP_CHUNKS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#ifndef CURL_DISABLE_HTTP + +#include "dynbuf.h" + +struct connectdata; + +/* + * The longest possible hexadecimal number we support in a chunked transfer. + * Neither RFC2616 nor the later HTTP specs define a maximum chunk size. + * For 64-bit curl_off_t we support 16 digits. For 32-bit, 8 digits. + */ +#define CHUNK_MAXNUM_LEN (SIZEOF_CURL_OFF_T * 2) + +typedef enum { + /* await and buffer all hexadecimal digits until we get one that is not a + hexadecimal digit. When done, we go CHUNK_LF */ + CHUNK_HEX, + + /* wait for LF, ignore all else */ + CHUNK_LF, + + /* We eat the amount of data specified. When done, we move on to the + POST_CR state. */ + CHUNK_DATA, + + /* POSTLF should get a CR and then a LF and nothing else, then move back to + HEX as the CRLF combination marks the end of a chunk. A missing CR is no + big deal. */ + CHUNK_POSTLF, + + /* Used to mark that we are out of the game. NOTE: that there is a + 'datasize' field in the struct that will tell how many bytes that were + not passed to the client in the end of the last buffer! */ + CHUNK_STOP, + + /* At this point optional trailer headers can be found, unless the next line + is CRLF */ + CHUNK_TRAILER, + + /* A trailer CR has been found - next state is CHUNK_TRAILER_POSTCR. + Next char must be a LF */ + CHUNK_TRAILER_CR, + + /* A trailer LF must be found now, otherwise CHUNKE_BAD_CHUNK will be + signalled If this is an empty trailer CHUNKE_STOP will be signalled. + Otherwise the trailer will be broadcasted via Curl_client_write() and the + next state will be CHUNK_TRAILER */ + CHUNK_TRAILER_POSTCR, + + /* Successfully de-chunked everything */ + CHUNK_DONE, + + /* Failed on seeing a bad or not correctly terminated chunk */ + CHUNK_FAILED +} ChunkyState; + +typedef enum { + CHUNKE_OK = 0, + CHUNKE_TOO_LONG_HEX = 1, + CHUNKE_ILLEGAL_HEX, + CHUNKE_BAD_CHUNK, + CHUNKE_BAD_ENCODING, + CHUNKE_OUT_OF_MEMORY, + CHUNKE_PASSTHRU_ERROR /* Curl_httpchunk_read() returns a CURLcode to use */ +} CHUNKcode; + +struct Curl_chunker { + curl_off_t datasize; + ChunkyState state; + CHUNKcode last_code; + struct dynbuf trailer; /* for chunked-encoded trailer */ + unsigned char hexindex; + char hexbuffer[CHUNK_MAXNUM_LEN + 1]; /* +1 for null-terminator */ + BIT(ignore_body); /* never write response body data */ +}; + +/* The following functions are defined in http_chunks.c */ +void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch, + bool ignore_body); +void Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch); +void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch, + bool ignore_body); + +/* + * Read BODY bytes in HTTP/1.1 chunked encoding from `buf` and return + * the amount of bytes consumed. The actual response bytes and trailer + * headers are written out to the client. + * On success, this will consume all bytes up to the end of the response, + * e.g. the last chunk, has been processed. + * @param data the transfer involved + * @param ch the chunker instance keeping state across calls + * @param buf the response data + * @param blen amount of bytes in `buf` + * @param pconsumed on successful return, the number of bytes in `buf` + * consumed + * + * This function always uses ASCII hex values to accommodate non-ASCII hosts. + * For example, 0x0d and 0x0a are used instead of '\r' and '\n'. + */ +CURLcode Curl_httpchunk_read(struct Curl_easy *data, struct Curl_chunker *ch, + char *buf, size_t blen, size_t *pconsumed); + +/** + * @return TRUE iff chunked decoded has finished successfully. + */ +bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch); + +extern const struct Curl_cwtype Curl_httpchunk_unencoder; + +extern const struct Curl_crtype Curl_httpchunk_encoder; + +/** + * Add a transfer-encoding "chunked" reader to the transfers reader stack + */ +CURLcode Curl_httpchunk_add_reader(struct Curl_easy *data); + +#endif /* !CURL_DISABLE_HTTP */ + +#endif /* HEADER_CURL_HTTP_CHUNKS_H */ diff --git a/third_party/curl/lib/http_digest.c b/third_party/curl/lib/http_digest.c new file mode 100644 index 0000000000..2db3125a8e --- /dev/null +++ b/third_party/curl/lib/http_digest.c @@ -0,0 +1,185 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) + +#include "urldata.h" +#include "strcase.h" +#include "vauth/vauth.h" +#include "http_digest.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* Test example headers: + +WWW-Authenticate: Digest realm="testrealm", nonce="1053604598" +Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598" + +*/ + +CURLcode Curl_input_digest(struct Curl_easy *data, + bool proxy, + const char *header) /* rest of the *-authenticate: + header */ +{ + /* Point to the correct struct with this */ + struct digestdata *digest; + + if(proxy) { + digest = &data->state.proxydigest; + } + else { + digest = &data->state.digest; + } + + if(!checkprefix("Digest", header) || !ISBLANK(header[6])) + return CURLE_BAD_CONTENT_ENCODING; + + header += strlen("Digest"); + while(*header && ISBLANK(*header)) + header++; + + return Curl_auth_decode_digest_http_message(header, digest); +} + +CURLcode Curl_output_digest(struct Curl_easy *data, + bool proxy, + const unsigned char *request, + const unsigned char *uripath) +{ + CURLcode result; + unsigned char *path = NULL; + char *tmp = NULL; + char *response; + size_t len; + bool have_chlg; + + /* Point to the address of the pointer that holds the string to send to the + server, which is for a plain host or for an HTTP proxy */ + char **allocuserpwd; + + /* Point to the name and password for this */ + const char *userp; + const char *passwdp; + + /* Point to the correct struct with this */ + struct digestdata *digest; + struct auth *authp; + + if(proxy) { +#ifdef CURL_DISABLE_PROXY + return CURLE_NOT_BUILT_IN; +#else + digest = &data->state.proxydigest; + allocuserpwd = &data->state.aptr.proxyuserpwd; + userp = data->state.aptr.proxyuser; + passwdp = data->state.aptr.proxypasswd; + authp = &data->state.authproxy; +#endif + } + else { + digest = &data->state.digest; + allocuserpwd = &data->state.aptr.userpwd; + userp = data->state.aptr.user; + passwdp = data->state.aptr.passwd; + authp = &data->state.authhost; + } + + Curl_safefree(*allocuserpwd); + + /* not set means empty */ + if(!userp) + userp = ""; + + if(!passwdp) + passwdp = ""; + +#if defined(USE_WINDOWS_SSPI) + have_chlg = digest->input_token ? TRUE : FALSE; +#else + have_chlg = digest->nonce ? TRUE : FALSE; +#endif + + if(!have_chlg) { + authp->done = FALSE; + return CURLE_OK; + } + + /* So IE browsers < v7 cut off the URI part at the query part when they + evaluate the MD5 and some (IIS?) servers work with them so we may need to + do the Digest IE-style. Note that the different ways cause different MD5 + sums to get sent. + + Apache servers can be set to do the Digest IE-style automatically using + the BrowserMatch feature: + https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#msie + + Further details on Digest implementation differences: + http://www.fngtps.com/2006/09/http-authentication + */ + + if(authp->iestyle) { + tmp = strchr((char *)uripath, '?'); + if(tmp) { + size_t urilen = tmp - (char *)uripath; + /* typecast is fine here since the value is always less than 32 bits */ + path = (unsigned char *) aprintf("%.*s", (int)urilen, uripath); + } + } + if(!tmp) + path = (unsigned char *) strdup((char *) uripath); + + if(!path) + return CURLE_OUT_OF_MEMORY; + + result = Curl_auth_create_digest_http_message(data, userp, passwdp, request, + path, digest, &response, &len); + free(path); + if(result) + return result; + + *allocuserpwd = aprintf("%sAuthorization: Digest %s\r\n", + proxy ? "Proxy-" : "", + response); + free(response); + if(!*allocuserpwd) + return CURLE_OUT_OF_MEMORY; + + authp->done = TRUE; + + return CURLE_OK; +} + +void Curl_http_auth_cleanup_digest(struct Curl_easy *data) +{ + Curl_auth_digest_cleanup(&data->state.digest); + Curl_auth_digest_cleanup(&data->state.proxydigest); +} + +#endif diff --git a/third_party/curl/lib/http_digest.h b/third_party/curl/lib/http_digest.h new file mode 100644 index 0000000000..5f797310fd --- /dev/null +++ b/third_party/curl/lib/http_digest.h @@ -0,0 +1,44 @@ +#ifndef HEADER_CURL_HTTP_DIGEST_H +#define HEADER_CURL_HTTP_DIGEST_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) + +/* this is for digest header input */ +CURLcode Curl_input_digest(struct Curl_easy *data, + bool proxy, const char *header); + +/* this is for creating digest header output */ +CURLcode Curl_output_digest(struct Curl_easy *data, + bool proxy, + const unsigned char *request, + const unsigned char *uripath); + +void Curl_http_auth_cleanup_digest(struct Curl_easy *data); + +#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_DIGEST_AUTH */ + +#endif /* HEADER_CURL_HTTP_DIGEST_H */ diff --git a/third_party/curl/lib/http_negotiate.c b/third_party/curl/lib/http_negotiate.c new file mode 100644 index 0000000000..629de834f1 --- /dev/null +++ b/third_party/curl/lib/http_negotiate.c @@ -0,0 +1,239 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) + +#include "urldata.h" +#include "sendf.h" +#include "http_negotiate.h" +#include "vauth/vauth.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, + bool proxy, const char *header) +{ + CURLcode result; + size_t len; + + /* Point to the username, password, service and host */ + const char *userp; + const char *passwdp; + const char *service; + const char *host; + + /* Point to the correct struct with this */ + struct negotiatedata *neg_ctx; + curlnegotiate state; + + if(proxy) { +#ifndef CURL_DISABLE_PROXY + userp = conn->http_proxy.user; + passwdp = conn->http_proxy.passwd; + service = data->set.str[STRING_PROXY_SERVICE_NAME] ? + data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; + host = conn->http_proxy.host.name; + neg_ctx = &conn->proxyneg; + state = conn->proxy_negotiate_state; +#else + return CURLE_NOT_BUILT_IN; +#endif + } + else { + userp = conn->user; + passwdp = conn->passwd; + service = data->set.str[STRING_SERVICE_NAME] ? + data->set.str[STRING_SERVICE_NAME] : "HTTP"; + host = conn->host.name; + neg_ctx = &conn->negotiate; + state = conn->http_negotiate_state; + } + + /* Not set means empty */ + if(!userp) + userp = ""; + + if(!passwdp) + passwdp = ""; + + /* Obtain the input token, if any */ + header += strlen("Negotiate"); + while(*header && ISBLANK(*header)) + header++; + + len = strlen(header); + neg_ctx->havenegdata = len != 0; + if(!len) { + if(state == GSS_AUTHSUCC) { + infof(data, "Negotiate auth restarted"); + Curl_http_auth_cleanup_negotiate(conn); + } + else if(state != GSS_AUTHNONE) { + /* The server rejected our authentication and has not supplied any more + negotiation mechanisms */ + Curl_http_auth_cleanup_negotiate(conn); + return CURLE_LOGIN_DENIED; + } + } + + /* Supports SSL channel binding for Windows ISS extended protection */ +#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) + neg_ctx->sslContext = conn->sslContext; +#endif + + /* Initialize the security context and decode our challenge */ + result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, + host, header, neg_ctx); + + if(result) + Curl_http_auth_cleanup_negotiate(conn); + + return result; +} + +CURLcode Curl_output_negotiate(struct Curl_easy *data, + struct connectdata *conn, bool proxy) +{ + struct negotiatedata *neg_ctx; + struct auth *authp; + curlnegotiate *state; + char *base64 = NULL; + size_t len = 0; + char *userp; + CURLcode result; + + if(proxy) { +#ifndef CURL_DISABLE_PROXY + neg_ctx = &conn->proxyneg; + authp = &data->state.authproxy; + state = &conn->proxy_negotiate_state; +#else + return CURLE_NOT_BUILT_IN; +#endif + } + else { + neg_ctx = &conn->negotiate; + authp = &data->state.authhost; + state = &conn->http_negotiate_state; + } + + authp->done = FALSE; + + if(*state == GSS_AUTHRECV) { + if(neg_ctx->havenegdata) { + neg_ctx->havemultiplerequests = TRUE; + } + } + else if(*state == GSS_AUTHSUCC) { + if(!neg_ctx->havenoauthpersist) { + neg_ctx->noauthpersist = !neg_ctx->havemultiplerequests; + } + } + + if(neg_ctx->noauthpersist || + (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) { + + if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) { + infof(data, "Curl_output_negotiate, " + "no persistent authentication: cleanup existing context"); + Curl_http_auth_cleanup_negotiate(conn); + } + if(!neg_ctx->context) { + result = Curl_input_negotiate(data, conn, proxy, "Negotiate"); + if(result == CURLE_AUTH_ERROR) { + /* negotiate auth failed, let's continue unauthenticated to stay + * compatible with the behavior before curl-7_64_0-158-g6c6035532 */ + authp->done = TRUE; + return CURLE_OK; + } + else if(result) + return result; + } + + result = Curl_auth_create_spnego_message(neg_ctx, &base64, &len); + if(result) + return result; + + userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", + base64); + + if(proxy) { +#ifndef CURL_DISABLE_PROXY + Curl_safefree(data->state.aptr.proxyuserpwd); + data->state.aptr.proxyuserpwd = userp; +#endif + } + else { + Curl_safefree(data->state.aptr.userpwd); + data->state.aptr.userpwd = userp; + } + + free(base64); + + if(!userp) { + return CURLE_OUT_OF_MEMORY; + } + + *state = GSS_AUTHSENT; + #ifdef HAVE_GSSAPI + if(neg_ctx->status == GSS_S_COMPLETE || + neg_ctx->status == GSS_S_CONTINUE_NEEDED) { + *state = GSS_AUTHDONE; + } + #else + #ifdef USE_WINDOWS_SSPI + if(neg_ctx->status == SEC_E_OK || + neg_ctx->status == SEC_I_CONTINUE_NEEDED) { + *state = GSS_AUTHDONE; + } + #endif + #endif + } + + if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) { + /* connection is already authenticated, + * do not send a header in future requests */ + authp->done = TRUE; + } + + neg_ctx->havenegdata = FALSE; + + return CURLE_OK; +} + +void Curl_http_auth_cleanup_negotiate(struct connectdata *conn) +{ + conn->http_negotiate_state = GSS_AUTHNONE; + conn->proxy_negotiate_state = GSS_AUTHNONE; + + Curl_auth_cleanup_spnego(&conn->negotiate); + Curl_auth_cleanup_spnego(&conn->proxyneg); +} + +#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */ diff --git a/third_party/curl/lib/http_negotiate.h b/third_party/curl/lib/http_negotiate.h new file mode 100644 index 0000000000..76d8356132 --- /dev/null +++ b/third_party/curl/lib/http_negotiate.h @@ -0,0 +1,43 @@ +#ifndef HEADER_CURL_HTTP_NEGOTIATE_H +#define HEADER_CURL_HTTP_NEGOTIATE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) + +/* this is for Negotiate header input */ +CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, + bool proxy, const char *header); + +/* this is for creating Negotiate header output */ +CURLcode Curl_output_negotiate(struct Curl_easy *data, + struct connectdata *conn, bool proxy); + +void Curl_http_auth_cleanup_negotiate(struct connectdata *conn); + +#else /* !CURL_DISABLE_HTTP && USE_SPNEGO */ +#define Curl_http_auth_cleanup_negotiate(x) +#endif + +#endif /* HEADER_CURL_HTTP_NEGOTIATE_H */ diff --git a/third_party/curl/lib/http_ntlm.c b/third_party/curl/lib/http_ntlm.c new file mode 100644 index 0000000000..95c138d4f4 --- /dev/null +++ b/third_party/curl/lib/http_ntlm.c @@ -0,0 +1,270 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) + +/* + * NTLM details: + * + * https://davenport.sourceforge.net/ntlm.html + * https://www.innovation.ch/java/ntlm.html + */ + +#define DEBUG_ME 0 + +#include "urldata.h" +#include "sendf.h" +#include "strcase.h" +#include "http_ntlm.h" +#include "curl_ntlm_core.h" +#include "curl_base64.h" +#include "vauth/vauth.h" +#include "url.h" + +/* SSL backend-specific #if branches in this file must be kept in the order + documented in curl_ntlm_core. */ +#if defined(USE_WINDOWS_SSPI) +#include "curl_sspi.h" +#endif + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if DEBUG_ME +# define DEBUG_OUT(x) x +#else +# define DEBUG_OUT(x) Curl_nop_stmt +#endif + +CURLcode Curl_input_ntlm(struct Curl_easy *data, + bool proxy, /* if proxy or not */ + const char *header) /* rest of the www-authenticate: + header */ +{ + /* point to the correct struct with this */ + struct ntlmdata *ntlm; + curlntlm *state; + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + ntlm = proxy ? &conn->proxyntlm : &conn->ntlm; + state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state; + + if(checkprefix("NTLM", header)) { + header += strlen("NTLM"); + + while(*header && ISSPACE(*header)) + header++; + + if(*header) { + unsigned char *hdr; + size_t hdrlen; + + result = Curl_base64_decode(header, &hdr, &hdrlen); + if(!result) { + struct bufref hdrbuf; + + Curl_bufref_init(&hdrbuf); + Curl_bufref_set(&hdrbuf, hdr, hdrlen, curl_free); + result = Curl_auth_decode_ntlm_type2_message(data, &hdrbuf, ntlm); + Curl_bufref_free(&hdrbuf); + } + if(result) + return result; + + *state = NTLMSTATE_TYPE2; /* We got a type-2 message */ + } + else { + if(*state == NTLMSTATE_LAST) { + infof(data, "NTLM auth restarted"); + Curl_http_auth_cleanup_ntlm(conn); + } + else if(*state == NTLMSTATE_TYPE3) { + infof(data, "NTLM handshake rejected"); + Curl_http_auth_cleanup_ntlm(conn); + *state = NTLMSTATE_NONE; + return CURLE_REMOTE_ACCESS_DENIED; + } + else if(*state >= NTLMSTATE_TYPE1) { + infof(data, "NTLM handshake failure (internal error)"); + return CURLE_REMOTE_ACCESS_DENIED; + } + + *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */ + } + } + + return result; +} + +/* + * This is for creating ntlm header output + */ +CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) +{ + char *base64 = NULL; + size_t len = 0; + CURLcode result = CURLE_OK; + struct bufref ntlmmsg; + + /* point to the address of the pointer that holds the string to send to the + server, which is for a plain host or for an HTTP proxy */ + char **allocuserpwd; + + /* point to the username, password, service and host */ + const char *userp; + const char *passwdp; + const char *service = NULL; + const char *hostname = NULL; + + /* point to the correct struct with this */ + struct ntlmdata *ntlm; + curlntlm *state; + struct auth *authp; + struct connectdata *conn = data->conn; + + DEBUGASSERT(conn); + DEBUGASSERT(data); + + if(proxy) { +#ifndef CURL_DISABLE_PROXY + allocuserpwd = &data->state.aptr.proxyuserpwd; + userp = data->state.aptr.proxyuser; + passwdp = data->state.aptr.proxypasswd; + service = data->set.str[STRING_PROXY_SERVICE_NAME] ? + data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; + hostname = conn->http_proxy.host.name; + ntlm = &conn->proxyntlm; + state = &conn->proxy_ntlm_state; + authp = &data->state.authproxy; +#else + return CURLE_NOT_BUILT_IN; +#endif + } + else { + allocuserpwd = &data->state.aptr.userpwd; + userp = data->state.aptr.user; + passwdp = data->state.aptr.passwd; + service = data->set.str[STRING_SERVICE_NAME] ? + data->set.str[STRING_SERVICE_NAME] : "HTTP"; + hostname = conn->host.name; + ntlm = &conn->ntlm; + state = &conn->http_ntlm_state; + authp = &data->state.authhost; + } + authp->done = FALSE; + + /* not set means empty */ + if(!userp) + userp = ""; + + if(!passwdp) + passwdp = ""; + +#ifdef USE_WINDOWS_SSPI + if(!s_hSecDll) { + /* not thread safe and leaks - use curl_global_init() to avoid */ + CURLcode err = Curl_sspi_global_init(); + if(!s_hSecDll) + return err; + } +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + ntlm->sslContext = conn->sslContext; +#endif +#endif + + Curl_bufref_init(&ntlmmsg); + + /* connection is already authenticated, do not send a header in future + * requests so go directly to NTLMSTATE_LAST */ + if(*state == NTLMSTATE_TYPE3) + *state = NTLMSTATE_LAST; + + switch(*state) { + case NTLMSTATE_TYPE1: + default: /* for the weird cases we (re)start here */ + /* Create a type-1 message */ + result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp, + service, hostname, + ntlm, &ntlmmsg); + if(!result) { + DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); + result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), + Curl_bufref_len(&ntlmmsg), &base64, &len); + if(!result) { + free(*allocuserpwd); + *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", + proxy ? "Proxy-" : "", + base64); + free(base64); + if(!*allocuserpwd) + result = CURLE_OUT_OF_MEMORY; + } + } + break; + + case NTLMSTATE_TYPE2: + /* We already received the type-2 message, create a type-3 message */ + result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp, + ntlm, &ntlmmsg); + if(!result && Curl_bufref_len(&ntlmmsg)) { + result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), + Curl_bufref_len(&ntlmmsg), &base64, &len); + if(!result) { + free(*allocuserpwd); + *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", + proxy ? "Proxy-" : "", + base64); + free(base64); + if(!*allocuserpwd) + result = CURLE_OUT_OF_MEMORY; + else { + *state = NTLMSTATE_TYPE3; /* we send a type-3 */ + authp->done = TRUE; + } + } + } + break; + + case NTLMSTATE_LAST: + Curl_safefree(*allocuserpwd); + authp->done = TRUE; + break; + } + Curl_bufref_free(&ntlmmsg); + + return result; +} + +void Curl_http_auth_cleanup_ntlm(struct connectdata *conn) +{ + Curl_auth_cleanup_ntlm(&conn->ntlm); + Curl_auth_cleanup_ntlm(&conn->proxyntlm); +} + +#endif /* !CURL_DISABLE_HTTP && USE_NTLM */ diff --git a/third_party/curl/lib/http_ntlm.h b/third_party/curl/lib/http_ntlm.h new file mode 100644 index 0000000000..f37572baec --- /dev/null +++ b/third_party/curl/lib/http_ntlm.h @@ -0,0 +1,44 @@ +#ifndef HEADER_CURL_HTTP_NTLM_H +#define HEADER_CURL_HTTP_NTLM_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) + +/* this is for ntlm header input */ +CURLcode Curl_input_ntlm(struct Curl_easy *data, bool proxy, + const char *header); + +/* this is for creating ntlm header output */ +CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy); + +void Curl_http_auth_cleanup_ntlm(struct connectdata *conn); + +#else /* !CURL_DISABLE_HTTP && USE_NTLM */ +#define Curl_http_auth_cleanup_ntlm(x) +#endif + +#endif /* HEADER_CURL_HTTP_NTLM_H */ diff --git a/third_party/curl/lib/http_proxy.c b/third_party/curl/lib/http_proxy.c new file mode 100644 index 0000000000..a5f27f5ce8 --- /dev/null +++ b/third_party/curl/lib/http_proxy.c @@ -0,0 +1,337 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "http_proxy.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) + +#include +#ifdef USE_HYPER +#include +#endif +#include "sendf.h" +#include "http.h" +#include "url.h" +#include "select.h" +#include "progress.h" +#include "cfilters.h" +#include "cf-h1-proxy.h" +#include "cf-h2-proxy.h" +#include "connect.h" +#include "curlx.h" +#include "vtls/vtls.h" +#include "transfer.h" +#include "multiif.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf, + const char **phostname, + int *pport, bool *pipv6_ip) +{ + DEBUGASSERT(cf); + DEBUGASSERT(cf->conn); + + if(cf->conn->bits.conn_to_host) + *phostname = cf->conn->conn_to_host.name; + else if(cf->sockindex == SECONDARYSOCKET) + *phostname = cf->conn->secondaryhostname; + else + *phostname = cf->conn->host.name; + + if(cf->sockindex == SECONDARYSOCKET) + *pport = cf->conn->secondary_port; + else if(cf->conn->bits.conn_to_port) + *pport = cf->conn->conn_to_port; + else + *pport = cf->conn->remote_port; + + if(*phostname != cf->conn->host.name) + *pipv6_ip = (strchr(*phostname, ':') != NULL); + else + *pipv6_ip = cf->conn->bits.ipv6_ip; + + return CURLE_OK; +} + +CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + int http_version_major) +{ + const char *hostname = NULL; + char *authority = NULL; + int port; + bool ipv6_ip; + CURLcode result; + struct httpreq *req = NULL; + + result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); + if(result) + goto out; + + authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, + ipv6_ip?"]":"", port); + if(!authority) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1, + NULL, 0, authority, strlen(authority), + NULL, 0); + if(result) + goto out; + + /* Setup the proxy-authorization header, if any */ + result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, + req->authority, TRUE); + if(result) + goto out; + + /* If user is not overriding Host: header, we add for HTTP/1.x */ + if(http_version_major == 1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { + result = Curl_dynhds_cadd(&req->headers, "Host", authority); + if(result) + goto out; + } + + if(data->state.aptr.proxyuserpwd) { + result = Curl_dynhds_h1_cadd_line(&req->headers, + data->state.aptr.proxyuserpwd); + if(result) + goto out; + } + + if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) && + data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { + result = Curl_dynhds_cadd(&req->headers, "User-Agent", + data->set.str[STRING_USERAGENT]); + if(result) + goto out; + } + + if(http_version_major == 1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { + result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); + if(result) + goto out; + } + + result = Curl_dynhds_add_custom(data, TRUE, &req->headers); + +out: + if(result && req) { + Curl_http_req_free(req); + req = NULL; + } + free(authority); + *preq = req; + return result; +} + + +struct cf_proxy_ctx { + /* the protocol specific sub-filter we install during connect */ + struct Curl_cfilter *cf_protocol; +}; + +static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_proxy_ctx *ctx = cf->ctx; + CURLcode result; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + CURL_TRC_CF(data, cf, "connect"); +connect_sub: + result = cf->next->cft->do_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + + *done = FALSE; + if(!ctx->cf_protocol) { + struct Curl_cfilter *cf_protocol = NULL; + int alpn = Curl_conn_cf_is_ssl(cf->next)? + cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1; + + /* First time call after the subchain connected */ + switch(alpn) { + case CURL_HTTP_VERSION_NONE: + case CURL_HTTP_VERSION_1_0: + case CURL_HTTP_VERSION_1_1: + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1"); + infof(data, "CONNECT tunnel: HTTP/1.%d negotiated", + (alpn == CURL_HTTP_VERSION_1_0)? 0 : 1); + result = Curl_cf_h1_proxy_insert_after(cf, data); + if(result) + goto out; + cf_protocol = cf->next; + break; +#ifdef USE_NGHTTP2 + case CURL_HTTP_VERSION_2: + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); + infof(data, "CONNECT tunnel: HTTP/2 negotiated"); + result = Curl_cf_h2_proxy_insert_after(cf, data); + if(result) + goto out; + cf_protocol = cf->next; + break; +#endif + default: + infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn); + result = CURLE_COULDNT_CONNECT; + goto out; + } + + ctx->cf_protocol = cf_protocol; + /* after we installed the filter "below" us, we call connect + * on out sub-chain again. + */ + goto connect_sub; + } + else { + /* subchain connected and we had already installed the protocol filter. + * This means the protocol tunnel is established, we are done. + */ + DEBUGASSERT(ctx->cf_protocol); + result = CURLE_OK; + } + +out: + if(!result) { + cf->connected = TRUE; + *done = TRUE; + } + return result; +} + +void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char **phost, + const char **pdisplay_host, + int *pport) +{ + (void)data; + if(!cf->connected) { + *phost = cf->conn->http_proxy.host.name; + *pdisplay_host = cf->conn->http_proxy.host.dispname; + *pport = (int)cf->conn->http_proxy.port; + } + else { + cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); + } +} + +static void http_proxy_cf_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_proxy_ctx *ctx = cf->ctx; + + (void)data; + CURL_TRC_CF(data, cf, "destroy"); + free(ctx); +} + +static void http_proxy_cf_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_proxy_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "close"); + cf->connected = FALSE; + if(ctx->cf_protocol) { + struct Curl_cfilter *f; + /* if someone already removed it, we assume he also + * took care of destroying it. */ + for(f = cf->next; f; f = f->next) { + if(f == ctx->cf_protocol) { + /* still in our sub-chain */ + Curl_conn_cf_discard_sub(cf, ctx->cf_protocol, data, FALSE); + break; + } + } + ctx->cf_protocol = NULL; + } + if(cf->next) + cf->next->cft->do_close(cf->next, data); +} + + +struct Curl_cftype Curl_cft_http_proxy = { + "HTTP-PROXY", + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, + 0, + http_proxy_cf_destroy, + http_proxy_cf_connect, + http_proxy_cf_close, + Curl_cf_def_shutdown, + Curl_cf_http_proxy_get_host, + Curl_cf_def_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + struct cf_proxy_ctx *ctx = NULL; + CURLcode result; + + (void)data; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); + if(result) + goto out; + ctx = NULL; + Curl_conn_cf_insert_after(cf_at, cf); + +out: + free(ctx); + return result; +} + +#endif /* ! CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */ diff --git a/third_party/curl/lib/http_proxy.h b/third_party/curl/lib/http_proxy.h new file mode 100644 index 0000000000..2b5f7ae706 --- /dev/null +++ b/third_party/curl/lib/http_proxy.h @@ -0,0 +1,61 @@ +#ifndef HEADER_CURL_HTTP_PROXY_H +#define HEADER_CURL_HTTP_PROXY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include "urldata.h" + +CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf, + const char **phostname, + int *pport, bool *pipv6_ip); + +CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + int http_version_major); + +/* Default proxy timeout in milliseconds */ +#define PROXY_TIMEOUT (3600*1000) + +void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char **phost, + const char **pdisplay_host, + int *pport); + +CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_http_proxy; + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + +#define IS_HTTPS_PROXY(t) (((t) == CURLPROXY_HTTPS) || \ + ((t) == CURLPROXY_HTTPS2)) + +#endif /* HEADER_CURL_HTTP_PROXY_H */ diff --git a/third_party/curl/lib/idn.c b/third_party/curl/lib/idn.c new file mode 100644 index 0000000000..ef55ddfec0 --- /dev/null +++ b/third_party/curl/lib/idn.c @@ -0,0 +1,337 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + + /* + * IDN conversions + */ + +#include "curl_setup.h" +#include "urldata.h" +#include "idn.h" +#include "sendf.h" +#include "curl_multibyte.h" +#include "warnless.h" + +#ifdef USE_LIBIDN2 +#include + +#if defined(_WIN32) && defined(UNICODE) +#define IDN2_LOOKUP(name, host, flags) \ + idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags) +#else +#define IDN2_LOOKUP(name, host, flags) \ + idn2_lookup_ul((const char *)name, (char **)host, flags) +#endif +#endif /* USE_LIBIDN2 */ + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* for macOS and iOS targets */ +#if defined(USE_APPLE_IDN) +#include + +#define MAX_HOST_LENGTH 512 + +static CURLcode mac_idn_to_ascii(const char *in, char **out) +{ + size_t inlen = strlen(in); + if(inlen < MAX_HOST_LENGTH) { + UErrorCode err = U_ZERO_ERROR; + UIDNA* idna = uidna_openUTS46( + UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_ASCII, &err); + if(!U_FAILURE(err)) { + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + char buffer[MAX_HOST_LENGTH] = {0}; + (void)uidna_nameToASCII_UTF8(idna, in, -1, buffer, + sizeof(buffer) - 1, &info, &err); + uidna_close(idna); + if(!U_FAILURE(err)) { + *out = strdup(buffer); + if(*out) + return CURLE_OK; + else + return CURLE_OUT_OF_MEMORY; + } + } + } + return CURLE_URL_MALFORMAT; +} + +static CURLcode mac_ascii_to_idn(const char *in, char **out) +{ + size_t inlen = strlen(in); + if(inlen < MAX_HOST_LENGTH) { + UErrorCode err = U_ZERO_ERROR; + UIDNA* idna = uidna_openUTS46( + UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_UNICODE, &err); + if(!U_FAILURE(err)) { + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + char buffer[MAX_HOST_LENGTH] = {0}; + (void)uidna_nameToUnicodeUTF8(idna, in, -1, buffer, + sizeof(buffer) - 1, &info, &err); + uidna_close(idna); + if(!U_FAILURE(err)) { + *out = strdup(buffer); + if(*out) + return CURLE_OK; + else + return CURLE_OUT_OF_MEMORY; + } + } + } + return CURLE_URL_MALFORMAT; +} +#endif + +#ifdef USE_WIN32_IDN +/* using Windows kernel32 and normaliz libraries. */ + +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600 +WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags, + const WCHAR *lpUnicodeCharStr, + int cchUnicodeChar, + WCHAR *lpASCIICharStr, + int cchASCIIChar); +WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags, + const WCHAR *lpASCIICharStr, + int cchASCIIChar, + WCHAR *lpUnicodeCharStr, + int cchUnicodeChar); +#endif + +#define IDN_MAX_LENGTH 255 + +static CURLcode win32_idn_to_ascii(const char *in, char **out) +{ + wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); + *out = NULL; + if(in_w) { + wchar_t punycode[IDN_MAX_LENGTH]; + int chars = IdnToAscii(0, in_w, (int)(wcslen(in_w) + 1), punycode, + IDN_MAX_LENGTH); + curlx_unicodefree(in_w); + if(chars) { + char *mstr = curlx_convert_wchar_to_UTF8(punycode); + if(mstr) { + *out = strdup(mstr); + curlx_unicodefree(mstr); + if(!*out) + return CURLE_OUT_OF_MEMORY; + } + else + return CURLE_OUT_OF_MEMORY; + } + else + return CURLE_URL_MALFORMAT; + } + else + return CURLE_URL_MALFORMAT; + + return CURLE_OK; +} + +static CURLcode win32_ascii_to_idn(const char *in, char **output) +{ + char *out = NULL; + + wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); + if(in_w) { + WCHAR idn[IDN_MAX_LENGTH]; /* stores a UTF-16 string */ + int chars = IdnToUnicode(0, in_w, (int)(wcslen(in_w) + 1), idn, + IDN_MAX_LENGTH); + if(chars) { + /* 'chars' is "the number of characters retrieved" */ + char *mstr = curlx_convert_wchar_to_UTF8(idn); + if(mstr) { + out = strdup(mstr); + curlx_unicodefree(mstr); + if(!out) + return CURLE_OUT_OF_MEMORY; + } + } + else + return CURLE_URL_MALFORMAT; + } + else + return CURLE_URL_MALFORMAT; + *output = out; + return CURLE_OK; +} + +#endif /* USE_WIN32_IDN */ + +/* + * Helpers for IDNA conversions. + */ +bool Curl_is_ASCII_name(const char *hostname) +{ + /* get an UNSIGNED local version of the pointer */ + const unsigned char *ch = (const unsigned char *)hostname; + + if(!hostname) /* bad input, consider it ASCII! */ + return TRUE; + + while(*ch) { + if(*ch++ & 0x80) + return FALSE; + } + return TRUE; +} + +#ifdef USE_IDN +/* + * Curl_idn_decode() returns an allocated IDN decoded string if it was + * possible. NULL on error. + * + * CURLE_URL_MALFORMAT - the hostname could not be converted + * CURLE_OUT_OF_MEMORY - memory problem + * + */ +static CURLcode idn_decode(const char *input, char **output) +{ + char *decoded = NULL; + CURLcode result = CURLE_OK; +#ifdef USE_LIBIDN2 + if(idn2_check_version(IDN2_VERSION)) { + int flags = IDN2_NFC_INPUT +#if IDN2_VERSION_NUMBER >= 0x00140000 + /* IDN2_NFC_INPUT: Normalize input string using normalization form C. + IDN2_NONTRANSITIONAL: Perform Unicode TR46 non-transitional + processing. */ + | IDN2_NONTRANSITIONAL +#endif + ; + int rc = IDN2_LOOKUP(input, &decoded, flags); + if(rc != IDN2_OK) + /* fallback to TR46 Transitional mode for better IDNA2003 + compatibility */ + rc = IDN2_LOOKUP(input, &decoded, IDN2_TRANSITIONAL); + if(rc != IDN2_OK) + result = CURLE_URL_MALFORMAT; + } + else + /* a too old libidn2 version */ + result = CURLE_NOT_BUILT_IN; +#elif defined(USE_WIN32_IDN) + result = win32_idn_to_ascii(input, &decoded); +#elif defined(USE_APPLE_IDN) + result = mac_idn_to_ascii(input, &decoded); +#endif + if(!result) + *output = decoded; + return result; +} + +static CURLcode idn_encode(const char *puny, char **output) +{ + char *enc = NULL; +#ifdef USE_LIBIDN2 + int rc = idn2_to_unicode_8z8z(puny, &enc, 0); + if(rc != IDNA_SUCCESS) + return rc == IDNA_MALLOC_ERROR ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT; +#elif defined(USE_WIN32_IDN) + CURLcode result = win32_ascii_to_idn(puny, &enc); + if(result) + return result; +#elif defined(USE_APPLE_IDN) + CURLcode result = mac_ascii_to_idn(puny, &enc); + if(result) + return result; +#endif + *output = enc; + return CURLE_OK; +} + +CURLcode Curl_idn_decode(const char *input, char **output) +{ + char *d = NULL; + CURLcode result = idn_decode(input, &d); +#ifdef USE_LIBIDN2 + if(!result) { + char *c = strdup(d); + idn2_free(d); + if(c) + d = c; + else + result = CURLE_OUT_OF_MEMORY; + } +#endif + if(!result) + *output = d; + return result; +} + +CURLcode Curl_idn_encode(const char *puny, char **output) +{ + char *d = NULL; + CURLcode result = idn_encode(puny, &d); +#ifdef USE_LIBIDN2 + if(!result) { + char *c = strdup(d); + idn2_free(d); + if(c) + d = c; + else + result = CURLE_OUT_OF_MEMORY; + } +#endif + if(!result) + *output = d; + return result; +} + +/* + * Frees data allocated by idnconvert_hostname() + */ +void Curl_free_idnconverted_hostname(struct hostname *host) +{ + Curl_safefree(host->encalloc); +} + +#endif /* USE_IDN */ + +/* + * Perform any necessary IDN conversion of hostname + */ +CURLcode Curl_idnconvert_hostname(struct hostname *host) +{ + /* set the name we use to display the hostname */ + host->dispname = host->name; + +#ifdef USE_IDN + /* Check name for non-ASCII and convert hostname if we can */ + if(!Curl_is_ASCII_name(host->name)) { + char *decoded; + CURLcode result = Curl_idn_decode(host->name, &decoded); + if(result) + return result; + /* successful */ + host->name = host->encalloc = decoded; + } +#endif + return CURLE_OK; +} diff --git a/third_party/curl/lib/idn.h b/third_party/curl/lib/idn.h new file mode 100644 index 0000000000..2bdce8927f --- /dev/null +++ b/third_party/curl/lib/idn.h @@ -0,0 +1,39 @@ +#ifndef HEADER_CURL_IDN_H +#define HEADER_CURL_IDN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +bool Curl_is_ASCII_name(const char *hostname); +CURLcode Curl_idnconvert_hostname(struct hostname *host); +#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) +#define USE_IDN +void Curl_free_idnconverted_hostname(struct hostname *host); +CURLcode Curl_idn_decode(const char *input, char **output); +CURLcode Curl_idn_encode(const char *input, char **output); + +#else +#define Curl_free_idnconverted_hostname(x) +#define Curl_idn_decode(x) NULL +#endif +#endif /* HEADER_CURL_IDN_H */ diff --git a/third_party/curl/lib/if2ip.c b/third_party/curl/lib/if2ip.c new file mode 100644 index 0000000000..42e14500bc --- /dev/null +++ b/third_party/curl/lib/if2ip.c @@ -0,0 +1,260 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_NET_IF_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_SYS_SOCKIO_H +# include +#endif +#ifdef HAVE_IFADDRS_H +# include +#endif +#ifdef HAVE_STROPTS_H +# include +#endif +#ifdef __VMS +# include +#endif + +#include "inet_ntop.h" +#include "strcase.h" +#include "if2ip.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* ------------------------------------------------------------------ */ + +#ifdef USE_IPV6 +/* Return the scope of the given address. */ +unsigned int Curl_ipv6_scope(const struct sockaddr *sa) +{ + if(sa->sa_family == AF_INET6) { + const struct sockaddr_in6 * sa6 = (const struct sockaddr_in6 *)(void *) sa; + const unsigned char *b = sa6->sin6_addr.s6_addr; + unsigned short w = (unsigned short) ((b[0] << 8) | b[1]); + + if((b[0] & 0xFE) == 0xFC) /* Handle ULAs */ + return IPV6_SCOPE_UNIQUELOCAL; + switch(w & 0xFFC0) { + case 0xFE80: + return IPV6_SCOPE_LINKLOCAL; + case 0xFEC0: + return IPV6_SCOPE_SITELOCAL; + case 0x0000: + w = b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] | b[8] | b[9] | + b[10] | b[11] | b[12] | b[13] | b[14]; + if(w || b[15] != 0x01) + break; + return IPV6_SCOPE_NODELOCAL; + default: + break; + } + } + return IPV6_SCOPE_GLOBAL; +} +#endif + +#ifndef CURL_DISABLE_BINDLOCAL + +#if defined(HAVE_GETIFADDRS) + +if2ip_result_t Curl_if2ip(int af, +#ifdef USE_IPV6 + unsigned int remote_scope, + unsigned int local_scope_id, +#endif + const char *interf, + char *buf, size_t buf_size) +{ + struct ifaddrs *iface, *head; + if2ip_result_t res = IF2IP_NOT_FOUND; + +#if defined(USE_IPV6) && \ + !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) + (void) local_scope_id; +#endif + + if(getifaddrs(&head) >= 0) { + for(iface = head; iface != NULL; iface = iface->ifa_next) { + if(iface->ifa_addr) { + if(iface->ifa_addr->sa_family == af) { + if(strcasecompare(iface->ifa_name, interf)) { + void *addr; + const char *ip; + char scope[12] = ""; + char ipstr[64]; +#ifdef USE_IPV6 + if(af == AF_INET6) { +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + unsigned int scopeid = 0; +#endif + unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr); + + if(ifscope != remote_scope) { + /* We are interested only in interface addresses whose scope + matches the remote address we want to connect to: global + for global, link-local for link-local, etc... */ + if(res == IF2IP_NOT_FOUND) + res = IF2IP_AF_NOT_SUPPORTED; + continue; + } + + addr = + &((struct sockaddr_in6 *)(void *)iface->ifa_addr)->sin6_addr; +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + /* Include the scope of this interface as part of the address */ + scopeid = ((struct sockaddr_in6 *)(void *)iface->ifa_addr) + ->sin6_scope_id; + + /* If given, scope id should match. */ + if(local_scope_id && scopeid != local_scope_id) { + if(res == IF2IP_NOT_FOUND) + res = IF2IP_AF_NOT_SUPPORTED; + + continue; + } + + if(scopeid) + msnprintf(scope, sizeof(scope), "%%%u", scopeid); +#endif + } + else +#endif + addr = + &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr; + res = IF2IP_FOUND; + ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr)); + msnprintf(buf, buf_size, "%s%s", ip, scope); + break; + } + } + else if((res == IF2IP_NOT_FOUND) && + strcasecompare(iface->ifa_name, interf)) { + res = IF2IP_AF_NOT_SUPPORTED; + } + } + } + + freeifaddrs(head); + } + + return res; +} + +#elif defined(HAVE_IOCTL_SIOCGIFADDR) + +if2ip_result_t Curl_if2ip(int af, +#ifdef USE_IPV6 + unsigned int remote_scope, + unsigned int local_scope_id, +#endif + const char *interf, + char *buf, size_t buf_size) +{ + struct ifreq req; + struct in_addr in; + struct sockaddr_in *s; + curl_socket_t dummy; + size_t len; + const char *r; + +#ifdef USE_IPV6 + (void)remote_scope; + (void)local_scope_id; +#endif + + if(!interf || (af != AF_INET)) + return IF2IP_NOT_FOUND; + + len = strlen(interf); + if(len >= sizeof(req.ifr_name)) + return IF2IP_NOT_FOUND; + + dummy = socket(AF_INET, SOCK_STREAM, 0); + if(CURL_SOCKET_BAD == dummy) + return IF2IP_NOT_FOUND; + + memset(&req, 0, sizeof(req)); + memcpy(req.ifr_name, interf, len + 1); + req.ifr_addr.sa_family = AF_INET; + + if(ioctl(dummy, SIOCGIFADDR, &req) < 0) { + sclose(dummy); + /* With SIOCGIFADDR, we cannot tell the difference between an interface + that does not exist and an interface that has no address of the + correct family. Assume the interface does not exist */ + return IF2IP_NOT_FOUND; + } + + s = (struct sockaddr_in *)(void *)&req.ifr_addr; + memcpy(&in, &s->sin_addr, sizeof(in)); + r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size); + + sclose(dummy); + if(!r) + return IF2IP_NOT_FOUND; + return IF2IP_FOUND; +} + +#else + +if2ip_result_t Curl_if2ip(int af, +#ifdef USE_IPV6 + unsigned int remote_scope, + unsigned int local_scope_id, +#endif + const char *interf, + char *buf, size_t buf_size) +{ + (void) af; +#ifdef USE_IPV6 + (void) remote_scope; + (void) local_scope_id; +#endif + (void) interf; + (void) buf; + (void) buf_size; + return IF2IP_NOT_FOUND; +} + +#endif + +#endif /* CURL_DISABLE_BINDLOCAL */ diff --git a/third_party/curl/lib/if2ip.h b/third_party/curl/lib/if2ip.h new file mode 100644 index 0000000000..f4b2f4c15d --- /dev/null +++ b/third_party/curl/lib/if2ip.h @@ -0,0 +1,92 @@ +#ifndef HEADER_CURL_IF2IP_H +#define HEADER_CURL_IF2IP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +/* IPv6 address scopes. */ +#define IPV6_SCOPE_GLOBAL 0 /* Global scope. */ +#define IPV6_SCOPE_LINKLOCAL 1 /* Link-local scope. */ +#define IPV6_SCOPE_SITELOCAL 2 /* Site-local scope (deprecated). */ +#define IPV6_SCOPE_UNIQUELOCAL 3 /* Unique local */ +#define IPV6_SCOPE_NODELOCAL 4 /* Loopback. */ + +#ifdef USE_IPV6 +unsigned int Curl_ipv6_scope(const struct sockaddr *sa); +#else +#define Curl_ipv6_scope(x) 0 +#endif + +typedef enum { + IF2IP_NOT_FOUND = 0, /* Interface not found */ + IF2IP_AF_NOT_SUPPORTED = 1, /* Int. exists but has no address for this af */ + IF2IP_FOUND = 2 /* The address has been stored in "buf" */ +} if2ip_result_t; + +if2ip_result_t Curl_if2ip(int af, +#ifdef USE_IPV6 + unsigned int remote_scope, + unsigned int local_scope_id, +#endif + const char *interf, + char *buf, size_t buf_size); + +#ifdef __INTERIX + +/* Nedelcho Stanev's work-around for SFU 3.0 */ +struct ifreq { +#define IFNAMSIZ 16 +#define IFHWADDRLEN 6 + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_metric; + int ifru_mtu; + } ifr_ifru; +}; + +/* This define was added by Daniel to avoid an extra #ifdef INTERIX in the + C code. */ + +#define ifr_name ifr_ifrn.ifrn_name /* interface name */ +#define ifr_addr ifr_ifru.ifru_addr /* address */ +#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ +#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ +#define ifr_metric ifr_ifru.ifru_metric /* metric */ +#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ + +#define SIOCGIFADDR _IOW('s', 102, struct ifreq) /* Get if addr */ + +#endif /* __INTERIX */ + +#endif /* HEADER_CURL_IF2IP_H */ diff --git a/third_party/curl/lib/imap.c b/third_party/curl/lib/imap.c new file mode 100644 index 0000000000..efe91b14b0 --- /dev/null +++ b/third_party/curl/lib/imap.c @@ -0,0 +1,2117 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC2195 CRAM-MD5 authentication + * RFC2595 Using TLS with IMAP, POP3 and ACAP + * RFC2831 DIGEST-MD5 authentication + * RFC3501 IMAPv4 protocol + * RFC4422 Simple Authentication and Security Layer (SASL) + * RFC4616 PLAIN authentication + * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism + * RFC4959 IMAP Extension for SASL Initial Client Response + * RFC5092 IMAP URL Scheme + * RFC6749 OAuth 2.0 Authorization Framework + * RFC8314 Use of TLS for Email Submission and Access + * Draft LOGIN SASL Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_IMAP + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "escape.h" +#include "http.h" /* for HTTP proxy tunnel stuff */ +#include "socks.h" +#include "imap.h" +#include "mime.h" +#include "strtoofft.h" +#include "strcase.h" +#include "vtls/vtls.h" +#include "cfilters.h" +#include "connect.h" +#include "select.h" +#include "multiif.h" +#include "url.h" +#include "bufref.h" +#include "curl_sasl.h" +#include "warnless.h" +#include "curl_ctype.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* Local API functions */ +static CURLcode imap_regular_transfer(struct Curl_easy *data, bool *done); +static CURLcode imap_do(struct Curl_easy *data, bool *done); +static CURLcode imap_done(struct Curl_easy *data, CURLcode status, + bool premature); +static CURLcode imap_connect(struct Curl_easy *data, bool *done); +static CURLcode imap_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead); +static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done); +static int imap_getsock(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t *socks); +static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done); +static CURLcode imap_setup_connection(struct Curl_easy *data, + struct connectdata *conn); +static char *imap_atom(const char *str, bool escape_only); +static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...) + CURL_PRINTF(2, 3); +static CURLcode imap_parse_url_options(struct connectdata *conn); +static CURLcode imap_parse_url_path(struct Curl_easy *data); +static CURLcode imap_parse_custom_request(struct Curl_easy *data); +static CURLcode imap_perform_authenticate(struct Curl_easy *data, + const char *mech, + const struct bufref *initresp); +static CURLcode imap_continue_authenticate(struct Curl_easy *data, + const char *mech, + const struct bufref *resp); +static CURLcode imap_cancel_authenticate(struct Curl_easy *data, + const char *mech); +static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out); + +/* + * IMAP protocol handler. + */ + +const struct Curl_handler Curl_handler_imap = { + "imap", /* scheme */ + imap_setup_connection, /* setup_connection */ + imap_do, /* do_it */ + imap_done, /* done */ + ZERO_NULL, /* do_more */ + imap_connect, /* connect_it */ + imap_multi_statemach, /* connecting */ + imap_doing, /* doing */ + imap_getsock, /* proto_getsock */ + imap_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + imap_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_IMAP, /* defport */ + CURLPROTO_IMAP, /* protocol */ + CURLPROTO_IMAP, /* family */ + PROTOPT_CLOSEACTION| /* flags */ + PROTOPT_URLOPTIONS +}; + +#ifdef USE_SSL +/* + * IMAPS protocol handler. + */ + +const struct Curl_handler Curl_handler_imaps = { + "imaps", /* scheme */ + imap_setup_connection, /* setup_connection */ + imap_do, /* do_it */ + imap_done, /* done */ + ZERO_NULL, /* do_more */ + imap_connect, /* connect_it */ + imap_multi_statemach, /* connecting */ + imap_doing, /* doing */ + imap_getsock, /* proto_getsock */ + imap_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + imap_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_IMAPS, /* defport */ + CURLPROTO_IMAPS, /* protocol */ + CURLPROTO_IMAP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_URLOPTIONS +}; +#endif + +#define IMAP_RESP_OK 1 +#define IMAP_RESP_NOT_OK 2 +#define IMAP_RESP_PREAUTH 3 + +/* SASL parameters for the imap protocol */ +static const struct SASLproto saslimap = { + "imap", /* The service name */ + imap_perform_authenticate, /* Send authentication command */ + imap_continue_authenticate, /* Send authentication continuation */ + imap_cancel_authenticate, /* Send authentication cancellation */ + imap_get_message, /* Get SASL response message */ + 0, /* No maximum initial response length */ + '+', /* Code received when continuation is expected */ + IMAP_RESP_OK, /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ +}; + + +#ifdef USE_SSL +static void imap_to_imaps(struct connectdata *conn) +{ + /* Change the connection handler */ + conn->handler = &Curl_handler_imaps; + + /* Set the connection's upgraded to TLS flag */ + conn->bits.tls_upgraded = TRUE; +} +#else +#define imap_to_imaps(x) Curl_nop_stmt +#endif + +/*********************************************************************** + * + * imap_matchresp() + * + * Determines whether the untagged response is related to the specified + * command by checking if it is in format "* ..." or + * "* ...". + * + * The "* " marker is assumed to have already been checked by the caller. + */ +static bool imap_matchresp(const char *line, size_t len, const char *cmd) +{ + const char *end = line + len; + size_t cmd_len = strlen(cmd); + + /* Skip the untagged response marker */ + line += 2; + + /* Do we have a number after the marker? */ + if(line < end && ISDIGIT(*line)) { + /* Skip the number */ + do + line++; + while(line < end && ISDIGIT(*line)); + + /* Do we have the space character? */ + if(line == end || *line != ' ') + return FALSE; + + line++; + } + + /* Does the command name match and is it followed by a space character or at + the end of line? */ + if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) && + (line[cmd_len] == ' ' || line + cmd_len + 2 == end)) + return TRUE; + + return FALSE; +} + +/*********************************************************************** + * + * imap_endofresp() + * + * Checks whether the given string is a valid tagged, untagged or continuation + * response which can be processed by the response handler. + */ +static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn, + char *line, size_t len, int *resp) +{ + struct IMAP *imap = data->req.p.imap; + struct imap_conn *imapc = &conn->proto.imapc; + const char *id = imapc->resptag; + size_t id_len = strlen(id); + + /* Do we have a tagged command response? */ + if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') { + line += id_len + 1; + len -= id_len + 1; + + if(len >= 2 && !memcmp(line, "OK", 2)) + *resp = IMAP_RESP_OK; + else if(len >= 7 && !memcmp(line, "PREAUTH", 7)) + *resp = IMAP_RESP_PREAUTH; + else + *resp = IMAP_RESP_NOT_OK; + + return TRUE; + } + + /* Do we have an untagged command response? */ + if(len >= 2 && !memcmp("* ", line, 2)) { + switch(imapc->state) { + /* States which are interested in untagged responses */ + case IMAP_CAPABILITY: + if(!imap_matchresp(line, len, "CAPABILITY")) + return FALSE; + break; + + case IMAP_LIST: + if((!imap->custom && !imap_matchresp(line, len, "LIST")) || + (imap->custom && !imap_matchresp(line, len, imap->custom) && + (!strcasecompare(imap->custom, "STORE") || + !imap_matchresp(line, len, "FETCH")) && + !strcasecompare(imap->custom, "SELECT") && + !strcasecompare(imap->custom, "EXAMINE") && + !strcasecompare(imap->custom, "SEARCH") && + !strcasecompare(imap->custom, "EXPUNGE") && + !strcasecompare(imap->custom, "LSUB") && + !strcasecompare(imap->custom, "UID") && + !strcasecompare(imap->custom, "GETQUOTAROOT") && + !strcasecompare(imap->custom, "NOOP"))) + return FALSE; + break; + + case IMAP_SELECT: + /* SELECT is special in that its untagged responses do not have a + common prefix so accept anything! */ + break; + + case IMAP_FETCH: + if(!imap_matchresp(line, len, "FETCH")) + return FALSE; + break; + + case IMAP_SEARCH: + if(!imap_matchresp(line, len, "SEARCH")) + return FALSE; + break; + + /* Ignore other untagged responses */ + default: + return FALSE; + } + + *resp = '*'; + return TRUE; + } + + /* Do we have a continuation response? This should be a + symbol followed by + a space and optionally some text as per RFC-3501 for the AUTHENTICATE and + APPEND commands and as outlined in Section 4. Examples of RFC-4959 but + some email servers ignore this and only send a single + instead. */ + if(imap && !imap->custom && ((len == 3 && line[0] == '+') || + (len >= 2 && !memcmp("+ ", line, 2)))) { + switch(imapc->state) { + /* States which are interested in continuation responses */ + case IMAP_AUTHENTICATE: + case IMAP_APPEND: + *resp = '+'; + break; + + default: + failf(data, "Unexpected continuation response"); + *resp = -1; + break; + } + + return TRUE; + } + + return FALSE; /* Nothing for us */ +} + +/*********************************************************************** + * + * imap_get_message() + * + * Gets the authentication message from the response buffer. + */ +static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out) +{ + char *message = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf); + size_t len = data->conn->proto.imapc.pp.nfinal; + + if(len > 2) { + /* Find the start of the message */ + len -= 2; + for(message += 2; *message == ' ' || *message == '\t'; message++, len--) + ; + + /* Find the end of the message */ + while(len--) + if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && + message[len] != '\t') + break; + + /* Terminate the message */ + message[++len] = '\0'; + Curl_bufref_set(out, message, len, NULL); + } + else + /* junk input => zero length output */ + Curl_bufref_set(out, "", 0, NULL); + + return CURLE_OK; +} + +/*********************************************************************** + * + * imap_state() + * + * This is the ONLY way to change IMAP state! + */ +static void imap_state(struct Curl_easy *data, imapstate newstate) +{ + struct imap_conn *imapc = &data->conn->proto.imapc; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char * const names[]={ + "STOP", + "SERVERGREET", + "CAPABILITY", + "STARTTLS", + "UPGRADETLS", + "AUTHENTICATE", + "LOGIN", + "LIST", + "SELECT", + "FETCH", + "FETCH_FINAL", + "APPEND", + "APPEND_FINAL", + "SEARCH", + "LOGOUT", + /* LAST */ + }; + + if(imapc->state != newstate) + infof(data, "IMAP %p state change from %s to %s", + (void *)imapc, names[imapc->state], names[newstate]); +#endif + + imapc->state = newstate; +} + +/*********************************************************************** + * + * imap_perform_capability() + * + * Sends the CAPABILITY command in order to obtain a list of server side + * supported capabilities. + */ +static CURLcode imap_perform_capability(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct imap_conn *imapc = &conn->proto.imapc; + imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */ + imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */ + imapc->tls_supported = FALSE; /* Clear the TLS capability */ + + /* Send the CAPABILITY command */ + result = imap_sendf(data, "CAPABILITY"); + + if(!result) + imap_state(data, IMAP_CAPABILITY); + + return result; +} + +/*********************************************************************** + * + * imap_perform_starttls() + * + * Sends the STARTTLS command to start the upgrade to TLS. + */ +static CURLcode imap_perform_starttls(struct Curl_easy *data) +{ + /* Send the STARTTLS command */ + CURLcode result = imap_sendf(data, "STARTTLS"); + + if(!result) + imap_state(data, IMAP_STARTTLS); + + return result; +} + +/*********************************************************************** + * + * imap_perform_upgrade_tls() + * + * Performs the upgrade to TLS. + */ +static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Start the SSL connection */ + struct imap_conn *imapc = &conn->proto.imapc; + CURLcode result; + bool ssldone = FALSE; + + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + if(result) + goto out; + } + + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + if(!result) { + imapc->ssldone = ssldone; + if(imapc->state != IMAP_UPGRADETLS) + imap_state(data, IMAP_UPGRADETLS); + + if(imapc->ssldone) { + imap_to_imaps(conn); + result = imap_perform_capability(data, conn); + } + } +out: + return result; +} + +/*********************************************************************** + * + * imap_perform_login() + * + * Sends a clear text LOGIN command to authenticate with. + */ +static CURLcode imap_perform_login(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + char *user; + char *passwd; + + /* Check we have a username and password to authenticate with and end the + connect phase if we do not */ + if(!data->state.aptr.user) { + imap_state(data, IMAP_STOP); + + return result; + } + + /* Make sure the username and password are in the correct atom format */ + user = imap_atom(conn->user, false); + passwd = imap_atom(conn->passwd, false); + + /* Send the LOGIN command */ + result = imap_sendf(data, "LOGIN %s %s", user ? user : "", + passwd ? passwd : ""); + + free(user); + free(passwd); + + if(!result) + imap_state(data, IMAP_LOGIN); + + return result; +} + +/*********************************************************************** + * + * imap_perform_authenticate() + * + * Sends an AUTHENTICATE command allowing the client to login with the given + * SASL authentication mechanism. + */ +static CURLcode imap_perform_authenticate(struct Curl_easy *data, + const char *mech, + const struct bufref *initresp) +{ + CURLcode result = CURLE_OK; + const char *ir = (const char *) Curl_bufref_ptr(initresp); + + if(ir) { + /* Send the AUTHENTICATE command with the initial response */ + result = imap_sendf(data, "AUTHENTICATE %s %s", mech, ir); + } + else { + /* Send the AUTHENTICATE command */ + result = imap_sendf(data, "AUTHENTICATE %s", mech); + } + + return result; +} + +/*********************************************************************** + * + * imap_continue_authenticate() + * + * Sends SASL continuation data. + */ +static CURLcode imap_continue_authenticate(struct Curl_easy *data, + const char *mech, + const struct bufref *resp) +{ + struct imap_conn *imapc = &data->conn->proto.imapc; + + (void)mech; + + return Curl_pp_sendf(data, &imapc->pp, + "%s", (const char *) Curl_bufref_ptr(resp)); +} + +/*********************************************************************** + * + * imap_cancel_authenticate() + * + * Sends SASL cancellation. + */ +static CURLcode imap_cancel_authenticate(struct Curl_easy *data, + const char *mech) +{ + struct imap_conn *imapc = &data->conn->proto.imapc; + + (void)mech; + + return Curl_pp_sendf(data, &imapc->pp, "*"); +} + +/*********************************************************************** + * + * imap_perform_authentication() + * + * Initiates the authentication sequence, with the appropriate SASL + * authentication mechanism, falling back to clear text should a common + * mechanism not be available between the client and server. + */ +static CURLcode imap_perform_authentication(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct imap_conn *imapc = &conn->proto.imapc; + saslprogress progress; + + /* Check if already authenticated OR if there is enough data to authenticate + with and end the connect phase if we do not */ + if(imapc->preauth || + !Curl_sasl_can_authenticate(&imapc->sasl, data)) { + imap_state(data, IMAP_STOP); + return result; + } + + /* Calculate the SASL login details */ + result = Curl_sasl_start(&imapc->sasl, data, imapc->ir_supported, &progress); + + if(!result) { + if(progress == SASL_INPROGRESS) + imap_state(data, IMAP_AUTHENTICATE); + else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) + /* Perform clear text authentication */ + result = imap_perform_login(data, conn); + else { + /* Other mechanisms not supported */ + infof(data, "No known authentication mechanisms supported"); + result = CURLE_LOGIN_DENIED; + } + } + + return result; +} + +/*********************************************************************** + * + * imap_perform_list() + * + * Sends a LIST command or an alternative custom request. + */ +static CURLcode imap_perform_list(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct IMAP *imap = data->req.p.imap; + + if(imap->custom) + /* Send the custom request */ + result = imap_sendf(data, "%s%s", imap->custom, + imap->custom_params ? imap->custom_params : ""); + else { + /* Make sure the mailbox is in the correct atom format if necessary */ + char *mailbox = imap->mailbox ? imap_atom(imap->mailbox, true) + : strdup(""); + if(!mailbox) + return CURLE_OUT_OF_MEMORY; + + /* Send the LIST command */ + result = imap_sendf(data, "LIST \"%s\" *", mailbox); + + free(mailbox); + } + + if(!result) + imap_state(data, IMAP_LIST); + + return result; +} + +/*********************************************************************** + * + * imap_perform_select() + * + * Sends a SELECT command to ask the server to change the selected mailbox. + */ +static CURLcode imap_perform_select(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct IMAP *imap = data->req.p.imap; + struct imap_conn *imapc = &conn->proto.imapc; + char *mailbox; + + /* Invalidate old information as we are switching mailboxes */ + Curl_safefree(imapc->mailbox); + Curl_safefree(imapc->mailbox_uidvalidity); + + /* Check we have a mailbox */ + if(!imap->mailbox) { + failf(data, "Cannot SELECT without a mailbox."); + return CURLE_URL_MALFORMAT; + } + + /* Make sure the mailbox is in the correct atom format */ + mailbox = imap_atom(imap->mailbox, false); + if(!mailbox) + return CURLE_OUT_OF_MEMORY; + + /* Send the SELECT command */ + result = imap_sendf(data, "SELECT %s", mailbox); + + free(mailbox); + + if(!result) + imap_state(data, IMAP_SELECT); + + return result; +} + +/*********************************************************************** + * + * imap_perform_fetch() + * + * Sends a FETCH command to initiate the download of a message. + */ +static CURLcode imap_perform_fetch(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct IMAP *imap = data->req.p.imap; + /* Check we have a UID */ + if(imap->uid) { + + /* Send the FETCH command */ + if(imap->partial) + result = imap_sendf(data, "UID FETCH %s BODY[%s]<%s>", + imap->uid, imap->section ? imap->section : "", + imap->partial); + else + result = imap_sendf(data, "UID FETCH %s BODY[%s]", + imap->uid, imap->section ? imap->section : ""); + } + else if(imap->mindex) { + /* Send the FETCH command */ + if(imap->partial) + result = imap_sendf(data, "FETCH %s BODY[%s]<%s>", + imap->mindex, imap->section ? imap->section : "", + imap->partial); + else + result = imap_sendf(data, "FETCH %s BODY[%s]", + imap->mindex, imap->section ? imap->section : ""); + } + else { + failf(data, "Cannot FETCH without a UID."); + return CURLE_URL_MALFORMAT; + } + if(!result) + imap_state(data, IMAP_FETCH); + + return result; +} + +/*********************************************************************** + * + * imap_perform_append() + * + * Sends an APPEND command to initiate the upload of a message. + */ +static CURLcode imap_perform_append(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct IMAP *imap = data->req.p.imap; + char *mailbox; + + /* Check we have a mailbox */ + if(!imap->mailbox) { + failf(data, "Cannot APPEND without a mailbox."); + return CURLE_URL_MALFORMAT; + } + +#ifndef CURL_DISABLE_MIME + /* Prepare the mime data if some. */ + if(data->set.mimepost.kind != MIMEKIND_NONE) { + /* Use the whole structure as data. */ + data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY; + + /* Add external headers and mime version. */ + curl_mime_headers(&data->set.mimepost, data->set.headers, 0); + result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL, + NULL, MIMESTRATEGY_MAIL); + + if(!result) + if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) + result = Curl_mime_add_header(&data->set.mimepost.curlheaders, + "Mime-Version: 1.0"); + + if(!result) + result = Curl_creader_set_mime(data, &data->set.mimepost); + if(result) + return result; + data->state.infilesize = Curl_creader_client_length(data); + } + else +#endif + { + result = Curl_creader_set_fread(data, data->state.infilesize); + if(result) + return result; + } + + /* Check we know the size of the upload */ + if(data->state.infilesize < 0) { + failf(data, "Cannot APPEND with unknown input file size"); + return CURLE_UPLOAD_FAILED; + } + + /* Make sure the mailbox is in the correct atom format */ + mailbox = imap_atom(imap->mailbox, false); + if(!mailbox) + return CURLE_OUT_OF_MEMORY; + + /* Send the APPEND command */ + result = imap_sendf(data, + "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}", + mailbox, data->state.infilesize); + + free(mailbox); + + if(!result) + imap_state(data, IMAP_APPEND); + + return result; +} + +/*********************************************************************** + * + * imap_perform_search() + * + * Sends a SEARCH command. + */ +static CURLcode imap_perform_search(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct IMAP *imap = data->req.p.imap; + + /* Check we have a query string */ + if(!imap->query) { + failf(data, "Cannot SEARCH without a query string."); + return CURLE_URL_MALFORMAT; + } + + /* Send the SEARCH command */ + result = imap_sendf(data, "SEARCH %s", imap->query); + + if(!result) + imap_state(data, IMAP_SEARCH); + + return result; +} + +/*********************************************************************** + * + * imap_perform_logout() + * + * Performs the logout action prior to sclose() being called. + */ +static CURLcode imap_perform_logout(struct Curl_easy *data) +{ + /* Send the LOGOUT command */ + CURLcode result = imap_sendf(data, "LOGOUT"); + + if(!result) + imap_state(data, IMAP_LOGOUT); + + return result; +} + +/* For the initial server greeting */ +static CURLcode imap_state_servergreet_resp(struct Curl_easy *data, + int imapcode, + imapstate instate) +{ + struct connectdata *conn = data->conn; + (void)instate; /* no use for this yet */ + + if(imapcode == IMAP_RESP_PREAUTH) { + /* PREAUTH */ + struct imap_conn *imapc = &conn->proto.imapc; + imapc->preauth = TRUE; + infof(data, "PREAUTH connection, already authenticated"); + } + else if(imapcode != IMAP_RESP_OK) { + failf(data, "Got unexpected imap-server response"); + return CURLE_WEIRD_SERVER_REPLY; + } + + return imap_perform_capability(data, conn); +} + +/* For CAPABILITY responses */ +static CURLcode imap_state_capability_resp(struct Curl_easy *data, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct imap_conn *imapc = &conn->proto.imapc; + const char *line = Curl_dyn_ptr(&imapc->pp.recvbuf); + + (void)instate; /* no use for this yet */ + + /* Do we have a untagged response? */ + if(imapcode == '*') { + line += 2; + + /* Loop through the data line */ + for(;;) { + size_t wordlen; + while(*line && + (*line == ' ' || *line == '\t' || + *line == '\r' || *line == '\n')) { + + line++; + } + + if(!*line) + break; + + /* Extract the word */ + for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' && + line[wordlen] != '\t' && line[wordlen] != '\r' && + line[wordlen] != '\n';) + wordlen++; + + /* Does the server support the STARTTLS capability? */ + if(wordlen == 8 && !memcmp(line, "STARTTLS", 8)) + imapc->tls_supported = TRUE; + + /* Has the server explicitly disabled clear text authentication? */ + else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13)) + imapc->login_disabled = TRUE; + + /* Does the server support the SASL-IR capability? */ + else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7)) + imapc->ir_supported = TRUE; + + /* Do we have a SASL based authentication mechanism? */ + else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) { + size_t llen; + unsigned short mechbit; + + line += 5; + wordlen -= 5; + + /* Test the word for a matching authentication mechanism */ + mechbit = Curl_sasl_decode_mech(line, wordlen, &llen); + if(mechbit && llen == wordlen) + imapc->sasl.authmechs |= mechbit; + } + + line += wordlen; + } + } + else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + /* PREAUTH is not compatible with STARTTLS. */ + if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { + /* Switch to TLS connection now */ + result = imap_perform_starttls(data); + } + else if(data->set.use_ssl <= CURLUSESSL_TRY) + result = imap_perform_authentication(data, conn); + else { + failf(data, "STARTTLS not available."); + result = CURLE_USE_SSL_FAILED; + } + } + else + result = imap_perform_authentication(data, conn); + + return result; +} + +/* For STARTTLS responses */ +static CURLcode imap_state_starttls_resp(struct Curl_easy *data, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + (void)instate; /* no use for this yet */ + + /* Pipelining in response is forbidden. */ + if(data->conn->proto.imapc.pp.overflow) + return CURLE_WEIRD_SERVER_REPLY; + + if(imapcode != IMAP_RESP_OK) { + if(data->set.use_ssl != CURLUSESSL_TRY) { + failf(data, "STARTTLS denied"); + result = CURLE_USE_SSL_FAILED; + } + else + result = imap_perform_authentication(data, conn); + } + else + result = imap_perform_upgrade_tls(data, conn); + + return result; +} + +/* For SASL authentication responses */ +static CURLcode imap_state_auth_resp(struct Curl_easy *data, + struct connectdata *conn, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + struct imap_conn *imapc = &conn->proto.imapc; + saslprogress progress; + + (void)instate; /* no use for this yet */ + + result = Curl_sasl_continue(&imapc->sasl, data, imapcode, &progress); + if(!result) + switch(progress) { + case SASL_DONE: + imap_state(data, IMAP_STOP); /* Authenticated */ + break; + case SASL_IDLE: /* No mechanism left after cancellation */ + if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) + /* Perform clear text authentication */ + result = imap_perform_login(data, conn); + else { + failf(data, "Authentication cancelled"); + result = CURLE_LOGIN_DENIED; + } + break; + default: + break; + } + + return result; +} + +/* For LOGIN responses */ +static CURLcode imap_state_login_resp(struct Curl_easy *data, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + (void)instate; /* no use for this yet */ + + if(imapcode != IMAP_RESP_OK) { + failf(data, "Access denied. %c", imapcode); + result = CURLE_LOGIN_DENIED; + } + else + /* End of connect phase */ + imap_state(data, IMAP_STOP); + + return result; +} + +/* For LIST and SEARCH responses */ +static CURLcode imap_state_listsearch_resp(struct Curl_easy *data, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf); + size_t len = data->conn->proto.imapc.pp.nfinal; + + (void)instate; /* No use for this yet */ + + if(imapcode == '*') + result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); + else if(imapcode != IMAP_RESP_OK) + result = CURLE_QUOTE_ERROR; + else + /* End of DO phase */ + imap_state(data, IMAP_STOP); + + return result; +} + +/* For SELECT responses */ +static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct IMAP *imap = data->req.p.imap; + struct imap_conn *imapc = &conn->proto.imapc; + const char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf); + + (void)instate; /* no use for this yet */ + + if(imapcode == '*') { + /* See if this is an UIDVALIDITY response */ + if(checkprefix("OK [UIDVALIDITY ", line + 2)) { + size_t len = 0; + const char *p = &line[2] + strlen("OK [UIDVALIDITY "); + while((len < 20) && p[len] && ISDIGIT(p[len])) + len++; + if(len && (p[len] == ']')) { + struct dynbuf uid; + Curl_dyn_init(&uid, 20); + if(Curl_dyn_addn(&uid, p, len)) + return CURLE_OUT_OF_MEMORY; + Curl_safefree(imapc->mailbox_uidvalidity); + imapc->mailbox_uidvalidity = Curl_dyn_ptr(&uid); + } + } + } + else if(imapcode == IMAP_RESP_OK) { + /* Check if the UIDVALIDITY has been specified and matches */ + if(imap->uidvalidity && imapc->mailbox_uidvalidity && + !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) { + failf(data, "Mailbox UIDVALIDITY has changed"); + result = CURLE_REMOTE_FILE_NOT_FOUND; + } + else { + /* Note the currently opened mailbox on this connection */ + DEBUGASSERT(!imapc->mailbox); + imapc->mailbox = strdup(imap->mailbox); + if(!imapc->mailbox) + return CURLE_OUT_OF_MEMORY; + + if(imap->custom) + result = imap_perform_list(data); + else if(imap->query) + result = imap_perform_search(data); + else + result = imap_perform_fetch(data); + } + } + else { + failf(data, "Select failed"); + result = CURLE_LOGIN_DENIED; + } + + return result; +} + +/* For the (first line of the) FETCH responses */ +static CURLcode imap_state_fetch_resp(struct Curl_easy *data, + struct connectdata *conn, int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + struct imap_conn *imapc = &conn->proto.imapc; + struct pingpong *pp = &imapc->pp; + const char *ptr = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf); + size_t len = data->conn->proto.imapc.pp.nfinal; + bool parsed = FALSE; + curl_off_t size = 0; + + (void)instate; /* no use for this yet */ + + if(imapcode != '*') { + Curl_pgrsSetDownloadSize(data, -1); + imap_state(data, IMAP_STOP); + return CURLE_REMOTE_FILE_NOT_FOUND; + } + + /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse + the continuation data contained within the curly brackets */ + ptr = memchr(ptr, '{', len); + if(ptr) { + char *endptr; + if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size) && + (endptr - ptr > 1 && *endptr == '}')) + parsed = TRUE; + } + + if(parsed) { + infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download", + size); + Curl_pgrsSetDownloadSize(data, size); + + if(pp->overflow) { + /* At this point there is a data in the receive buffer that is body + content, send it as body and then skip it. Do note that there may + even be additional "headers" after the body. */ + size_t chunk = pp->overflow; + + /* keep only the overflow */ + Curl_dyn_tail(&pp->recvbuf, chunk); + pp->nfinal = 0; /* done */ + + if(chunk > (size_t)size) + /* The conversion from curl_off_t to size_t is always fine here */ + chunk = (size_t)size; + + if(!chunk) { + /* no size, we are done with the data */ + imap_state(data, IMAP_STOP); + return CURLE_OK; + } + result = Curl_client_write(data, CLIENTWRITE_BODY, + Curl_dyn_ptr(&pp->recvbuf), chunk); + if(result) + return result; + + infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU + " bytes are left for transfer", chunk, size - chunk); + + /* Have we used the entire overflow or just part of it?*/ + if(pp->overflow > chunk) { + /* remember the remaining trailing overflow data */ + pp->overflow -= chunk; + Curl_dyn_tail(&pp->recvbuf, pp->overflow); + } + else { + pp->overflow = 0; /* handled */ + /* Free the cache */ + Curl_dyn_reset(&pp->recvbuf); + } + } + + if(data->req.bytecount == size) + /* The entire data is already transferred! */ + Curl_xfer_setup_nop(data); + else { + /* IMAP download */ + data->req.maxdownload = size; + /* force a recv/send check of this connection, as the data might've been + read off the socket already */ + data->state.select_bits = CURL_CSELECT_IN; + Curl_xfer_setup1(data, CURL_XFER_RECV, size, FALSE); + } + } + else { + /* We do not know how to parse this line */ + failf(data, "Failed to parse FETCH response."); + result = CURLE_WEIRD_SERVER_REPLY; + } + + /* End of DO phase */ + imap_state(data, IMAP_STOP); + + return result; +} + +/* For final FETCH responses performed after the download */ +static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + + (void)instate; /* No use for this yet */ + + if(imapcode != IMAP_RESP_OK) + result = CURLE_WEIRD_SERVER_REPLY; + else + /* End of DONE phase */ + imap_state(data, IMAP_STOP); + + return result; +} + +/* For APPEND responses */ +static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + (void)instate; /* No use for this yet */ + + if(imapcode != '+') { + result = CURLE_UPLOAD_FAILED; + } + else { + /* Set the progress upload size */ + Curl_pgrsSetUploadSize(data, data->state.infilesize); + + /* IMAP upload */ + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); + + /* End of DO phase */ + imap_state(data, IMAP_STOP); + } + + return result; +} + +/* For final APPEND responses performed after the upload */ +static CURLcode imap_state_append_final_resp(struct Curl_easy *data, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + + (void)instate; /* No use for this yet */ + + if(imapcode != IMAP_RESP_OK) + result = CURLE_UPLOAD_FAILED; + else + /* End of DONE phase */ + imap_state(data, IMAP_STOP); + + return result; +} + +static CURLcode imap_statemachine(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + int imapcode; + struct imap_conn *imapc = &conn->proto.imapc; + struct pingpong *pp = &imapc->pp; + size_t nread = 0; + (void)data; + + /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */ + if(imapc->state == IMAP_UPGRADETLS) + return imap_perform_upgrade_tls(data, conn); + + /* Flush any data that needs to be sent */ + if(pp->sendleft) + return Curl_pp_flushsend(data, pp); + + do { + /* Read the response from the server */ + result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &imapcode, &nread); + if(result) + return result; + + /* Was there an error parsing the response line? */ + if(imapcode == -1) + return CURLE_WEIRD_SERVER_REPLY; + + if(!imapcode) + break; + + /* We have now received a full IMAP server response */ + switch(imapc->state) { + case IMAP_SERVERGREET: + result = imap_state_servergreet_resp(data, imapcode, imapc->state); + break; + + case IMAP_CAPABILITY: + result = imap_state_capability_resp(data, imapcode, imapc->state); + break; + + case IMAP_STARTTLS: + result = imap_state_starttls_resp(data, imapcode, imapc->state); + break; + + case IMAP_AUTHENTICATE: + result = imap_state_auth_resp(data, conn, imapcode, imapc->state); + break; + + case IMAP_LOGIN: + result = imap_state_login_resp(data, imapcode, imapc->state); + break; + + case IMAP_LIST: + case IMAP_SEARCH: + result = imap_state_listsearch_resp(data, imapcode, imapc->state); + break; + + case IMAP_SELECT: + result = imap_state_select_resp(data, imapcode, imapc->state); + break; + + case IMAP_FETCH: + result = imap_state_fetch_resp(data, conn, imapcode, imapc->state); + break; + + case IMAP_FETCH_FINAL: + result = imap_state_fetch_final_resp(data, imapcode, imapc->state); + break; + + case IMAP_APPEND: + result = imap_state_append_resp(data, imapcode, imapc->state); + break; + + case IMAP_APPEND_FINAL: + result = imap_state_append_final_resp(data, imapcode, imapc->state); + break; + + case IMAP_LOGOUT: + default: + /* internal error */ + imap_state(data, IMAP_STOP); + break; + } + } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp)); + + return result; +} + +/* Called repeatedly until done from multi.c */ +static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct imap_conn *imapc = &conn->proto.imapc; + + if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) { + bool ssldone = FALSE; + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + imapc->ssldone = ssldone; + if(result || !ssldone) + return result; + } + + result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE); + *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE; + + return result; +} + +static CURLcode imap_block_statemach(struct Curl_easy *data, + struct connectdata *conn, + bool disconnecting) +{ + CURLcode result = CURLE_OK; + struct imap_conn *imapc = &conn->proto.imapc; + + while(imapc->state != IMAP_STOP && !result) + result = Curl_pp_statemach(data, &imapc->pp, TRUE, disconnecting); + + return result; +} + +/* Allocate and initialize the struct IMAP for the current Curl_easy if + required */ +static CURLcode imap_init(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct IMAP *imap; + + imap = data->req.p.imap = calloc(1, sizeof(struct IMAP)); + if(!imap) + result = CURLE_OUT_OF_MEMORY; + + return result; +} + +/* For the IMAP "protocol connect" and "doing" phases only */ +static int imap_getsock(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *socks) +{ + return Curl_pp_getsock(data, &conn->proto.imapc.pp, socks); +} + +/*********************************************************************** + * + * imap_connect() + * + * This function should do everything that is to be considered a part of the + * connection phase. + * + * The variable 'done' points to will be TRUE if the protocol-layer connect + * phase is done when this function returns, or FALSE if not. + */ +static CURLcode imap_connect(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct imap_conn *imapc = &conn->proto.imapc; + struct pingpong *pp = &imapc->pp; + + *done = FALSE; /* default to not done yet */ + + /* We always support persistent connections in IMAP */ + connkeep(conn, "IMAP default"); + + PINGPONG_SETUP(pp, imap_statemachine, imap_endofresp); + + /* Set the default preferred authentication type and mechanism */ + imapc->preftype = IMAP_TYPE_ANY; + Curl_sasl_init(&imapc->sasl, data, &saslimap); + + Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD); + Curl_pp_init(pp); + + /* Parse the URL options */ + result = imap_parse_url_options(conn); + if(result) + return result; + + /* Start off waiting for the server greeting response */ + imap_state(data, IMAP_SERVERGREET); + + /* Start off with an response id of '*' */ + strcpy(imapc->resptag, "*"); + + result = imap_multi_statemach(data, done); + + return result; +} + +/*********************************************************************** + * + * imap_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +static CURLcode imap_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct IMAP *imap = data->req.p.imap; + + (void)premature; + + if(!imap) + return CURLE_OK; + + if(status) { + connclose(conn, "IMAP done with bad status"); /* marked for closure */ + result = status; /* use the already set error code */ + } + else if(!data->set.connect_only && !imap->custom && + (imap->uid || imap->mindex || data->state.upload || + IS_MIME_POST(data))) { + /* Handle responses after FETCH or APPEND transfer has finished */ + + if(!data->state.upload && !IS_MIME_POST(data)) + imap_state(data, IMAP_FETCH_FINAL); + else { + /* End the APPEND command first by sending an empty line */ + result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", ""); + if(!result) + imap_state(data, IMAP_APPEND_FINAL); + } + + /* Run the state-machine */ + if(!result) + result = imap_block_statemach(data, conn, FALSE); + } + + /* Cleanup our per-request based variables */ + Curl_safefree(imap->mailbox); + Curl_safefree(imap->uidvalidity); + Curl_safefree(imap->uid); + Curl_safefree(imap->mindex); + Curl_safefree(imap->section); + Curl_safefree(imap->partial); + Curl_safefree(imap->query); + Curl_safefree(imap->custom); + Curl_safefree(imap->custom_params); + + /* Clear the transfer mode for the next request */ + imap->transfer = PPTRANSFER_BODY; + + return result; +} + +/*********************************************************************** + * + * imap_perform() + * + * This is the actual DO function for IMAP. Fetch or append a message, or do + * other things according to the options previously setup. + */ +static CURLcode imap_perform(struct Curl_easy *data, bool *connected, + bool *dophase_done) +{ + /* This is IMAP and no proxy */ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct IMAP *imap = data->req.p.imap; + struct imap_conn *imapc = &conn->proto.imapc; + bool selected = FALSE; + + DEBUGF(infof(data, "DO phase starts")); + + if(data->req.no_body) { + /* Requested no body means no transfer */ + imap->transfer = PPTRANSFER_INFO; + } + + *dophase_done = FALSE; /* not done yet */ + + /* Determine if the requested mailbox (with the same UIDVALIDITY if set) + has already been selected on this connection */ + if(imap->mailbox && imapc->mailbox && + strcasecompare(imap->mailbox, imapc->mailbox) && + (!imap->uidvalidity || !imapc->mailbox_uidvalidity || + strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity))) + selected = TRUE; + + /* Start the first command in the DO phase */ + if(data->state.upload || IS_MIME_POST(data)) + /* APPEND can be executed directly */ + result = imap_perform_append(data); + else if(imap->custom && (selected || !imap->mailbox)) + /* Custom command using the same mailbox or no mailbox */ + result = imap_perform_list(data); + else if(!imap->custom && selected && (imap->uid || imap->mindex)) + /* FETCH from the same mailbox */ + result = imap_perform_fetch(data); + else if(!imap->custom && selected && imap->query) + /* SEARCH the current mailbox */ + result = imap_perform_search(data); + else if(imap->mailbox && !selected && + (imap->custom || imap->uid || imap->mindex || imap->query)) + /* SELECT the mailbox */ + result = imap_perform_select(data); + else + /* LIST */ + result = imap_perform_list(data); + + if(result) + return result; + + /* Run the state-machine */ + result = imap_multi_statemach(data, dophase_done); + + *connected = Curl_conn_is_connected(conn, FIRSTSOCKET); + + if(*dophase_done) + DEBUGF(infof(data, "DO phase is complete")); + + return result; +} + +/*********************************************************************** + * + * imap_do() + * + * This function is registered as 'curl_do' function. It decodes the path + * parts etc as a wrapper to the actual DO function (imap_perform). + * + * The input argument is already checked for validity. + */ +static CURLcode imap_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + *done = FALSE; /* default to false */ + + /* Parse the URL path */ + result = imap_parse_url_path(data); + if(result) + return result; + + /* Parse the custom request */ + result = imap_parse_custom_request(data); + if(result) + return result; + + result = imap_regular_transfer(data, done); + + return result; +} + +/*********************************************************************** + * + * imap_disconnect() + * + * Disconnect from an IMAP server. Cleanup protocol-specific per-connection + * resources. BLOCKING. + */ +static CURLcode imap_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) +{ + struct imap_conn *imapc = &conn->proto.imapc; + (void)data; + + /* We cannot send quit unconditionally. If this connection is stale or + bad in any way, sending quit and waiting around here will make the + disconnect wait in vain and cause more problems than we need to. */ + + /* The IMAP session may or may not have been allocated/setup at this + point! */ + if(!dead_connection && conn->bits.protoconnstart) { + if(!imap_perform_logout(data)) + (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */ + } + + /* Disconnect from the server */ + Curl_pp_disconnect(&imapc->pp); + Curl_dyn_free(&imapc->dyn); + + /* Cleanup the SASL module */ + Curl_sasl_cleanup(conn, imapc->sasl.authused); + + /* Cleanup our connection based variables */ + Curl_safefree(imapc->mailbox); + Curl_safefree(imapc->mailbox_uidvalidity); + + return CURLE_OK; +} + +/* Call this when the DO phase has completed */ +static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected) +{ + struct IMAP *imap = data->req.p.imap; + + (void)connected; + + if(imap->transfer != PPTRANSFER_BODY) + /* no data to transfer */ + Curl_xfer_setup_nop(data); + + return CURLE_OK; +} + +/* Called from multi.c while DOing */ +static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done) +{ + CURLcode result = imap_multi_statemach(data, dophase_done); + + if(result) + DEBUGF(infof(data, "DO phase failed")); + else if(*dophase_done) { + result = imap_dophase_done(data, FALSE /* not connected */); + + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +/*********************************************************************** + * + * imap_regular_transfer() + * + * The input argument is already checked for validity. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. + */ +static CURLcode imap_regular_transfer(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + + /* Make sure size is unknown at this point */ + data->req.size = -1; + + /* Set the progress data */ + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); + + /* Carry out the perform */ + result = imap_perform(data, &connected, dophase_done); + + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) + result = imap_dophase_done(data, connected); + + return result; +} + +static CURLcode imap_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Initialise the IMAP layer */ + CURLcode result = imap_init(data); + if(result) + return result; + + /* Clear the TLS upgraded flag */ + conn->bits.tls_upgraded = FALSE; + + return CURLE_OK; +} + +/*********************************************************************** + * + * imap_sendf() + * + * Sends the formatted string as an IMAP command to the server. + * + * Designed to never block. + */ +static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...) +{ + CURLcode result = CURLE_OK; + struct imap_conn *imapc = &data->conn->proto.imapc; + + DEBUGASSERT(fmt); + + /* Calculate the tag based on the connection ID and command ID */ + msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", + 'A' + curlx_sltosi((long)(data->conn->connection_id % 26)), + ++imapc->cmdid); + + /* start with a blank buffer */ + Curl_dyn_reset(&imapc->dyn); + + /* append tag + space + fmt */ + result = Curl_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt); + if(!result) { + va_list ap; + va_start(ap, fmt); +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif + result = Curl_pp_vsendf(data, &imapc->pp, Curl_dyn_ptr(&imapc->dyn), ap); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + va_end(ap); + } + return result; +} + +/*********************************************************************** + * + * imap_atom() + * + * Checks the input string for characters that need escaping and returns an + * atom ready for sending to the server. + * + * The returned string needs to be freed. + * + */ +static char *imap_atom(const char *str, bool escape_only) +{ + struct dynbuf line; + size_t nclean; + size_t len; + + if(!str) + return NULL; + + len = strlen(str); + nclean = strcspn(str, "() {%*]\\\""); + if(len == nclean) + /* nothing to escape, return a strdup */ + return strdup(str); + + Curl_dyn_init(&line, 2000); + + if(!escape_only && Curl_dyn_addn(&line, "\"", 1)) + return NULL; + + while(*str) { + if((*str == '\\' || *str == '"') && + Curl_dyn_addn(&line, "\\", 1)) + return NULL; + if(Curl_dyn_addn(&line, str, 1)) + return NULL; + str++; + } + + if(!escape_only && Curl_dyn_addn(&line, "\"", 1)) + return NULL; + + return Curl_dyn_ptr(&line); +} + +/*********************************************************************** + * + * imap_is_bchar() + * + * Portable test of whether the specified char is a "bchar" as defined in the + * grammar of RFC-5092. + */ +static bool imap_is_bchar(char ch) +{ + /* Performing the alnum check with this macro is faster because of ASCII + arithmetic */ + if(ISALNUM(ch)) + return true; + + switch(ch) { + /* bchar */ + case ':': case '@': case '/': + /* bchar -> achar */ + case '&': case '=': + /* bchar -> achar -> uchar -> unreserved (without alphanumeric) */ + case '-': case '.': case '_': case '~': + /* bchar -> achar -> uchar -> sub-delims-sh */ + case '!': case '$': case '\'': case '(': case ')': case '*': + case '+': case ',': + /* bchar -> achar -> uchar -> pct-encoded */ + case '%': /* HEXDIG chars are already included above */ + return true; + + default: + return false; + } +} + +/*********************************************************************** + * + * imap_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode imap_parse_url_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct imap_conn *imapc = &conn->proto.imapc; + const char *ptr = conn->options; + bool prefer_login = false; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(strncasecompare(key, "AUTH=+LOGIN", 11)) { + /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */ + prefer_login = true; + imapc->sasl.prefmech = SASL_AUTH_NONE; + } + else if(strncasecompare(key, "AUTH=", 5)) { + prefer_login = false; + result = Curl_sasl_parse_url_auth_option(&imapc->sasl, + value, ptr - value); + } + else { + prefer_login = false; + result = CURLE_URL_MALFORMAT; + } + + if(*ptr == ';') + ptr++; + } + + if(prefer_login) + imapc->preftype = IMAP_TYPE_CLEARTEXT; + else { + switch(imapc->sasl.prefmech) { + case SASL_AUTH_NONE: + imapc->preftype = IMAP_TYPE_NONE; + break; + case SASL_AUTH_DEFAULT: + imapc->preftype = IMAP_TYPE_ANY; + break; + default: + imapc->preftype = IMAP_TYPE_SASL; + break; + } + } + + return result; +} + +/*********************************************************************** + * + * imap_parse_url_path() + * + * Parse the URL path into separate path components. + * + */ +static CURLcode imap_parse_url_path(struct Curl_easy *data) +{ + /* The imap struct is already initialised in imap_connect() */ + CURLcode result = CURLE_OK; + struct IMAP *imap = data->req.p.imap; + const char *begin = &data->state.up.path[1]; /* skip leading slash */ + const char *ptr = begin; + + /* See how much of the URL is a valid path and decode it */ + while(imap_is_bchar(*ptr)) + ptr++; + + if(ptr != begin) { + /* Remove the trailing slash if present */ + const char *end = ptr; + if(end > begin && end[-1] == '/') + end--; + + result = Curl_urldecode(begin, end - begin, &imap->mailbox, NULL, + REJECT_CTRL); + if(result) + return result; + } + else + imap->mailbox = NULL; + + /* There can be any number of parameters in the form ";NAME=VALUE" */ + while(*ptr == ';') { + char *name; + char *value; + size_t valuelen; + + /* Find the length of the name parameter */ + begin = ++ptr; + while(*ptr && *ptr != '=') + ptr++; + + if(!*ptr) + return CURLE_URL_MALFORMAT; + + /* Decode the name parameter */ + result = Curl_urldecode(begin, ptr - begin, &name, NULL, + REJECT_CTRL); + if(result) + return result; + + /* Find the length of the value parameter */ + begin = ++ptr; + while(imap_is_bchar(*ptr)) + ptr++; + + /* Decode the value parameter */ + result = Curl_urldecode(begin, ptr - begin, &value, &valuelen, + REJECT_CTRL); + if(result) { + free(name); + return result; + } + + DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", name, value)); + + /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and + PARTIAL) stripping of the trailing slash character if it is present. + + Note: Unknown parameters trigger a URL_MALFORMAT error. */ + if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) { + if(valuelen > 0 && value[valuelen - 1] == '/') + value[valuelen - 1] = '\0'; + + imap->uidvalidity = value; + value = NULL; + } + else if(strcasecompare(name, "UID") && !imap->uid) { + if(valuelen > 0 && value[valuelen - 1] == '/') + value[valuelen - 1] = '\0'; + + imap->uid = value; + value = NULL; + } + else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) { + if(valuelen > 0 && value[valuelen - 1] == '/') + value[valuelen - 1] = '\0'; + + imap->mindex = value; + value = NULL; + } + else if(strcasecompare(name, "SECTION") && !imap->section) { + if(valuelen > 0 && value[valuelen - 1] == '/') + value[valuelen - 1] = '\0'; + + imap->section = value; + value = NULL; + } + else if(strcasecompare(name, "PARTIAL") && !imap->partial) { + if(valuelen > 0 && value[valuelen - 1] == '/') + value[valuelen - 1] = '\0'; + + imap->partial = value; + value = NULL; + } + else { + free(name); + free(value); + + return CURLE_URL_MALFORMAT; + } + + free(name); + free(value); + } + + /* Does the URL contain a query parameter? Only valid when we have a mailbox + and no UID as per RFC-5092 */ + if(imap->mailbox && !imap->uid && !imap->mindex) { + /* Get the query parameter, URL decoded */ + (void)curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query, + CURLU_URLDECODE); + } + + /* Any extra stuff at the end of the URL is an error */ + if(*ptr) + return CURLE_URL_MALFORMAT; + + return CURLE_OK; +} + +/*********************************************************************** + * + * imap_parse_custom_request() + * + * Parse the custom request. + */ +static CURLcode imap_parse_custom_request(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct IMAP *imap = data->req.p.imap; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + + if(custom) { + /* URL decode the custom request */ + result = Curl_urldecode(custom, 0, &imap->custom, NULL, REJECT_CTRL); + + /* Extract the parameters if specified */ + if(!result) { + const char *params = imap->custom; + + while(*params && *params != ' ') + params++; + + if(*params) { + imap->custom_params = strdup(params); + imap->custom[params - imap->custom] = '\0'; + + if(!imap->custom_params) + result = CURLE_OUT_OF_MEMORY; + } + } + } + + return result; +} + +#endif /* CURL_DISABLE_IMAP */ diff --git a/third_party/curl/lib/imap.h b/third_party/curl/lib/imap.h new file mode 100644 index 0000000000..784ee97e55 --- /dev/null +++ b/third_party/curl/lib/imap.h @@ -0,0 +1,101 @@ +#ifndef HEADER_CURL_IMAP_H +#define HEADER_CURL_IMAP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "pingpong.h" +#include "curl_sasl.h" + +/**************************************************************************** + * IMAP unique setup + ***************************************************************************/ +typedef enum { + IMAP_STOP, /* do nothing state, stops the state machine */ + IMAP_SERVERGREET, /* waiting for the initial greeting immediately after + a connect */ + IMAP_CAPABILITY, + IMAP_STARTTLS, + IMAP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS + (multi mode only) */ + IMAP_AUTHENTICATE, + IMAP_LOGIN, + IMAP_LIST, + IMAP_SELECT, + IMAP_FETCH, + IMAP_FETCH_FINAL, + IMAP_APPEND, + IMAP_APPEND_FINAL, + IMAP_SEARCH, + IMAP_LOGOUT, + IMAP_LAST /* never used */ +} imapstate; + +/* This IMAP struct is used in the Curl_easy. All IMAP data that is + connection-oriented must be in imap_conn to properly deal with the fact that + perhaps the Curl_easy is changed between the times the connection is + used. */ +struct IMAP { + curl_pp_transfer transfer; + char *mailbox; /* Mailbox to select */ + char *uidvalidity; /* UIDVALIDITY to check in select */ + char *uid; /* Message UID to fetch */ + char *mindex; /* Index in mail box of mail to fetch */ + char *section; /* Message SECTION to fetch */ + char *partial; /* Message PARTIAL to fetch */ + char *query; /* Query to search for */ + char *custom; /* Custom request */ + char *custom_params; /* Parameters for the custom request */ +}; + +/* imap_conn is used for struct connection-oriented data in the connectdata + struct */ +struct imap_conn { + struct pingpong pp; + struct SASL sasl; /* SASL-related parameters */ + struct dynbuf dyn; /* for the IMAP commands */ + char *mailbox; /* The last selected mailbox */ + char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */ + imapstate state; /* Always use imap.c:state() to change state! */ + char resptag[5]; /* Response tag to wait for */ + unsigned char preftype; /* Preferred authentication type */ + unsigned char cmdid; /* Last used command ID */ + BIT(ssldone); /* Is connect() over SSL done? */ + BIT(preauth); /* Is this connection PREAUTH? */ + BIT(tls_supported); /* StartTLS capability supported by server */ + BIT(login_disabled); /* LOGIN command disabled by server */ + BIT(ir_supported); /* Initial response supported by server */ +}; + +extern const struct Curl_handler Curl_handler_imap; +extern const struct Curl_handler Curl_handler_imaps; + +/* Authentication type flags */ +#define IMAP_TYPE_CLEARTEXT (1 << 0) +#define IMAP_TYPE_SASL (1 << 1) + +/* Authentication type values */ +#define IMAP_TYPE_NONE 0 +#define IMAP_TYPE_ANY (IMAP_TYPE_CLEARTEXT|IMAP_TYPE_SASL) + +#endif /* HEADER_CURL_IMAP_H */ diff --git a/third_party/curl/lib/inet_ntop.c b/third_party/curl/lib/inet_ntop.c new file mode 100644 index 0000000000..3a81eef6ef --- /dev/null +++ b/third_party/curl/lib/inet_ntop.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 1996-2022 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * SPDX-License-Identifier: ISC + */ +/* + * Original code by Paul Vixie. "curlified" by Gisle Vanem. + */ + +#include "curl_setup.h" + +#ifndef HAVE_INET_NTOP + +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#include "inet_ntop.h" +#include "curl_printf.h" + +#define IN6ADDRSZ 16 +#define INADDRSZ 4 +#define INT16SZ 2 + +/* + * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make + * sure we have _some_ value for AF_INET6 without polluting our fake value + * everywhere. + */ +#if !defined(USE_IPV6) && !defined(AF_INET6) +#define AF_INET6 (AF_INET + 1) +#endif + +/* + * Format an IPv4 address, more or less like inet_ntop(). + * + * Returns `dst' (as a const) + * Note: + * - uses no statics + * - takes a unsigned char* not an in_addr as input + */ +static char *inet_ntop4(const unsigned char *src, char *dst, size_t size) +{ + char tmp[sizeof("255.255.255.255")]; + size_t len; + + DEBUGASSERT(size >= 16); + + tmp[0] = '\0'; + (void)msnprintf(tmp, sizeof(tmp), "%d.%d.%d.%d", + ((int)((unsigned char)src[0])) & 0xff, + ((int)((unsigned char)src[1])) & 0xff, + ((int)((unsigned char)src[2])) & 0xff, + ((int)((unsigned char)src[3])) & 0xff); + + len = strlen(tmp); + if(len == 0 || len >= size) { + errno = ENOSPC; + return (NULL); + } + strcpy(dst, tmp); + return dst; +} + +/* + * Convert IPv6 binary address into presentation (printable) format. + */ +static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + char *tp; + struct { + int base; + int len; + } best, cur; + unsigned int words[IN6ADDRSZ / INT16SZ]; + int i; + + /* Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof(words)); + for(i = 0; i < IN6ADDRSZ; i++) + words[i/2] |= ((unsigned int)src[i] << ((1 - (i % 2)) << 3)); + + best.base = -1; + cur.base = -1; + best.len = 0; + cur.len = 0; + + for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + if(words[i] == 0) { + if(cur.base == -1) { + cur.base = i; cur.len = 1; + } + else + cur.len++; + } + else if(cur.base != -1) { + if(best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + if((cur.base != -1) && (best.base == -1 || cur.len > best.len)) + best = cur; + if(best.base != -1 && best.len < 2) + best.base = -1; + /* Format the result. */ + tp = tmp; + for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if(best.base != -1 && i >= best.base && i < (best.base + best.len)) { + if(i == best.base) + *tp++ = ':'; + continue; + } + + /* Are we following an initial run of 0x00s or any real hex? + */ + if(i) + *tp++ = ':'; + + /* Is this address an encapsulated IPv4? + */ + if(i == 6 && best.base == 0 && + (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + if(!inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp))) { + errno = ENOSPC; + return (NULL); + } + tp += strlen(tp); + break; + } + tp += msnprintf(tp, 5, "%x", words[i]); + } + + /* Was it a trailing run of 0x00's? + */ + if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* Check for overflow, copy, and we are done. + */ + if((size_t)(tp - tmp) > size) { + errno = ENOSPC; + return (NULL); + } + strcpy(dst, tmp); + return dst; +} + +/* + * Convert a network format address to presentation format. + * + * Returns pointer to presentation format address (`buf'). + * Returns NULL on error and errno set with the specific + * error, EAFNOSUPPORT or ENOSPC. + * + * On Windows we store the error in the thread errno, not in the winsock error + * code. This is to avoid losing the actual last winsock error. When this + * function returns NULL, check errno not SOCKERRNO. + */ +char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size) +{ + switch(af) { + case AF_INET: + return inet_ntop4((const unsigned char *)src, buf, size); + case AF_INET6: + return inet_ntop6((const unsigned char *)src, buf, size); + default: + errno = EAFNOSUPPORT; + return NULL; + } +} +#endif /* HAVE_INET_NTOP */ diff --git a/third_party/curl/lib/inet_ntop.h b/third_party/curl/lib/inet_ntop.h new file mode 100644 index 0000000000..f592f25251 --- /dev/null +++ b/third_party/curl/lib/inet_ntop.h @@ -0,0 +1,44 @@ +#ifndef HEADER_CURL_INET_NTOP_H +#define HEADER_CURL_INET_NTOP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size); + +#ifdef HAVE_INET_NTOP +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef _WIN32 +#define Curl_inet_ntop(af,addr,buf,size) \ + inet_ntop(af, addr, buf, size) +#else +#define Curl_inet_ntop(af,addr,buf,size) \ + inet_ntop(af, addr, buf, (curl_socklen_t)(size)) +#endif +#endif + +#endif /* HEADER_CURL_INET_NTOP_H */ diff --git a/third_party/curl/lib/inet_pton.c b/third_party/curl/lib/inet_pton.c new file mode 100644 index 0000000000..49b923f182 --- /dev/null +++ b/third_party/curl/lib/inet_pton.c @@ -0,0 +1,243 @@ +/* This is from the BIND 4.9.4 release, modified to compile by itself */ + +/* Copyright (c) Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * + * SPDX-License-Identifier: ISC + */ + +#include "curl_setup.h" + +#ifndef HAVE_INET_PTON + +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#include "inet_pton.h" + +#define IN6ADDRSZ 16 +#define INADDRSZ 4 +#define INT16SZ 2 + +/* + * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make + * sure we have _some_ value for AF_INET6 without polluting our fake value + * everywhere. + */ +#if !defined(USE_IPV6) && !defined(AF_INET6) +#define AF_INET6 (AF_INET + 1) +#endif + +/* + * WARNING: Do not even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static int inet_pton4(const char *src, unsigned char *dst); +static int inet_pton6(const char *src, unsigned char *dst); + +/* int + * inet_pton(af, src, dst) + * convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * return: + * 1 if the address was valid for the specified address family + * 0 if the address was not valid (`dst' is untouched in this case) + * -1 if some other error occurred (`dst' is untouched in this case, too) + * notice: + * On Windows we store the error in the thread errno, not + * in the winsock error code. This is to avoid losing the + * actual last winsock error. When this function returns + * -1, check errno not SOCKERRNO. + * author: + * Paul Vixie, 1996. + */ +int +Curl_inet_pton(int af, const char *src, void *dst) +{ + switch(af) { + case AF_INET: + return (inet_pton4(src, (unsigned char *)dst)); + case AF_INET6: + return (inet_pton6(src, (unsigned char *)dst)); + default: + errno = EAFNOSUPPORT; + return (-1); + } + /* NOTREACHED */ +} + +/* int + * inet_pton4(src, dst) + * like inet_aton() but without all the hexadecimal and shorthand. + * return: + * 1 if `src' is a valid dotted quad, else 0. + * notice: + * does not touch `dst' unless it is returning 1. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton4(const char *src, unsigned char *dst) +{ + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + unsigned char tmp[INADDRSZ], *tp; + + saw_digit = 0; + octets = 0; + tp = tmp; + *tp = 0; + while((ch = *src++) != '\0') { + const char *pch; + + pch = strchr(digits, ch); + if(pch) { + unsigned int val = (unsigned int)(*tp * 10) + + (unsigned int)(pch - digits); + + if(saw_digit && *tp == 0) + return (0); + if(val > 255) + return (0); + *tp = (unsigned char)val; + if(!saw_digit) { + if(++octets > 4) + return (0); + saw_digit = 1; + } + } + else if(ch == '.' && saw_digit) { + if(octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } + else + return (0); + } + if(octets < 4) + return (0); + memcpy(dst, tmp, INADDRSZ); + return (1); +} + +/* int + * inet_pton6(src, dst) + * convert presentation level address to network order binary form. + * return: + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * notice: + * (1) does not touch `dst' unless it is returning 1. + * (2) :: in a full address is silently ignored. + * credit: + * inspired by Mark Andrews. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton6(const char *src, unsigned char *dst) +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp; + const char *curtok; + int ch, saw_xdigit; + size_t val; + + memset((tp = tmp), 0, IN6ADDRSZ); + endp = tp + IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if(*src == ':') + if(*++src != ':') + return (0); + curtok = src; + saw_xdigit = 0; + val = 0; + while((ch = *src++) != '\0') { + const char *xdigits; + const char *pch; + + pch = strchr((xdigits = xdigits_l), ch); + if(!pch) + pch = strchr((xdigits = xdigits_u), ch); + if(pch) { + val <<= 4; + val |= (pch - xdigits); + if(++saw_xdigit > 4) + return (0); + continue; + } + if(ch == ':') { + curtok = src; + if(!saw_xdigit) { + if(colonp) + return (0); + colonp = tp; + continue; + } + if(tp + INT16SZ > endp) + return (0); + *tp++ = (unsigned char) ((val >> 8) & 0xff); + *tp++ = (unsigned char) (val & 0xff); + saw_xdigit = 0; + val = 0; + continue; + } + if(ch == '.' && ((tp + INADDRSZ) <= endp) && + inet_pton4(curtok, tp) > 0) { + tp += INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return (0); + } + if(saw_xdigit) { + if(tp + INT16SZ > endp) + return (0); + *tp++ = (unsigned char) ((val >> 8) & 0xff); + *tp++ = (unsigned char) (val & 0xff); + } + if(colonp) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we will do the shift by hand. + */ + const ssize_t n = tp - colonp; + ssize_t i; + + if(tp == endp) + return (0); + for(i = 1; i <= n; i++) { + *(endp - i) = *(colonp + n - i); + *(colonp + n - i) = 0; + } + tp = endp; + } + if(tp != endp) + return (0); + memcpy(dst, tmp, IN6ADDRSZ); + return (1); +} + +#endif /* HAVE_INET_PTON */ diff --git a/third_party/curl/lib/inet_pton.h b/third_party/curl/lib/inet_pton.h new file mode 100644 index 0000000000..f8562fa8a7 --- /dev/null +++ b/third_party/curl/lib/inet_pton.h @@ -0,0 +1,38 @@ +#ifndef HEADER_CURL_INET_PTON_H +#define HEADER_CURL_INET_PTON_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +int Curl_inet_pton(int, const char *, void *); + +#ifdef HAVE_INET_PTON +#ifdef HAVE_ARPA_INET_H +#include +#endif +#define Curl_inet_pton(x,y,z) inet_pton(x,y,z) +#endif + +#endif /* HEADER_CURL_INET_PTON_H */ diff --git a/third_party/curl/lib/krb5.c b/third_party/curl/lib/krb5.c new file mode 100644 index 0000000000..45944cefc2 --- /dev/null +++ b/third_party/curl/lib/krb5.c @@ -0,0 +1,922 @@ +/* GSSAPI/krb5 support for FTP - loosely based on old krb4.c + * + * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * Copyright (C) Daniel Stenberg + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + +#include "curl_setup.h" + +#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP) + +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#include "urldata.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "curl_base64.h" +#include "ftp.h" +#include "curl_gssapi.h" +#include "sendf.h" +#include "transfer.h" +#include "curl_krb5.h" +#include "warnless.h" +#include "strcase.h" +#include "strdup.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, + const char *cmd) +{ + size_t bytes_written; +#define SBUF_SIZE 1024 + char s[SBUF_SIZE]; + size_t write_len; + char *sptr = s; + CURLcode result = CURLE_OK; +#ifdef HAVE_GSSAPI + unsigned char data_sec = conn->data_prot; +#endif + + DEBUGASSERT(cmd); + + write_len = strlen(cmd); + if(!write_len || write_len > (sizeof(s) -3)) + return CURLE_BAD_FUNCTION_ARGUMENT; + + memcpy(&s, cmd, write_len); + strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */ + write_len += 2; + bytes_written = 0; + + for(;;) { +#ifdef HAVE_GSSAPI + conn->data_prot = PROT_CMD; +#endif + result = Curl_xfer_send(data, sptr, write_len, &bytes_written); +#ifdef HAVE_GSSAPI + DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); + conn->data_prot = data_sec; +#endif + + if(result) + break; + + Curl_debug(data, CURLINFO_HEADER_OUT, sptr, bytes_written); + + if(bytes_written != write_len) { + write_len -= bytes_written; + sptr += bytes_written; + } + else + break; + } + + return result; +} + +static int +krb5_init(void *app_data) +{ + gss_ctx_id_t *context = app_data; + /* Make sure our context is initialized for krb5_end. */ + *context = GSS_C_NO_CONTEXT; + return 0; +} + +static int +krb5_check_prot(void *app_data, int level) +{ + (void)app_data; /* unused */ + if(level == PROT_CONFIDENTIAL) + return -1; + return 0; +} + +static int +krb5_decode(void *app_data, void *buf, int len, + int level UNUSED_PARAM, + struct connectdata *conn UNUSED_PARAM) +{ + gss_ctx_id_t *context = app_data; + OM_uint32 maj, min; + gss_buffer_desc enc, dec; + + (void)level; + (void)conn; + + enc.value = buf; + enc.length = len; + maj = gss_unwrap(&min, *context, &enc, &dec, NULL, NULL); + if(maj != GSS_S_COMPLETE) + return -1; + + memcpy(buf, dec.value, dec.length); + len = curlx_uztosi(dec.length); + gss_release_buffer(&min, &dec); + + return len; +} + +static int +krb5_encode(void *app_data, const void *from, int length, int level, void **to) +{ + gss_ctx_id_t *context = app_data; + gss_buffer_desc dec, enc; + OM_uint32 maj, min; + int state; + int len; + + /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal + * libraries modify the input buffer in gss_wrap() + */ + dec.value = (void *)from; + dec.length = (size_t)length; + maj = gss_wrap(&min, *context, + level == PROT_PRIVATE, + GSS_C_QOP_DEFAULT, + &dec, &state, &enc); + + if(maj != GSS_S_COMPLETE) + return -1; + + /* malloc a new buffer, in case gss_release_buffer does not work as + expected */ + *to = malloc(enc.length); + if(!*to) + return -1; + memcpy(*to, enc.value, enc.length); + len = curlx_uztosi(enc.length); + gss_release_buffer(&min, &enc); + return len; +} + +static int +krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) +{ + int ret = AUTH_OK; + char *p; + const char *host = conn->host.name; + ssize_t nread; + curl_socklen_t l = sizeof(conn->local_addr); + CURLcode result; + const char *service = data->set.str[STRING_SERVICE_NAME] ? + data->set.str[STRING_SERVICE_NAME] : + "ftp"; + const char *srv_host = "host"; + gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp; + OM_uint32 maj, min; + gss_name_t gssname; + gss_ctx_id_t *context = app_data; + struct gss_channel_bindings_struct chan; + size_t base64_sz = 0; + struct sockaddr_in *remote_addr = + (struct sockaddr_in *)(void *)&conn->remote_addr->sa_addr; + char *stringp; + + if(getsockname(conn->sock[FIRSTSOCKET], + (struct sockaddr *)&conn->local_addr, &l) < 0) + perror("getsockname()"); + + chan.initiator_addrtype = GSS_C_AF_INET; + chan.initiator_address.length = l - 4; + chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr; + chan.acceptor_addrtype = GSS_C_AF_INET; + chan.acceptor_address.length = l - 4; + chan.acceptor_address.value = &remote_addr->sin_addr.s_addr; + chan.application_data.length = 0; + chan.application_data.value = NULL; + + /* this loop will execute twice (once for service, once for host) */ + for(;;) { + /* this really should not be repeated here, but cannot help it */ + if(service == srv_host) { + result = ftpsend(data, conn, "AUTH GSSAPI"); + if(result) + return -2; + + if(Curl_GetFTPResponse(data, &nread, NULL)) + return -1; + else { + struct pingpong *pp = &conn->proto.ftpc.pp; + char *line = Curl_dyn_ptr(&pp->recvbuf); + if(line[0] != '3') + return -1; + } + } + + stringp = aprintf("%s@%s", service, host); + if(!stringp) + return -2; + + input_buffer.value = stringp; + input_buffer.length = strlen(stringp); + maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE, + &gssname); + free(stringp); + if(maj != GSS_S_COMPLETE) { + gss_release_name(&min, &gssname); + if(service == srv_host) { + failf(data, "Error importing service name %s@%s", service, host); + return AUTH_ERROR; + } + service = srv_host; + continue; + } + /* We pass NULL as |output_name_type| to avoid a leak. */ + gss_display_name(&min, gssname, &output_buffer, NULL); + infof(data, "Trying against %s", (char *)output_buffer.value); + gssresp = GSS_C_NO_BUFFER; + *context = GSS_C_NO_CONTEXT; + + do { + /* Release the buffer at each iteration to avoid leaking: the first time + we are releasing the memory from gss_display_name. The last item is + taken care by a final gss_release_buffer. */ + gss_release_buffer(&min, &output_buffer); + ret = AUTH_OK; + maj = Curl_gss_init_sec_context(data, + &min, + context, + gssname, + &Curl_krb5_mech_oid, + &chan, + gssresp, + &output_buffer, + TRUE, + NULL); + + if(gssresp) { + free(_gssresp.value); + gssresp = NULL; + } + + if(GSS_ERROR(maj)) { + infof(data, "Error creating security context"); + ret = AUTH_ERROR; + break; + } + + if(output_buffer.length) { + char *cmd; + + result = Curl_base64_encode((char *)output_buffer.value, + output_buffer.length, &p, &base64_sz); + if(result) { + infof(data, "base64-encoding: %s", curl_easy_strerror(result)); + ret = AUTH_ERROR; + break; + } + + cmd = aprintf("ADAT %s", p); + if(cmd) + result = ftpsend(data, conn, cmd); + else + result = CURLE_OUT_OF_MEMORY; + + free(p); + free(cmd); + + if(result) { + ret = -2; + break; + } + + if(Curl_GetFTPResponse(data, &nread, NULL)) { + ret = -1; + break; + } + else { + struct pingpong *pp = &conn->proto.ftpc.pp; + size_t len = Curl_dyn_len(&pp->recvbuf); + p = Curl_dyn_ptr(&pp->recvbuf); + if((len < 4) || (p[0] != '2' && p[0] != '3')) { + infof(data, "Server did not accept auth data"); + ret = AUTH_ERROR; + break; + } + } + + _gssresp.value = NULL; /* make sure it is initialized */ + p += 4; /* over '789 ' */ + p = strstr(p, "ADAT="); + if(p) { + result = Curl_base64_decode(p + 5, + (unsigned char **)&_gssresp.value, + &_gssresp.length); + if(result) { + failf(data, "base64-decoding: %s", curl_easy_strerror(result)); + ret = AUTH_CONTINUE; + break; + } + } + + gssresp = &_gssresp; + } + } while(maj == GSS_S_CONTINUE_NEEDED); + + gss_release_name(&min, &gssname); + gss_release_buffer(&min, &output_buffer); + + if(gssresp) + free(_gssresp.value); + + if(ret == AUTH_OK || service == srv_host) + return ret; + + service = srv_host; + } + return ret; +} + +static void krb5_end(void *app_data) +{ + OM_uint32 min; + gss_ctx_id_t *context = app_data; + if(*context != GSS_C_NO_CONTEXT) { + OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER); + (void)maj; + DEBUGASSERT(maj == GSS_S_COMPLETE); + } +} + +static const struct Curl_sec_client_mech Curl_krb5_client_mech = { + "GSSAPI", + sizeof(gss_ctx_id_t), + krb5_init, + krb5_auth, + krb5_end, + krb5_check_prot, + + krb5_encode, + krb5_decode +}; + +static const struct { + unsigned char level; + const char *name; +} level_names[] = { + { PROT_CLEAR, "clear" }, + { PROT_SAFE, "safe" }, + { PROT_CONFIDENTIAL, "confidential" }, + { PROT_PRIVATE, "private" } +}; + +static unsigned char name_to_level(const char *name) +{ + int i; + for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) + if(curl_strequal(name, level_names[i].name)) + return level_names[i].level; + return PROT_NONE; +} + +/* Convert a protocol |level| to its char representation. + We take an int to catch programming mistakes. */ +static char level_to_char(int level) +{ + switch(level) { + case PROT_CLEAR: + return 'C'; + case PROT_SAFE: + return 'S'; + case PROT_CONFIDENTIAL: + return 'E'; + case PROT_PRIVATE: + return 'P'; + case PROT_CMD: + default: + /* Those 2 cases should not be reached! */ + break; + } + DEBUGASSERT(0); + /* Default to the most secure alternative. */ + return 'P'; +} + +/* Send an FTP command defined by |message| and the optional arguments. The + function returns the ftp_code. If an error occurs, -1 is returned. */ +static int ftp_send_command(struct Curl_easy *data, const char *message, ...) + CURL_PRINTF(2, 3); + +static int ftp_send_command(struct Curl_easy *data, const char *message, ...) +{ + int ftp_code; + ssize_t nread = 0; + va_list args; + char print_buffer[50]; + + va_start(args, message); + mvsnprintf(print_buffer, sizeof(print_buffer), message, args); + va_end(args); + + if(ftpsend(data, data->conn, print_buffer)) { + ftp_code = -1; + } + else { + if(Curl_GetFTPResponse(data, &nread, &ftp_code)) + ftp_code = -1; + } + + (void)nread; /* Unused */ + return ftp_code; +} + +/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode + saying whether an error occurred or CURLE_OK if |len| was read. */ +static CURLcode +socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len) +{ + char *to_p = to; + CURLcode result; + ssize_t nread = 0; + + while(len > 0) { + result = Curl_conn_recv(data, sockindex, to_p, len, &nread); + if(nread > 0) { + len -= nread; + to_p += nread; + } + else { + if(result == CURLE_AGAIN) + continue; + return result; + } + } + return CURLE_OK; +} + + +/* Write |len| bytes from the buffer |to| to the socket |fd|. Return a + CURLcode saying whether an error occurred or CURLE_OK if |len| was + written. */ +static CURLcode +socket_write(struct Curl_easy *data, int sockindex, const void *to, + size_t len) +{ + const char *to_p = to; + CURLcode result; + size_t written; + + while(len > 0) { + result = Curl_conn_send(data, sockindex, to_p, len, &written); + if(!result && written > 0) { + len -= written; + to_p += written; + } + else { + if(result == CURLE_AGAIN) + continue; + return result; + } + } + return CURLE_OK; +} + +static CURLcode read_data(struct Curl_easy *data, int sockindex, + struct krb5buffer *buf) +{ + struct connectdata *conn = data->conn; + int len; + CURLcode result; + int nread; + + result = socket_read(data, sockindex, &len, sizeof(len)); + if(result) + return result; + + if(len) { + len = (int)ntohl((uint32_t)len); + if(len > CURL_MAX_INPUT_LENGTH) + return CURLE_TOO_LARGE; + + Curl_dyn_reset(&buf->buf); + } + else + return CURLE_RECV_ERROR; + + do { + char buffer[1024]; + nread = CURLMIN(len, (int)sizeof(buffer)); + result = socket_read(data, sockindex, buffer, (size_t)nread); + if(result) + return result; + result = Curl_dyn_addn(&buf->buf, buffer, nread); + if(result) + return result; + len -= nread; + } while(len); + /* this decodes the dynbuf *in place* */ + nread = conn->mech->decode(conn->app_data, + Curl_dyn_ptr(&buf->buf), + len, conn->data_prot, conn); + if(nread < 0) + return CURLE_RECV_ERROR; + Curl_dyn_setlen(&buf->buf, nread); + buf->index = 0; + return CURLE_OK; +} + +static size_t +buffer_read(struct krb5buffer *buf, void *data, size_t len) +{ + size_t size = Curl_dyn_len(&buf->buf); + if(size - buf->index < len) + len = size - buf->index; + memcpy(data, Curl_dyn_ptr(&buf->buf) + buf->index, len); + buf->index += len; + return len; +} + +/* Matches Curl_recv signature */ +static ssize_t sec_recv(struct Curl_easy *data, int sockindex, + char *buffer, size_t len, CURLcode *err) +{ + size_t bytes_read; + size_t total_read = 0; + struct connectdata *conn = data->conn; + + *err = CURLE_OK; + + /* Handle clear text response. */ + if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) { + ssize_t nread; + *err = Curl_conn_recv(data, sockindex, buffer, len, &nread); + return nread; + } + + if(conn->in_buffer.eof_flag) { + conn->in_buffer.eof_flag = 0; + return 0; + } + + bytes_read = buffer_read(&conn->in_buffer, buffer, len); + len -= bytes_read; + total_read += bytes_read; + buffer += bytes_read; + + while(len > 0) { + if(read_data(data, sockindex, &conn->in_buffer)) + return -1; + if(Curl_dyn_len(&conn->in_buffer.buf) == 0) { + if(bytes_read > 0) + conn->in_buffer.eof_flag = 1; + return bytes_read; + } + bytes_read = buffer_read(&conn->in_buffer, buffer, len); + len -= bytes_read; + total_read += bytes_read; + buffer += bytes_read; + } + return total_read; +} + +/* Send |length| bytes from |from| to the |fd| socket taking care of encoding + and negotiating with the server. |from| can be NULL. */ +static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t fd, const char *from, int length) +{ + int bytes, htonl_bytes; /* 32-bit integers for htonl */ + char *buffer = NULL; + char *cmd_buffer; + size_t cmd_size = 0; + CURLcode error; + enum protection_level prot_level = conn->data_prot; + bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE; + + DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST); + + if(iscmd) { + if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5)) + prot_level = PROT_PRIVATE; + else + prot_level = conn->command_prot; + } + bytes = conn->mech->encode(conn->app_data, from, length, (int)prot_level, + (void **)&buffer); + if(!buffer || bytes <= 0) + return; /* error */ + + if(iscmd) { + error = Curl_base64_encode(buffer, curlx_sitouz(bytes), + &cmd_buffer, &cmd_size); + if(error) { + free(buffer); + return; /* error */ + } + if(cmd_size > 0) { + static const char *enc = "ENC "; + static const char *mic = "MIC "; + if(prot_level == PROT_PRIVATE) + socket_write(data, fd, enc, 4); + else + socket_write(data, fd, mic, 4); + + socket_write(data, fd, cmd_buffer, cmd_size); + socket_write(data, fd, "\r\n", 2); + infof(data, "Send: %s%s", prot_level == PROT_PRIVATE?enc:mic, + cmd_buffer); + free(cmd_buffer); + } + } + else { + htonl_bytes = (int)htonl((OM_uint32)bytes); + socket_write(data, fd, &htonl_bytes, sizeof(htonl_bytes)); + socket_write(data, fd, buffer, curlx_sitouz(bytes)); + } + free(buffer); +} + +static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t fd, const char *buffer, size_t length) +{ + ssize_t tx = 0, len = conn->buffer_size; + + if(len <= 0) + len = length; + while(length) { + if(length < (size_t)len) + len = length; + + do_sec_send(data, conn, fd, buffer, curlx_sztosi(len)); + length -= len; + buffer += len; + tx += len; + } + return tx; +} + +/* Matches Curl_send signature */ +static ssize_t sec_send(struct Curl_easy *data, int sockindex, + const void *buffer, size_t len, CURLcode *err) +{ + struct connectdata *conn = data->conn; + curl_socket_t fd = conn->sock[sockindex]; + *err = CURLE_OK; + return sec_write(data, conn, fd, buffer, len); +} + +int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, + char *buffer, enum protection_level level) +{ + /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an + int */ + int decoded_len; + char *buf; + int ret_code = 0; + size_t decoded_sz = 0; + CURLcode error; + + (void) data; + + if(!conn->mech) + /* not initialized, return error */ + return -1; + + DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); + + error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz); + if(error || decoded_sz == 0) + return -1; + + if(decoded_sz > (size_t)INT_MAX) { + free(buf); + return -1; + } + decoded_len = curlx_uztosi(decoded_sz); + + decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len, + (int)level, conn); + if(decoded_len <= 0) { + free(buf); + return -1; + } + + { + buf[decoded_len] = '\n'; + Curl_debug(data, CURLINFO_HEADER_IN, buf, decoded_len + 1); + } + + buf[decoded_len] = '\0'; + if(decoded_len <= 3) + /* suspiciously short */ + return 0; + + if(buf[3] != '-') + ret_code = atoi(buf); + + if(buf[decoded_len - 1] == '\n') + buf[decoded_len - 1] = '\0'; + strcpy(buffer, buf); + free(buf); + return ret_code; +} + +static int sec_set_protection_level(struct Curl_easy *data) +{ + int code; + struct connectdata *conn = data->conn; + unsigned char level = conn->request_data_prot; + + DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); + + if(!conn->sec_complete) { + infof(data, "Trying to change the protection level after the" + " completion of the data exchange."); + return -1; + } + + /* Bail out if we try to set up the same level */ + if(conn->data_prot == level) + return 0; + + if(level) { + char *pbsz; + unsigned int buffer_size = 1 << 20; /* 1048576 */ + struct pingpong *pp = &conn->proto.ftpc.pp; + char *line; + + code = ftp_send_command(data, "PBSZ %u", buffer_size); + if(code < 0) + return -1; + + if(code/100 != 2) { + failf(data, "Failed to set the protection's buffer size."); + return -1; + } + conn->buffer_size = buffer_size; + + line = Curl_dyn_ptr(&pp->recvbuf); + pbsz = strstr(line, "PBSZ="); + if(pbsz) { + /* stick to default value if the check fails */ + if(ISDIGIT(pbsz[5])) + buffer_size = (unsigned int)atoi(&pbsz[5]); + if(buffer_size < conn->buffer_size) + conn->buffer_size = buffer_size; + } + } + + /* Now try to negotiate the protection level. */ + code = ftp_send_command(data, "PROT %c", level_to_char(level)); + + if(code < 0) + return -1; + + if(code/100 != 2) { + failf(data, "Failed to set the protection level."); + return -1; + } + + conn->data_prot = level; + if(level == PROT_PRIVATE) + conn->command_prot = level; + + return 0; +} + +int +Curl_sec_request_prot(struct connectdata *conn, const char *level) +{ + unsigned char l = name_to_level(level); + if(l == PROT_NONE) + return -1; + DEBUGASSERT(l > PROT_NONE && l < PROT_LAST); + conn->request_data_prot = l; + return 0; +} + +static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn) +{ + int ret; + void *tmp_allocation; + const struct Curl_sec_client_mech *mech = &Curl_krb5_client_mech; + + tmp_allocation = realloc(conn->app_data, mech->size); + if(!tmp_allocation) { + failf(data, "Failed realloc of size %zu", mech->size); + mech = NULL; + return CURLE_OUT_OF_MEMORY; + } + conn->app_data = tmp_allocation; + + if(mech->init) { + ret = mech->init(conn->app_data); + if(ret) { + infof(data, "Failed initialization for %s. Skipping it.", + mech->name); + return CURLE_FAILED_INIT; + } + Curl_dyn_init(&conn->in_buffer.buf, CURL_MAX_INPUT_LENGTH); + } + + infof(data, "Trying mechanism %s...", mech->name); + ret = ftp_send_command(data, "AUTH %s", mech->name); + if(ret < 0) + return CURLE_COULDNT_CONNECT; + + if(ret/100 != 3) { + switch(ret) { + case 504: + infof(data, "Mechanism %s is not supported by the server (server " + "returned ftp code: 504).", mech->name); + break; + case 534: + infof(data, "Mechanism %s was rejected by the server (server returned " + "ftp code: 534).", mech->name); + break; + default: + if(ret/100 == 5) { + infof(data, "server does not support the security extensions"); + return CURLE_USE_SSL_FAILED; + } + break; + } + return CURLE_LOGIN_DENIED; + } + + /* Authenticate */ + ret = mech->auth(conn->app_data, data, conn); + + if(ret != AUTH_CONTINUE) { + if(ret != AUTH_OK) { + /* Mechanism has dumped the error to stderr, do not error here. */ + return CURLE_USE_SSL_FAILED; + } + DEBUGASSERT(ret == AUTH_OK); + + conn->mech = mech; + conn->sec_complete = 1; + conn->recv[FIRSTSOCKET] = sec_recv; + conn->send[FIRSTSOCKET] = sec_send; + conn->recv[SECONDARYSOCKET] = sec_recv; + conn->send[SECONDARYSOCKET] = sec_send; + conn->command_prot = PROT_SAFE; + /* Set the requested protection level */ + /* BLOCKING */ + (void)sec_set_protection_level(data); + } + + return CURLE_OK; +} + +CURLcode +Curl_sec_login(struct Curl_easy *data, struct connectdata *conn) +{ + return choose_mech(data, conn); +} + + +void +Curl_sec_end(struct connectdata *conn) +{ + if(conn->mech && conn->mech->end) + conn->mech->end(conn->app_data); + Curl_safefree(conn->app_data); + Curl_dyn_free(&conn->in_buffer.buf); + conn->in_buffer.index = 0; + conn->in_buffer.eof_flag = 0; + conn->sec_complete = 0; + conn->data_prot = PROT_CLEAR; + conn->mech = NULL; +} + +#endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */ diff --git a/third_party/curl/lib/ldap.c b/third_party/curl/lib/ldap.c new file mode 100644 index 0000000000..addb9b353a --- /dev/null +++ b/third_party/curl/lib/ldap.c @@ -0,0 +1,1116 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP) + +/* + * Notice that USE_OPENLDAP is only a source code selection switch. When + * libcurl is built with USE_OPENLDAP defined the libcurl source code that + * gets compiled is the code from openldap.c, otherwise the code that gets + * compiled is the code from ldap.c. + * + * When USE_OPENLDAP is defined a recent version of the OpenLDAP library + * might be required for compilation and runtime. In order to use ancient + * OpenLDAP library versions, USE_OPENLDAP shall not be defined. + */ + +/* Wincrypt must be included before anything that could include OpenSSL. */ +#if defined(USE_WIN32_CRYPTO) +#include +/* Undefine wincrypt conflicting symbols for BoringSSL. */ +#undef X509_NAME +#undef X509_EXTENSIONS +#undef PKCS7_ISSUER_AND_SERIAL +#undef PKCS7_SIGNER_INFO +#undef OCSP_REQUEST +#undef OCSP_RESPONSE +#endif + +#ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ +# ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4201) +# endif +# include /* for [P]UNICODE_STRING */ +# ifdef _MSC_VER +# pragma warning(pop) +# endif +# include +# ifndef LDAP_VENDOR_NAME +# error Your Platform SDK is NOT sufficient for LDAP support! \ + Update your Platform SDK, or disable LDAP support! +# else +# include +# endif +#else +# define LDAP_DEPRECATED 1 /* Be sure ldap_init() is defined. */ +# ifdef HAVE_LBER_H +# include +# endif +# include +# if (defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H)) +# include +# endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */ +#endif + +#include "urldata.h" +#include +#include "sendf.h" +#include "escape.h" +#include "progress.h" +#include "transfer.h" +#include "strcase.h" +#include "strtok.h" +#include "curl_ldap.h" +#include "curl_multibyte.h" +#include "curl_base64.h" +#include "connect.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#ifndef HAVE_LDAP_URL_PARSE + +/* Use our own implementation. */ + +struct ldap_urldesc { + char *lud_host; + int lud_port; +#if defined(USE_WIN32_LDAP) + TCHAR *lud_dn; + TCHAR **lud_attrs; +#else + char *lud_dn; + char **lud_attrs; +#endif + int lud_scope; +#if defined(USE_WIN32_LDAP) + TCHAR *lud_filter; +#else + char *lud_filter; +#endif + char **lud_exts; + size_t lud_attrs_dups; /* how many were dup'ed, this field is not in the + "real" struct so can only be used in code + without HAVE_LDAP_URL_PARSE defined */ +}; + +#undef LDAPURLDesc +#define LDAPURLDesc struct ldap_urldesc + +static int _ldap_url_parse(struct Curl_easy *data, + const struct connectdata *conn, + LDAPURLDesc **ludp); +static void _ldap_free_urldesc(LDAPURLDesc *ludp); + +#undef ldap_free_urldesc +#define ldap_free_urldesc _ldap_free_urldesc +#endif + +#ifdef DEBUG_LDAP + #define LDAP_TRACE(x) do { \ + _ldap_trace("%u: ", __LINE__); \ + _ldap_trace x; \ + } while(0) + + static void _ldap_trace(const char *fmt, ...) CURL_PRINTF(1, 2); +#else + #define LDAP_TRACE(x) Curl_nop_stmt +#endif + +#if defined(USE_WIN32_LDAP) && defined(ldap_err2string) +/* Use ansi error strings in UNICODE builds */ +#undef ldap_err2string +#define ldap_err2string ldap_err2stringA +#endif + +#if defined(USE_WIN32_LDAP) && defined(_MSC_VER) && (_MSC_VER <= 1600) +/* Workaround for warning: + 'type cast' : conversion from 'int' to 'void *' of greater size */ +#undef LDAP_OPT_ON +#undef LDAP_OPT_OFF +#define LDAP_OPT_ON ((void *)(size_t)1) +#define LDAP_OPT_OFF ((void *)(size_t)0) +#endif + +static CURLcode ldap_do(struct Curl_easy *data, bool *done); + +/* + * LDAP protocol handler. + */ + +const struct Curl_handler Curl_handler_ldap = { + "ldap", /* scheme */ + ZERO_NULL, /* setup_connection */ + ldap_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_LDAP, /* defport */ + CURLPROTO_LDAP, /* protocol */ + CURLPROTO_LDAP, /* family */ + PROTOPT_NONE /* flags */ +}; + +#ifdef HAVE_LDAP_SSL +/* + * LDAPS protocol handler. + */ + +const struct Curl_handler Curl_handler_ldaps = { + "ldaps", /* scheme */ + ZERO_NULL, /* setup_connection */ + ldap_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_LDAPS, /* defport */ + CURLPROTO_LDAPS, /* protocol */ + CURLPROTO_LDAP, /* family */ + PROTOPT_SSL /* flags */ +}; +#endif + +#if defined(USE_WIN32_LDAP) + +#if defined(USE_WINDOWS_SSPI) +static int ldap_win_bind_auth(LDAP *server, const char *user, + const char *passwd, unsigned long authflags) +{ + ULONG method = 0; + SEC_WINNT_AUTH_IDENTITY cred; + int rc = LDAP_AUTH_METHOD_NOT_SUPPORTED; + + memset(&cred, 0, sizeof(cred)); + +#if defined(USE_SPNEGO) + if(authflags & CURLAUTH_NEGOTIATE) { + method = LDAP_AUTH_NEGOTIATE; + } + else +#endif +#if defined(USE_NTLM) + if(authflags & CURLAUTH_NTLM) { + method = LDAP_AUTH_NTLM; + } + else +#endif +#if !defined(CURL_DISABLE_DIGEST_AUTH) + if(authflags & CURLAUTH_DIGEST) { + method = LDAP_AUTH_DIGEST; + } + else +#endif + { + /* required anyway if one of upper preprocessor definitions enabled */ + } + + if(method && user && passwd) { + CURLcode res = Curl_create_sspi_identity(user, passwd, &cred); + rc = (int)res; + if(!rc) { + rc = (int)ldap_bind_s(server, NULL, (TCHAR *)&cred, method); + Curl_sspi_free_identity(&cred); + } + } + else { + /* proceed with current user credentials */ + method = LDAP_AUTH_NEGOTIATE; + rc = (int)ldap_bind_s(server, NULL, NULL, method); + } + return rc; +} +#endif /* #if defined(USE_WINDOWS_SSPI) */ + +static int ldap_win_bind(struct Curl_easy *data, LDAP *server, + const char *user, const char *passwd) +{ + int rc = LDAP_INVALID_CREDENTIALS; + + PTCHAR inuser = NULL; + PTCHAR inpass = NULL; + + if(user && passwd && (data->set.httpauth & CURLAUTH_BASIC)) { + inuser = curlx_convert_UTF8_to_tchar((char *) user); + inpass = curlx_convert_UTF8_to_tchar((char *) passwd); + + rc = (int)ldap_simple_bind_s(server, inuser, inpass); + + curlx_unicodefree(inuser); + curlx_unicodefree(inpass); + } +#if defined(USE_WINDOWS_SSPI) + else { + rc = (int)ldap_win_bind_auth(server, user, passwd, data->set.httpauth); + } +#endif + + return rc; +} +#endif /* #if defined(USE_WIN32_LDAP) */ + +#if defined(USE_WIN32_LDAP) +#define FREE_ON_WINLDAP(x) curlx_unicodefree(x) +#define curl_ldap_num_t ULONG +#else +#define FREE_ON_WINLDAP(x) +#define curl_ldap_num_t int +#endif + + +static CURLcode ldap_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + int rc = 0; + LDAP *server = NULL; + LDAPURLDesc *ludp = NULL; + LDAPMessage *ldapmsg = NULL; + LDAPMessage *entryIterator; + int num = 0; + struct connectdata *conn = data->conn; + int ldap_proto = LDAP_VERSION3; + int ldap_ssl = 0; + char *val_b64 = NULL; + size_t val_b64_sz = 0; +#ifdef LDAP_OPT_NETWORK_TIMEOUT + struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */ +#endif +#if defined(USE_WIN32_LDAP) + TCHAR *host = NULL; +#else + char *host = NULL; +#endif + char *user = NULL; + char *passwd = NULL; + + *done = TRUE; /* unconditionally */ + infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d", + LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION); + infof(data, "LDAP local: %s", data->state.url); + +#ifdef HAVE_LDAP_URL_PARSE + rc = ldap_url_parse(data->state.url, &ludp); +#else + rc = _ldap_url_parse(data, conn, &ludp); +#endif + if(rc) { + failf(data, "Bad LDAP URL: %s", ldap_err2string((curl_ldap_num_t)rc)); + result = CURLE_URL_MALFORMAT; + goto quit; + } + + /* Get the URL scheme (either ldap or ldaps) */ + if(conn->given->flags & PROTOPT_SSL) + ldap_ssl = 1; + infof(data, "LDAP local: trying to establish %s connection", + ldap_ssl ? "encrypted" : "cleartext"); + +#if defined(USE_WIN32_LDAP) + host = curlx_convert_UTF8_to_tchar(conn->host.name); + if(!host) { + result = CURLE_OUT_OF_MEMORY; + + goto quit; + } +#else + host = conn->host.name; +#endif + + if(data->state.aptr.user) { + user = conn->user; + passwd = conn->passwd; + } + +#ifdef LDAP_OPT_NETWORK_TIMEOUT + ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout); +#endif + ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); + + if(ldap_ssl) { +#ifdef HAVE_LDAP_SSL +#ifdef USE_WIN32_LDAP + /* Win32 LDAP SDK does not support insecure mode without CA! */ + server = ldap_sslinit(host, (curl_ldap_num_t)conn->primary.remote_port, 1); + ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON); +#else + int ldap_option; + char *ldap_ca = conn->ssl_config.CAfile; +#if defined(CURL_HAS_NOVELL_LDAPSDK) + rc = ldapssl_client_init(NULL, NULL); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc)); + result = CURLE_SSL_CERTPROBLEM; + goto quit; + } + if(conn->ssl_config.verifypeer) { + /* Novell SDK supports DER or BASE64 files. */ + int cert_type = LDAPSSL_CERT_FILETYPE_B64; + if((data->set.ssl.cert_type) && + (strcasecompare(data->set.ssl.cert_type, "DER"))) + cert_type = LDAPSSL_CERT_FILETYPE_DER; + if(!ldap_ca) { + failf(data, "LDAP local: ERROR %s CA cert not set", + (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM")); + result = CURLE_SSL_CERTPROBLEM; + goto quit; + } + infof(data, "LDAP local: using %s CA cert '%s'", + (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"), + ldap_ca); + rc = ldapssl_add_trusted_cert(ldap_ca, cert_type); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: ERROR setting %s CA cert: %s", + (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"), + ldap_err2string(rc)); + result = CURLE_SSL_CERTPROBLEM; + goto quit; + } + ldap_option = LDAPSSL_VERIFY_SERVER; + } + else + ldap_option = LDAPSSL_VERIFY_NONE; + rc = ldapssl_set_verify_mode(ldap_option); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: ERROR setting cert verify mode: %s", + ldap_err2string(rc)); + result = CURLE_SSL_CERTPROBLEM; + goto quit; + } + server = ldapssl_init(host, conn->primary.remote_port, 1); + if(!server) { + failf(data, "LDAP local: Cannot connect to %s:%u", + conn->host.dispname, conn->primary.remote_port); + result = CURLE_COULDNT_CONNECT; + goto quit; + } +#elif defined(LDAP_OPT_X_TLS) + if(conn->ssl_config.verifypeer) { + /* OpenLDAP SDK supports BASE64 files. */ + if((data->set.ssl.cert_type) && + (!strcasecompare(data->set.ssl.cert_type, "PEM"))) { + failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type"); + result = CURLE_SSL_CERTPROBLEM; + goto quit; + } + if(!ldap_ca) { + failf(data, "LDAP local: ERROR PEM CA cert not set"); + result = CURLE_SSL_CERTPROBLEM; + goto quit; + } + infof(data, "LDAP local: using PEM CA cert: %s", ldap_ca); + rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: ERROR setting PEM CA cert: %s", + ldap_err2string(rc)); + result = CURLE_SSL_CERTPROBLEM; + goto quit; + } + ldap_option = LDAP_OPT_X_TLS_DEMAND; + } + else + ldap_option = LDAP_OPT_X_TLS_NEVER; + + rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: ERROR setting cert verify mode: %s", + ldap_err2string(rc)); + result = CURLE_SSL_CERTPROBLEM; + goto quit; + } + server = ldap_init(host, conn->primary.remote_port); + if(!server) { + failf(data, "LDAP local: Cannot connect to %s:%u", + conn->host.dispname, conn->primary.remote_port); + result = CURLE_COULDNT_CONNECT; + goto quit; + } + ldap_option = LDAP_OPT_X_TLS_HARD; + rc = ldap_set_option(server, LDAP_OPT_X_TLS, &ldap_option); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s", + ldap_err2string(rc)); + result = CURLE_SSL_CERTPROBLEM; + goto quit; + } +/* + rc = ldap_start_tls_s(server, NULL, NULL); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s", + ldap_err2string(rc)); + result = CURLE_SSL_CERTPROBLEM; + goto quit; + } +*/ +#else + (void)ldap_option; + (void)ldap_ca; + /* we should probably never come up to here since configure + should check in first place if we can support LDAP SSL/TLS */ + failf(data, "LDAP local: SSL/TLS not supported with this version " + "of the OpenLDAP toolkit\n"); + result = CURLE_SSL_CERTPROBLEM; + goto quit; +#endif +#endif +#endif /* CURL_LDAP_USE_SSL */ + } + else if(data->set.use_ssl > CURLUSESSL_TRY) { + failf(data, "LDAP local: explicit TLS not supported"); + result = CURLE_NOT_BUILT_IN; + goto quit; + } + else { + server = ldap_init(host, (curl_ldap_num_t)conn->primary.remote_port); + if(!server) { + failf(data, "LDAP local: Cannot connect to %s:%u", + conn->host.dispname, conn->primary.remote_port); + result = CURLE_COULDNT_CONNECT; + goto quit; + } + } +#ifdef USE_WIN32_LDAP + ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); + rc = ldap_win_bind(data, server, user, passwd); +#else + rc = ldap_simple_bind_s(server, user, passwd); +#endif + if(!ldap_ssl && rc) { + ldap_proto = LDAP_VERSION2; + ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); +#ifdef USE_WIN32_LDAP + rc = ldap_win_bind(data, server, user, passwd); +#else + rc = ldap_simple_bind_s(server, user, passwd); +#endif + } + if(rc) { +#ifdef USE_WIN32_LDAP + failf(data, "LDAP local: bind via ldap_win_bind %s", + ldap_err2string((ULONG)rc)); +#else + failf(data, "LDAP local: bind via ldap_simple_bind_s %s", + ldap_err2string(rc)); +#endif + result = CURLE_LDAP_CANNOT_BIND; + goto quit; + } + + Curl_pgrsSetDownloadCounter(data, 0); + rc = (int)ldap_search_s(server, ludp->lud_dn, + (curl_ldap_num_t)ludp->lud_scope, + ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); + + if(rc && rc != LDAP_SIZELIMIT_EXCEEDED) { + failf(data, "LDAP remote: %s", ldap_err2string((curl_ldap_num_t)rc)); + result = CURLE_LDAP_SEARCH_FAILED; + goto quit; + } + + num = 0; + for(entryIterator = ldap_first_entry(server, ldapmsg); + entryIterator; + entryIterator = ldap_next_entry(server, entryIterator), num++) { + BerElement *ber = NULL; +#if defined(USE_WIN32_LDAP) + TCHAR *attribute; +#else + char *attribute; +#endif + int i; + + /* Get the DN and write it to the client */ + { + char *name; + size_t name_len; +#if defined(USE_WIN32_LDAP) + TCHAR *dn = ldap_get_dn(server, entryIterator); + name = curlx_convert_tchar_to_UTF8(dn); + if(!name) { + ldap_memfree(dn); + + result = CURLE_OUT_OF_MEMORY; + + goto quit; + } +#else + char *dn = name = ldap_get_dn(server, entryIterator); +#endif + name_len = strlen(name); + + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"DN: ", 4); + if(result) { + FREE_ON_WINLDAP(name); + ldap_memfree(dn); + goto quit; + } + + result = Curl_client_write(data, CLIENTWRITE_BODY, name, name_len); + if(result) { + FREE_ON_WINLDAP(name); + ldap_memfree(dn); + goto quit; + } + + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); + if(result) { + FREE_ON_WINLDAP(name); + ldap_memfree(dn); + + goto quit; + } + + FREE_ON_WINLDAP(name); + ldap_memfree(dn); + } + + /* Get the attributes and write them to the client */ + for(attribute = ldap_first_attribute(server, entryIterator, &ber); + attribute; + attribute = ldap_next_attribute(server, entryIterator, ber)) { + BerValue **vals; + size_t attr_len; +#if defined(USE_WIN32_LDAP) + char *attr = curlx_convert_tchar_to_UTF8(attribute); + if(!attr) { + if(ber) + ber_free(ber, 0); + + result = CURLE_OUT_OF_MEMORY; + + goto quit; + } +#else + char *attr = attribute; +#endif + attr_len = strlen(attr); + + vals = ldap_get_values_len(server, entryIterator, attribute); + if(vals) { + for(i = 0; (vals[i] != NULL); i++) { + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1); + if(result) { + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + + result = Curl_client_write(data, CLIENTWRITE_BODY, attr, attr_len); + if(result) { + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)": ", 2); + if(result) { + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + + if((attr_len > 7) && + (strcmp(";binary", attr + (attr_len - 7)) == 0)) { + /* Binary attribute, encode to base64. */ + result = Curl_base64_encode(vals[i]->bv_val, vals[i]->bv_len, + &val_b64, &val_b64_sz); + if(result) { + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + + if(val_b64_sz > 0) { + result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64, + val_b64_sz); + free(val_b64); + if(result) { + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + } + } + else { + result = Curl_client_write(data, CLIENTWRITE_BODY, vals[i]->bv_val, + vals[i]->bv_len); + if(result) { + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + } + + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); + if(result) { + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + } + + /* Free memory used to store values */ + ldap_value_free_len(vals); + } + + /* Free the attribute as we are done with it */ + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); + if(result) + goto quit; + } + + if(ber) + ber_free(ber, 0); + } + +quit: + if(ldapmsg) { + ldap_msgfree(ldapmsg); + LDAP_TRACE(("Received %d entries\n", num)); + } + if(rc == LDAP_SIZELIMIT_EXCEEDED) + infof(data, "There are more than %d entries", num); + if(ludp) + ldap_free_urldesc(ludp); + if(server) + ldap_unbind_s(server); +#if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK) + if(ldap_ssl) + ldapssl_client_deinit(); +#endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */ + + FREE_ON_WINLDAP(host); + + /* no data to transfer */ + Curl_xfer_setup_nop(data); + connclose(conn, "LDAP connection always disable reuse"); + + return result; +} + +#ifdef DEBUG_LDAP +static void _ldap_trace(const char *fmt, ...) +{ + static int do_trace = -1; + va_list args; + + if(do_trace == -1) { + const char *env = getenv("CURL_TRACE"); + do_trace = (env && strtol(env, NULL, 10) > 0); + } + if(!do_trace) + return; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} +#endif + +#ifndef HAVE_LDAP_URL_PARSE + +/* + * Return scope-value for a scope-string. + */ +static int str2scope(const char *p) +{ + if(strcasecompare(p, "one")) + return LDAP_SCOPE_ONELEVEL; + if(strcasecompare(p, "onetree")) + return LDAP_SCOPE_ONELEVEL; + if(strcasecompare(p, "base")) + return LDAP_SCOPE_BASE; + if(strcasecompare(p, "sub")) + return LDAP_SCOPE_SUBTREE; + if(strcasecompare(p, "subtree")) + return LDAP_SCOPE_SUBTREE; + return (-1); +} + +/* + * Split 'str' into strings separated by commas. + * Note: out[] points into 'str'. + */ +static bool split_str(char *str, char ***out, size_t *count) +{ + char **res; + char *lasts; + char *s; + size_t i; + size_t items = 1; + + s = strchr(str, ','); + while(s) { + items++; + s = strchr(++s, ','); + } + + res = calloc(items, sizeof(char *)); + if(!res) + return FALSE; + + for(i = 0, s = strtok_r(str, ",", &lasts); s && i < items; + s = strtok_r(NULL, ",", &lasts), i++) + res[i] = s; + + *out = res; + *count = items; + + return TRUE; +} + +/* + * Break apart the pieces of an LDAP URL. + * Syntax: + * ldap://:/???? + * + * already known from 'conn->host.name'. + * already known from 'conn->remote_port'. + * extract the rest from 'data->state.path+1'. All fields are optional. + * e.g. + * ldap://:/??? + * yields ludp->lud_dn = "". + * + * Defined in RFC4516 section 2. + */ +static int _ldap_url_parse2(struct Curl_easy *data, + const struct connectdata *conn, LDAPURLDesc *ludp) +{ + int rc = LDAP_SUCCESS; + char *p; + char *path; + char *q = NULL; + char *query = NULL; + size_t i; + + if(!data || + !data->state.up.path || + data->state.up.path[0] != '/' || + !strncasecompare("LDAP", data->state.up.scheme, 4)) + return LDAP_INVALID_SYNTAX; + + ludp->lud_scope = LDAP_SCOPE_BASE; + ludp->lud_port = conn->remote_port; + ludp->lud_host = conn->host.name; + + /* Duplicate the path */ + p = path = strdup(data->state.up.path + 1); + if(!path) + return LDAP_NO_MEMORY; + + /* Duplicate the query if present */ + if(data->state.up.query) { + q = query = strdup(data->state.up.query); + if(!query) { + free(path); + return LDAP_NO_MEMORY; + } + } + + /* Parse the DN (Distinguished Name) */ + if(*p) { + char *dn = p; + char *unescaped; + CURLcode result; + + LDAP_TRACE(("DN '%s'\n", dn)); + + /* Unescape the DN */ + result = Curl_urldecode(dn, 0, &unescaped, NULL, REJECT_ZERO); + if(result) { + rc = LDAP_NO_MEMORY; + + goto quit; + } + +#if defined(USE_WIN32_LDAP) + /* Convert the unescaped string to a tchar */ + ludp->lud_dn = curlx_convert_UTF8_to_tchar(unescaped); + + /* Free the unescaped string as we are done with it */ + free(unescaped); + + if(!ludp->lud_dn) { + rc = LDAP_NO_MEMORY; + + goto quit; + } +#else + ludp->lud_dn = unescaped; +#endif + } + + p = q; + if(!p) + goto quit; + + /* Parse the attributes. skip "??" */ + q = strchr(p, '?'); + if(q) + *q++ = '\0'; + + if(*p) { + char **attributes; + size_t count = 0; + + /* Split the string into an array of attributes */ + if(!split_str(p, &attributes, &count)) { + rc = LDAP_NO_MEMORY; + + goto quit; + } + + /* Allocate our array (+1 for the NULL entry) */ +#if defined(USE_WIN32_LDAP) + ludp->lud_attrs = calloc(count + 1, sizeof(TCHAR *)); +#else + ludp->lud_attrs = calloc(count + 1, sizeof(char *)); +#endif + if(!ludp->lud_attrs) { + free(attributes); + + rc = LDAP_NO_MEMORY; + + goto quit; + } + + for(i = 0; i < count; i++) { + char *unescaped; + CURLcode result; + + LDAP_TRACE(("attr[%zu] '%s'\n", i, attributes[i])); + + /* Unescape the attribute */ + result = Curl_urldecode(attributes[i], 0, &unescaped, NULL, + REJECT_ZERO); + if(result) { + free(attributes); + + rc = LDAP_NO_MEMORY; + + goto quit; + } + +#if defined(USE_WIN32_LDAP) + /* Convert the unescaped string to a tchar */ + ludp->lud_attrs[i] = curlx_convert_UTF8_to_tchar(unescaped); + + /* Free the unescaped string as we are done with it */ + free(unescaped); + + if(!ludp->lud_attrs[i]) { + free(attributes); + + rc = LDAP_NO_MEMORY; + + goto quit; + } +#else + ludp->lud_attrs[i] = unescaped; +#endif + + ludp->lud_attrs_dups++; + } + + free(attributes); + } + + p = q; + if(!p) + goto quit; + + /* Parse the scope. skip "??" */ + q = strchr(p, '?'); + if(q) + *q++ = '\0'; + + if(*p) { + ludp->lud_scope = str2scope(p); + if(ludp->lud_scope == -1) { + rc = LDAP_INVALID_SYNTAX; + + goto quit; + } + LDAP_TRACE(("scope %d\n", ludp->lud_scope)); + } + + p = q; + if(!p) + goto quit; + + /* Parse the filter */ + q = strchr(p, '?'); + if(q) + *q++ = '\0'; + + if(*p) { + char *filter = p; + char *unescaped; + CURLcode result; + + LDAP_TRACE(("filter '%s'\n", filter)); + + /* Unescape the filter */ + result = Curl_urldecode(filter, 0, &unescaped, NULL, REJECT_ZERO); + if(result) { + rc = LDAP_NO_MEMORY; + + goto quit; + } + +#if defined(USE_WIN32_LDAP) + /* Convert the unescaped string to a tchar */ + ludp->lud_filter = curlx_convert_UTF8_to_tchar(unescaped); + + /* Free the unescaped string as we are done with it */ + free(unescaped); + + if(!ludp->lud_filter) { + rc = LDAP_NO_MEMORY; + + goto quit; + } +#else + ludp->lud_filter = unescaped; +#endif + } + + p = q; + if(p && !*p) { + rc = LDAP_INVALID_SYNTAX; + + goto quit; + } + +quit: + free(path); + free(query); + + return rc; +} + +static int _ldap_url_parse(struct Curl_easy *data, + const struct connectdata *conn, + LDAPURLDesc **ludpp) +{ + LDAPURLDesc *ludp = calloc(1, sizeof(*ludp)); + int rc; + + *ludpp = NULL; + if(!ludp) + return LDAP_NO_MEMORY; + + rc = _ldap_url_parse2(data, conn, ludp); + if(rc != LDAP_SUCCESS) { + _ldap_free_urldesc(ludp); + ludp = NULL; + } + *ludpp = ludp; + return (rc); +} + +static void _ldap_free_urldesc(LDAPURLDesc *ludp) +{ + if(!ludp) + return; + +#if defined(USE_WIN32_LDAP) + curlx_unicodefree(ludp->lud_dn); + curlx_unicodefree(ludp->lud_filter); +#else + free(ludp->lud_dn); + free(ludp->lud_filter); +#endif + + if(ludp->lud_attrs) { + size_t i; + for(i = 0; i < ludp->lud_attrs_dups; i++) { +#if defined(USE_WIN32_LDAP) + curlx_unicodefree(ludp->lud_attrs[i]); +#else + free(ludp->lud_attrs[i]); +#endif + } + free(ludp->lud_attrs); + } + + free(ludp); +} +#endif /* !HAVE_LDAP_URL_PARSE */ +#endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */ diff --git a/third_party/curl/lib/libcurl.rc b/third_party/curl/lib/libcurl.rc new file mode 100644 index 0000000000..1ceb4691fb --- /dev/null +++ b/third_party/curl/lib/libcurl.rc @@ -0,0 +1,65 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include +#include "../include/curl/curlver.h" + +LANGUAGE 0, 0 + +#define RC_VERSION LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR, LIBCURL_VERSION_PATCH, 0 + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RC_VERSION + PRODUCTVERSION RC_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#if defined(DEBUGBUILD) || defined(UNITTESTS) || defined(CURLDEBUG) || defined(_DEBUG) + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0L + +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "The curl library, https://curl.se/\0" + VALUE "FileDescription", "libcurl Shared Library\0" + VALUE "FileVersion", LIBCURL_VERSION "\0" + VALUE "InternalName", "libcurl\0" + VALUE "OriginalFilename", "libcurl.dll\0" + VALUE "ProductName", "The curl library\0" + VALUE "ProductVersion", LIBCURL_VERSION "\0" + VALUE "LegalCopyright", "Copyright (C) " LIBCURL_COPYRIGHT "\0" + VALUE "License", "https://curl.se/docs/copyright.html\0" + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/third_party/curl/lib/libcurl.vers.in b/third_party/curl/lib/libcurl.vers.in new file mode 100644 index 0000000000..ae978a485d --- /dev/null +++ b/third_party/curl/lib/libcurl.vers.in @@ -0,0 +1,13 @@ +HIDDEN +{ + local: + __*; + _rest*; + _save*; +}; + +CURL_@CURL_LT_SHLIB_VERSIONED_FLAVOUR@4 +{ + global: curl_*; + local: *; +}; diff --git a/third_party/curl/lib/llist.c b/third_party/curl/lib/llist.c new file mode 100644 index 0000000000..716f0cd576 --- /dev/null +++ b/third_party/curl/lib/llist.c @@ -0,0 +1,162 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "llist.h" +#include "curl_memory.h" + +/* this must be the last include file */ +#include "memdebug.h" + +/* + * @unittest: 1300 + */ +void +Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) +{ + l->size = 0; + l->dtor = dtor; + l->head = NULL; + l->tail = NULL; +} + +/* + * Curl_llist_insert_next() + * + * Inserts a new list element after the given one 'e'. If the given existing + * entry is NULL and the list already has elements, the new one will be + * inserted first in the list. + * + * The 'ne' argument should be a pointer into the object to store. + * + * @unittest: 1300 + */ +void +Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_element *e, + const void *p, + struct Curl_llist_element *ne) +{ + ne->ptr = (void *) p; + if(list->size == 0) { + list->head = ne; + list->head->prev = NULL; + list->head->next = NULL; + list->tail = ne; + } + else { + /* if 'e' is NULL here, we insert the new element first in the list */ + ne->next = e?e->next:list->head; + ne->prev = e; + if(!e) { + list->head->prev = ne; + list->head = ne; + } + else if(e->next) { + e->next->prev = ne; + } + else { + list->tail = ne; + } + if(e) + e->next = ne; + } + + ++list->size; +} + +/* + * Curl_llist_append() + * + * Adds a new list element to the end of the list. + * + * The 'ne' argument should be a pointer into the object to store. + * + * @unittest: 1300 + */ +void +Curl_llist_append(struct Curl_llist *list, const void *p, + struct Curl_llist_element *ne) +{ + Curl_llist_insert_next(list, list->tail, p, ne); +} + +/* + * @unittest: 1300 + */ +void +Curl_llist_remove(struct Curl_llist *list, struct Curl_llist_element *e, + void *user) +{ + void *ptr; + if(!e || list->size == 0) + return; + + if(e == list->head) { + list->head = e->next; + + if(!list->head) + list->tail = NULL; + else + e->next->prev = NULL; + } + else { + if(e->prev) + e->prev->next = e->next; + + if(!e->next) + list->tail = e->prev; + else + e->next->prev = e->prev; + } + + ptr = e->ptr; + + e->ptr = NULL; + e->prev = NULL; + e->next = NULL; + + --list->size; + + /* call the dtor() last for when it actually frees the 'e' memory itself */ + if(list->dtor) + list->dtor(user, ptr); +} + +void +Curl_llist_destroy(struct Curl_llist *list, void *user) +{ + if(list) { + while(list->size > 0) + Curl_llist_remove(list, list->tail, user); + } +} + +size_t +Curl_llist_count(struct Curl_llist *list) +{ + return list->size; +} diff --git a/third_party/curl/lib/llist.h b/third_party/curl/lib/llist.h new file mode 100644 index 0000000000..d75582fa97 --- /dev/null +++ b/third_party/curl/lib/llist.h @@ -0,0 +1,54 @@ +#ifndef HEADER_CURL_LLIST_H +#define HEADER_CURL_LLIST_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include + +typedef void (*Curl_llist_dtor)(void *, void *); + +struct Curl_llist_element { + void *ptr; + struct Curl_llist_element *prev; + struct Curl_llist_element *next; +}; + +struct Curl_llist { + struct Curl_llist_element *head; + struct Curl_llist_element *tail; + Curl_llist_dtor dtor; + size_t size; +}; + +void Curl_llist_init(struct Curl_llist *, Curl_llist_dtor); +void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_element *, + const void *, struct Curl_llist_element *node); +void Curl_llist_append(struct Curl_llist *, + const void *, struct Curl_llist_element *node); +void Curl_llist_remove(struct Curl_llist *, struct Curl_llist_element *, + void *); +size_t Curl_llist_count(struct Curl_llist *); +void Curl_llist_destroy(struct Curl_llist *, void *); +#endif /* HEADER_CURL_LLIST_H */ diff --git a/third_party/curl/lib/macos.c b/third_party/curl/lib/macos.c new file mode 100644 index 0000000000..205d30db32 --- /dev/null +++ b/third_party/curl/lib/macos.c @@ -0,0 +1,55 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef CURL_MACOS_CALL_COPYPROXIES + +#include + +#include "macos.h" + +#include + +CURLcode Curl_macos_init(void) +{ + { + /* + * The automagic conversion from IPv4 literals to IPv6 literals only + * works if the SCDynamicStoreCopyProxies system function gets called + * first. As Curl currently does not support system-wide HTTP proxies, we + * therefore do not use any value this function might return. + * + * This function is only available on macOS and is not needed for + * IPv4-only builds, hence the conditions for defining + * CURL_MACOS_CALL_COPYPROXIES in curl_setup.h. + */ + CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); + if(dict) + CFRelease(dict); + } + return CURLE_OK; +} + +#endif diff --git a/third_party/curl/lib/macos.h b/third_party/curl/lib/macos.h new file mode 100644 index 0000000000..637860e80f --- /dev/null +++ b/third_party/curl/lib/macos.h @@ -0,0 +1,39 @@ +#ifndef HEADER_CURL_MACOS_H +#define HEADER_CURL_MACOS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef CURL_MACOS_CALL_COPYPROXIES + +CURLcode Curl_macos_init(void); + +#else + +#define Curl_macos_init() CURLE_OK + +#endif + +#endif /* HEADER_CURL_MACOS_H */ diff --git a/third_party/curl/lib/md4.c b/third_party/curl/lib/md4.c new file mode 100644 index 0000000000..f006bdcf05 --- /dev/null +++ b/third_party/curl/lib/md4.c @@ -0,0 +1,530 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_CURL_NTLM_CORE) + +#include + +#include "strdup.h" +#include "curl_md4.h" +#include "warnless.h" + +#ifdef USE_OPENSSL +#include +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) && !defined(USE_AMISSL) +/* OpenSSL 3.0.0 marks the MD4 functions as deprecated */ +#define OPENSSL_NO_MD4 +#else +/* Cover also OPENSSL_NO_MD4 configured in openssl */ +#include +#endif +#endif /* USE_OPENSSL */ + +#ifdef USE_WOLFSSL +#include +#define VOID_MD4_INIT +#ifdef NO_MD4 +#define WOLFSSL_NO_MD4 +#endif +#endif + +#ifdef USE_MBEDTLS +#include +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +#include +#else +#include +#endif +#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \ + (MBEDTLS_VERSION_NUMBER < 0x03000000) + #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS +#endif +#endif /* USE_MBEDTLS */ + +#if defined(USE_GNUTLS) +#include +/* When OpenSSL or wolfSSL is available, we use their MD4 functions. */ +#elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) +#include +#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) +#include +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ + defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ + (__MAC_OS_X_VERSION_MIN_REQUIRED < 101500)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000) && \ + defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ + (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) +#define AN_APPLE_OS +#include +#elif defined(USE_WIN32_CRYPTO) +#include +#elif(defined(USE_MBEDTLS) && defined(MBEDTLS_MD4_C)) +#include +#endif + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +#if defined(USE_GNUTLS) + +typedef struct md4_ctx MD4_CTX; + +static int MD4_Init(MD4_CTX *ctx) +{ + md4_init(ctx); + return 1; +} + +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +{ + md4_update(ctx, size, data); +} + +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +{ + md4_digest(ctx, MD4_DIGEST_SIZE, result); +} + +#elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) + +#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) + +#elif defined(AN_APPLE_OS) +typedef CC_MD4_CTX MD4_CTX; + +static int MD4_Init(MD4_CTX *ctx) +{ + return CC_MD4_Init(ctx); +} + +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +{ + (void)CC_MD4_Update(ctx, data, (CC_LONG)size); +} + +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +{ + (void)CC_MD4_Final(result, ctx); +} + +#elif defined(USE_WIN32_CRYPTO) + +struct md4_ctx { + HCRYPTPROV hCryptProv; + HCRYPTHASH hHash; +}; +typedef struct md4_ctx MD4_CTX; + +static int MD4_Init(MD4_CTX *ctx) +{ + ctx->hCryptProv = 0; + ctx->hHash = 0; + + if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return 0; + + if(!CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash)) { + CryptReleaseContext(ctx->hCryptProv, 0); + ctx->hCryptProv = 0; + return 0; + } + + return 1; +} + +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +{ + CryptHashData(ctx->hHash, (BYTE *)data, (unsigned int) size, 0); +} + +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +{ + unsigned long length = 0; + + CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); + if(length == MD4_DIGEST_LENGTH) + CryptGetHashParam(ctx->hHash, HP_HASHVAL, result, &length, 0); + + if(ctx->hHash) + CryptDestroyHash(ctx->hHash); + + if(ctx->hCryptProv) + CryptReleaseContext(ctx->hCryptProv, 0); +} + +#elif(defined(USE_MBEDTLS) && defined(MBEDTLS_MD4_C)) + +struct md4_ctx { + void *data; + unsigned long size; +}; +typedef struct md4_ctx MD4_CTX; + +static int MD4_Init(MD4_CTX *ctx) +{ + ctx->data = NULL; + ctx->size = 0; + return 1; +} + +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +{ + if(!ctx->data) { + ctx->data = Curl_memdup(data, size); + if(ctx->data) + ctx->size = size; + } +} + +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +{ + if(ctx->data) { +#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + mbedtls_md4(ctx->data, ctx->size, result); +#else + (void) mbedtls_md4_ret(ctx->data, ctx->size, result); +#endif + + Curl_safefree(ctx->data); + ctx->size = 0; + } +} + +#else +/* When no other crypto library is available, or the crypto library does not + * support MD4, we use this code segment this implementation of it + * + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD4 Message-Digest Algorithm (RFC 1320). + * + * Homepage: + https://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. In case + * this attempt to disclaim copyright and place the software in the public + * domain is deemed null and void, then the software is Copyright (c) 2001 + * Alexander Peslyak and it is hereby released to the general public under the + * following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There is ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there is no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD4_u32plus; + +struct md4_ctx { + MD4_u32plus lo, hi; + MD4_u32plus a, b, c, d; + unsigned char buffer[64]; + MD4_u32plus block[16]; +}; +typedef struct md4_ctx MD4_CTX; + +static int MD4_Init(MD4_CTX *ctx); +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size); +static void MD4_Final(unsigned char *result, MD4_CTX *ctx); + +/* + * The basic MD4 functions. + * + * F and G are optimized compared to their RFC 1320 definitions, with the + * optimization for F borrowed from Colin Plumb's MD5 implementation. + */ +#define MD4_F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define MD4_G(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) +#define MD4_H(x, y, z) ((x) ^ (y) ^ (z)) + +/* + * The MD4 transformation for all three rounds. + */ +#define MD4_STEP(f, a, b, c, d, x, s) \ + (a) += f((b), (c), (d)) + (x); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * does not work. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define MD4_SET(n) \ + (*(MD4_u32plus *)(void *)&ptr[(n) * 4]) +#define MD4_GET(n) \ + MD4_SET(n) +#else +#define MD4_SET(n) \ + (ctx->block[(n)] = \ + (MD4_u32plus)ptr[(n) * 4] | \ + ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD4_u32plus)ptr[(n) * 4 + 3] << 24)) +#define MD4_GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There are no alignment requirements. + */ +static const void *my_md4_body(MD4_CTX *ctx, + const void *data, unsigned long size) +{ + const unsigned char *ptr; + MD4_u32plus a, b, c, d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + MD4_u32plus saved_a, saved_b, saved_c, saved_d; + + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + MD4_STEP(MD4_F, a, b, c, d, MD4_SET(0), 3) + MD4_STEP(MD4_F, d, a, b, c, MD4_SET(1), 7) + MD4_STEP(MD4_F, c, d, a, b, MD4_SET(2), 11) + MD4_STEP(MD4_F, b, c, d, a, MD4_SET(3), 19) + MD4_STEP(MD4_F, a, b, c, d, MD4_SET(4), 3) + MD4_STEP(MD4_F, d, a, b, c, MD4_SET(5), 7) + MD4_STEP(MD4_F, c, d, a, b, MD4_SET(6), 11) + MD4_STEP(MD4_F, b, c, d, a, MD4_SET(7), 19) + MD4_STEP(MD4_F, a, b, c, d, MD4_SET(8), 3) + MD4_STEP(MD4_F, d, a, b, c, MD4_SET(9), 7) + MD4_STEP(MD4_F, c, d, a, b, MD4_SET(10), 11) + MD4_STEP(MD4_F, b, c, d, a, MD4_SET(11), 19) + MD4_STEP(MD4_F, a, b, c, d, MD4_SET(12), 3) + MD4_STEP(MD4_F, d, a, b, c, MD4_SET(13), 7) + MD4_STEP(MD4_F, c, d, a, b, MD4_SET(14), 11) + MD4_STEP(MD4_F, b, c, d, a, MD4_SET(15), 19) + +/* Round 2 */ + MD4_STEP(MD4_G, a, b, c, d, MD4_GET(0) + 0x5a827999, 3) + MD4_STEP(MD4_G, d, a, b, c, MD4_GET(4) + 0x5a827999, 5) + MD4_STEP(MD4_G, c, d, a, b, MD4_GET(8) + 0x5a827999, 9) + MD4_STEP(MD4_G, b, c, d, a, MD4_GET(12) + 0x5a827999, 13) + MD4_STEP(MD4_G, a, b, c, d, MD4_GET(1) + 0x5a827999, 3) + MD4_STEP(MD4_G, d, a, b, c, MD4_GET(5) + 0x5a827999, 5) + MD4_STEP(MD4_G, c, d, a, b, MD4_GET(9) + 0x5a827999, 9) + MD4_STEP(MD4_G, b, c, d, a, MD4_GET(13) + 0x5a827999, 13) + MD4_STEP(MD4_G, a, b, c, d, MD4_GET(2) + 0x5a827999, 3) + MD4_STEP(MD4_G, d, a, b, c, MD4_GET(6) + 0x5a827999, 5) + MD4_STEP(MD4_G, c, d, a, b, MD4_GET(10) + 0x5a827999, 9) + MD4_STEP(MD4_G, b, c, d, a, MD4_GET(14) + 0x5a827999, 13) + MD4_STEP(MD4_G, a, b, c, d, MD4_GET(3) + 0x5a827999, 3) + MD4_STEP(MD4_G, d, a, b, c, MD4_GET(7) + 0x5a827999, 5) + MD4_STEP(MD4_G, c, d, a, b, MD4_GET(11) + 0x5a827999, 9) + MD4_STEP(MD4_G, b, c, d, a, MD4_GET(15) + 0x5a827999, 13) + +/* Round 3 */ + MD4_STEP(MD4_H, a, b, c, d, MD4_GET(0) + 0x6ed9eba1, 3) + MD4_STEP(MD4_H, d, a, b, c, MD4_GET(8) + 0x6ed9eba1, 9) + MD4_STEP(MD4_H, c, d, a, b, MD4_GET(4) + 0x6ed9eba1, 11) + MD4_STEP(MD4_H, b, c, d, a, MD4_GET(12) + 0x6ed9eba1, 15) + MD4_STEP(MD4_H, a, b, c, d, MD4_GET(2) + 0x6ed9eba1, 3) + MD4_STEP(MD4_H, d, a, b, c, MD4_GET(10) + 0x6ed9eba1, 9) + MD4_STEP(MD4_H, c, d, a, b, MD4_GET(6) + 0x6ed9eba1, 11) + MD4_STEP(MD4_H, b, c, d, a, MD4_GET(14) + 0x6ed9eba1, 15) + MD4_STEP(MD4_H, a, b, c, d, MD4_GET(1) + 0x6ed9eba1, 3) + MD4_STEP(MD4_H, d, a, b, c, MD4_GET(9) + 0x6ed9eba1, 9) + MD4_STEP(MD4_H, c, d, a, b, MD4_GET(5) + 0x6ed9eba1, 11) + MD4_STEP(MD4_H, b, c, d, a, MD4_GET(13) + 0x6ed9eba1, 15) + MD4_STEP(MD4_H, a, b, c, d, MD4_GET(3) + 0x6ed9eba1, 3) + MD4_STEP(MD4_H, d, a, b, c, MD4_GET(11) + 0x6ed9eba1, 9) + MD4_STEP(MD4_H, c, d, a, b, MD4_GET(7) + 0x6ed9eba1, 11) + MD4_STEP(MD4_H, b, c, d, a, MD4_GET(15) + 0x6ed9eba1, 15) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while(size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +static int MD4_Init(MD4_CTX *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; + return 1; +} + +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +{ + MD4_u32plus saved_lo; + unsigned long used; + + saved_lo = ctx->lo; + ctx->lo = (saved_lo + size) & 0x1fffffff; + if(ctx->lo < saved_lo) + ctx->hi++; + ctx->hi += (MD4_u32plus)size >> 29; + + used = saved_lo & 0x3f; + + if(used) { + unsigned long available = 64 - used; + + if(size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + my_md4_body(ctx, ctx->buffer, 64); + } + + if(size >= 64) { + data = my_md4_body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if(available < 8) { + memset(&ctx->buffer[used], 0, available); + my_md4_body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = curlx_ultouc((ctx->lo)&0xff); + ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8)&0xff); + ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16)&0xff); + ctx->buffer[59] = curlx_ultouc((ctx->lo >> 24)&0xff); + ctx->buffer[60] = curlx_ultouc((ctx->hi)&0xff); + ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8)&0xff); + ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff); + ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24); + + my_md4_body(ctx, ctx->buffer, 64); + + result[0] = curlx_ultouc((ctx->a)&0xff); + result[1] = curlx_ultouc((ctx->a >> 8)&0xff); + result[2] = curlx_ultouc((ctx->a >> 16)&0xff); + result[3] = curlx_ultouc(ctx->a >> 24); + result[4] = curlx_ultouc((ctx->b)&0xff); + result[5] = curlx_ultouc((ctx->b >> 8)&0xff); + result[6] = curlx_ultouc((ctx->b >> 16)&0xff); + result[7] = curlx_ultouc(ctx->b >> 24); + result[8] = curlx_ultouc((ctx->c)&0xff); + result[9] = curlx_ultouc((ctx->c >> 8)&0xff); + result[10] = curlx_ultouc((ctx->c >> 16)&0xff); + result[11] = curlx_ultouc(ctx->c >> 24); + result[12] = curlx_ultouc((ctx->d)&0xff); + result[13] = curlx_ultouc((ctx->d >> 8)&0xff); + result[14] = curlx_ultouc((ctx->d >> 16)&0xff); + result[15] = curlx_ultouc(ctx->d >> 24); + + memset(ctx, 0, sizeof(*ctx)); +} + +#endif /* CRYPTO LIBS */ + +CURLcode Curl_md4it(unsigned char *output, const unsigned char *input, + const size_t len) +{ + MD4_CTX ctx; + +#ifdef VOID_MD4_INIT + MD4_Init(&ctx); +#else + if(!MD4_Init(&ctx)) + return CURLE_FAILED_INIT; +#endif + + MD4_Update(&ctx, input, curlx_uztoui(len)); + MD4_Final(output, &ctx); + return CURLE_OK; +} + +#endif /* USE_CURL_NTLM_CORE */ diff --git a/third_party/curl/lib/md5.c b/third_party/curl/lib/md5.c new file mode 100644 index 0000000000..7b51429b48 --- /dev/null +++ b/third_party/curl/lib/md5.c @@ -0,0 +1,657 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_DIGEST_AUTH) + +#include +#include + +#include "curl_md5.h" +#include "curl_hmac.h" +#include "warnless.h" + +#ifdef USE_MBEDTLS +#include + +#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \ + (MBEDTLS_VERSION_NUMBER < 0x03000000) + #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS +#endif +#endif /* USE_MBEDTLS */ + +#ifdef USE_OPENSSL + #include + #if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0) + #define USE_OPENSSL_MD5 + #endif +#endif + +#ifdef USE_WOLFSSL + #include + #ifndef NO_MD5 + #define USE_WOLFSSL_MD5 + #endif +#endif + +#if defined(USE_GNUTLS) +#include +#elif defined(USE_OPENSSL_MD5) +#include +#elif defined(USE_WOLFSSL_MD5) +#include +#elif defined(USE_MBEDTLS) +#include +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ + defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ + (__MAC_OS_X_VERSION_MIN_REQUIRED < 101500)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000) && \ + defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ + (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) +#define AN_APPLE_OS +#include +#elif defined(USE_WIN32_CRYPTO) +#include +#endif + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if defined(USE_GNUTLS) + +typedef struct md5_ctx my_md5_ctx; + +static CURLcode my_md5_init(my_md5_ctx *ctx) +{ + md5_init(ctx); + return CURLE_OK; +} + +static void my_md5_update(my_md5_ctx *ctx, + const unsigned char *input, + unsigned int inputLen) +{ + md5_update(ctx, inputLen, input); +} + +static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +{ + md5_digest(ctx, 16, digest); +} + +#elif defined(USE_OPENSSL_MD5) || defined(USE_WOLFSSL_MD5) + +typedef MD5_CTX my_md5_ctx; + +static CURLcode my_md5_init(my_md5_ctx *ctx) +{ + if(!MD5_Init(ctx)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static void my_md5_update(my_md5_ctx *ctx, + const unsigned char *input, + unsigned int len) +{ + (void)MD5_Update(ctx, input, len); +} + +static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +{ + (void)MD5_Final(digest, ctx); +} + +#elif defined(USE_MBEDTLS) + +typedef mbedtls_md5_context my_md5_ctx; + +static CURLcode my_md5_init(my_md5_ctx *ctx) +{ +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) + if(mbedtls_md5_starts(ctx)) + return CURLE_OUT_OF_MEMORY; +#elif defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + if(mbedtls_md5_starts_ret(ctx)) + return CURLE_OUT_OF_MEMORY; +#else + (void)mbedtls_md5_starts(ctx); +#endif + return CURLE_OK; +} + +static void my_md5_update(my_md5_ctx *ctx, + const unsigned char *data, + unsigned int length) +{ +#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + (void) mbedtls_md5_update(ctx, data, length); +#else + (void) mbedtls_md5_update_ret(ctx, data, length); +#endif +} + +static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +{ +#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + (void) mbedtls_md5_finish(ctx, digest); +#else + (void) mbedtls_md5_finish_ret(ctx, digest); +#endif +} + +#elif defined(AN_APPLE_OS) + +/* For Apple operating systems: CommonCrypto has the functions we need. + These functions are available on Tiger and later, as well as iOS 2.0 + and later. If you are building for an older cat, well, sorry. + + Declaring the functions as static like this seems to be a bit more + reliable than defining COMMON_DIGEST_FOR_OPENSSL on older cats. */ +# define my_md5_ctx CC_MD5_CTX + +static CURLcode my_md5_init(my_md5_ctx *ctx) +{ + if(!CC_MD5_Init(ctx)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static void my_md5_update(my_md5_ctx *ctx, + const unsigned char *input, + unsigned int inputLen) +{ + CC_MD5_Update(ctx, input, inputLen); +} + +static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +{ + CC_MD5_Final(digest, ctx); +} + +#elif defined(USE_WIN32_CRYPTO) + +struct md5_ctx { + HCRYPTPROV hCryptProv; + HCRYPTHASH hHash; +}; +typedef struct md5_ctx my_md5_ctx; + +static CURLcode my_md5_init(my_md5_ctx *ctx) +{ + if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return CURLE_OUT_OF_MEMORY; + + if(!CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash)) { + CryptReleaseContext(ctx->hCryptProv, 0); + ctx->hCryptProv = 0; + return CURLE_FAILED_INIT; + } + + return CURLE_OK; +} + +static void my_md5_update(my_md5_ctx *ctx, + const unsigned char *input, + unsigned int inputLen) +{ + CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0); +} + +static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +{ + unsigned long length = 0; + CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); + if(length == 16) + CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0); + if(ctx->hHash) + CryptDestroyHash(ctx->hHash); + if(ctx->hCryptProv) + CryptReleaseContext(ctx->hCryptProv, 0); +} + +#else + +/* When no other crypto library is available we use this code segment */ + +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There is ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there is no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; + +struct md5_ctx { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +}; +typedef struct md5_ctx my_md5_ctx; + +static CURLcode my_md5_init(my_md5_ctx *ctx); +static void my_md5_update(my_md5_ctx *ctx, const void *data, + unsigned long size); +static void my_md5_final(unsigned char *result, my_md5_ctx *ctx); + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define MD5_F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define MD5_G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define MD5_H(x, y, z) (((x) ^ (y)) ^ (z)) +#define MD5_H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define MD5_I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define MD5_STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * does not work. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define MD5_SET(n) \ + (*(MD5_u32plus *)(void *)&ptr[(n) * 4]) +#define MD5_GET(n) \ + MD5_SET(n) +#else +#define MD5_SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define MD5_GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There are no alignment requirements. + */ +static const void *my_md5_body(my_md5_ctx *ctx, + const void *data, unsigned long size) +{ + const unsigned char *ptr; + MD5_u32plus a, b, c, d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + MD5_STEP(MD5_F, a, b, c, d, MD5_SET(0), 0xd76aa478, 7) + MD5_STEP(MD5_F, d, a, b, c, MD5_SET(1), 0xe8c7b756, 12) + MD5_STEP(MD5_F, c, d, a, b, MD5_SET(2), 0x242070db, 17) + MD5_STEP(MD5_F, b, c, d, a, MD5_SET(3), 0xc1bdceee, 22) + MD5_STEP(MD5_F, a, b, c, d, MD5_SET(4), 0xf57c0faf, 7) + MD5_STEP(MD5_F, d, a, b, c, MD5_SET(5), 0x4787c62a, 12) + MD5_STEP(MD5_F, c, d, a, b, MD5_SET(6), 0xa8304613, 17) + MD5_STEP(MD5_F, b, c, d, a, MD5_SET(7), 0xfd469501, 22) + MD5_STEP(MD5_F, a, b, c, d, MD5_SET(8), 0x698098d8, 7) + MD5_STEP(MD5_F, d, a, b, c, MD5_SET(9), 0x8b44f7af, 12) + MD5_STEP(MD5_F, c, d, a, b, MD5_SET(10), 0xffff5bb1, 17) + MD5_STEP(MD5_F, b, c, d, a, MD5_SET(11), 0x895cd7be, 22) + MD5_STEP(MD5_F, a, b, c, d, MD5_SET(12), 0x6b901122, 7) + MD5_STEP(MD5_F, d, a, b, c, MD5_SET(13), 0xfd987193, 12) + MD5_STEP(MD5_F, c, d, a, b, MD5_SET(14), 0xa679438e, 17) + MD5_STEP(MD5_F, b, c, d, a, MD5_SET(15), 0x49b40821, 22) + +/* Round 2 */ + MD5_STEP(MD5_G, a, b, c, d, MD5_GET(1), 0xf61e2562, 5) + MD5_STEP(MD5_G, d, a, b, c, MD5_GET(6), 0xc040b340, 9) + MD5_STEP(MD5_G, c, d, a, b, MD5_GET(11), 0x265e5a51, 14) + MD5_STEP(MD5_G, b, c, d, a, MD5_GET(0), 0xe9b6c7aa, 20) + MD5_STEP(MD5_G, a, b, c, d, MD5_GET(5), 0xd62f105d, 5) + MD5_STEP(MD5_G, d, a, b, c, MD5_GET(10), 0x02441453, 9) + MD5_STEP(MD5_G, c, d, a, b, MD5_GET(15), 0xd8a1e681, 14) + MD5_STEP(MD5_G, b, c, d, a, MD5_GET(4), 0xe7d3fbc8, 20) + MD5_STEP(MD5_G, a, b, c, d, MD5_GET(9), 0x21e1cde6, 5) + MD5_STEP(MD5_G, d, a, b, c, MD5_GET(14), 0xc33707d6, 9) + MD5_STEP(MD5_G, c, d, a, b, MD5_GET(3), 0xf4d50d87, 14) + MD5_STEP(MD5_G, b, c, d, a, MD5_GET(8), 0x455a14ed, 20) + MD5_STEP(MD5_G, a, b, c, d, MD5_GET(13), 0xa9e3e905, 5) + MD5_STEP(MD5_G, d, a, b, c, MD5_GET(2), 0xfcefa3f8, 9) + MD5_STEP(MD5_G, c, d, a, b, MD5_GET(7), 0x676f02d9, 14) + MD5_STEP(MD5_G, b, c, d, a, MD5_GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + MD5_STEP(MD5_H, a, b, c, d, MD5_GET(5), 0xfffa3942, 4) + MD5_STEP(MD5_H2, d, a, b, c, MD5_GET(8), 0x8771f681, 11) + MD5_STEP(MD5_H, c, d, a, b, MD5_GET(11), 0x6d9d6122, 16) + MD5_STEP(MD5_H2, b, c, d, a, MD5_GET(14), 0xfde5380c, 23) + MD5_STEP(MD5_H, a, b, c, d, MD5_GET(1), 0xa4beea44, 4) + MD5_STEP(MD5_H2, d, a, b, c, MD5_GET(4), 0x4bdecfa9, 11) + MD5_STEP(MD5_H, c, d, a, b, MD5_GET(7), 0xf6bb4b60, 16) + MD5_STEP(MD5_H2, b, c, d, a, MD5_GET(10), 0xbebfbc70, 23) + MD5_STEP(MD5_H, a, b, c, d, MD5_GET(13), 0x289b7ec6, 4) + MD5_STEP(MD5_H2, d, a, b, c, MD5_GET(0), 0xeaa127fa, 11) + MD5_STEP(MD5_H, c, d, a, b, MD5_GET(3), 0xd4ef3085, 16) + MD5_STEP(MD5_H2, b, c, d, a, MD5_GET(6), 0x04881d05, 23) + MD5_STEP(MD5_H, a, b, c, d, MD5_GET(9), 0xd9d4d039, 4) + MD5_STEP(MD5_H2, d, a, b, c, MD5_GET(12), 0xe6db99e5, 11) + MD5_STEP(MD5_H, c, d, a, b, MD5_GET(15), 0x1fa27cf8, 16) + MD5_STEP(MD5_H2, b, c, d, a, MD5_GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + MD5_STEP(MD5_I, a, b, c, d, MD5_GET(0), 0xf4292244, 6) + MD5_STEP(MD5_I, d, a, b, c, MD5_GET(7), 0x432aff97, 10) + MD5_STEP(MD5_I, c, d, a, b, MD5_GET(14), 0xab9423a7, 15) + MD5_STEP(MD5_I, b, c, d, a, MD5_GET(5), 0xfc93a039, 21) + MD5_STEP(MD5_I, a, b, c, d, MD5_GET(12), 0x655b59c3, 6) + MD5_STEP(MD5_I, d, a, b, c, MD5_GET(3), 0x8f0ccc92, 10) + MD5_STEP(MD5_I, c, d, a, b, MD5_GET(10), 0xffeff47d, 15) + MD5_STEP(MD5_I, b, c, d, a, MD5_GET(1), 0x85845dd1, 21) + MD5_STEP(MD5_I, a, b, c, d, MD5_GET(8), 0x6fa87e4f, 6) + MD5_STEP(MD5_I, d, a, b, c, MD5_GET(15), 0xfe2ce6e0, 10) + MD5_STEP(MD5_I, c, d, a, b, MD5_GET(6), 0xa3014314, 15) + MD5_STEP(MD5_I, b, c, d, a, MD5_GET(13), 0x4e0811a1, 21) + MD5_STEP(MD5_I, a, b, c, d, MD5_GET(4), 0xf7537e82, 6) + MD5_STEP(MD5_I, d, a, b, c, MD5_GET(11), 0xbd3af235, 10) + MD5_STEP(MD5_I, c, d, a, b, MD5_GET(2), 0x2ad7d2bb, 15) + MD5_STEP(MD5_I, b, c, d, a, MD5_GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while(size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +static CURLcode my_md5_init(my_md5_ctx *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; + + return CURLE_OK; +} + +static void my_md5_update(my_md5_ctx *ctx, const void *data, + unsigned long size) +{ + MD5_u32plus saved_lo; + unsigned long used; + + saved_lo = ctx->lo; + ctx->lo = (saved_lo + size) & 0x1fffffff; + if(ctx->lo < saved_lo) + ctx->hi++; + ctx->hi += (MD5_u32plus)size >> 29; + + used = saved_lo & 0x3f; + + if(used) { + unsigned long available = 64 - used; + + if(size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + my_md5_body(ctx, ctx->buffer, 64); + } + + if(size >= 64) { + data = my_md5_body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +static void my_md5_final(unsigned char *result, my_md5_ctx *ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if(available < 8) { + memset(&ctx->buffer[used], 0, available); + my_md5_body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = curlx_ultouc((ctx->lo)&0xff); + ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8)&0xff); + ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16)&0xff); + ctx->buffer[59] = curlx_ultouc(ctx->lo >> 24); + ctx->buffer[60] = curlx_ultouc((ctx->hi)&0xff); + ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8)&0xff); + ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff); + ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24); + + my_md5_body(ctx, ctx->buffer, 64); + + result[0] = curlx_ultouc((ctx->a)&0xff); + result[1] = curlx_ultouc((ctx->a >> 8)&0xff); + result[2] = curlx_ultouc((ctx->a >> 16)&0xff); + result[3] = curlx_ultouc(ctx->a >> 24); + result[4] = curlx_ultouc((ctx->b)&0xff); + result[5] = curlx_ultouc((ctx->b >> 8)&0xff); + result[6] = curlx_ultouc((ctx->b >> 16)&0xff); + result[7] = curlx_ultouc(ctx->b >> 24); + result[8] = curlx_ultouc((ctx->c)&0xff); + result[9] = curlx_ultouc((ctx->c >> 8)&0xff); + result[10] = curlx_ultouc((ctx->c >> 16)&0xff); + result[11] = curlx_ultouc(ctx->c >> 24); + result[12] = curlx_ultouc((ctx->d)&0xff); + result[13] = curlx_ultouc((ctx->d >> 8)&0xff); + result[14] = curlx_ultouc((ctx->d >> 16)&0xff); + result[15] = curlx_ultouc(ctx->d >> 24); + + memset(ctx, 0, sizeof(*ctx)); +} + +#endif /* CRYPTO LIBS */ + +const struct HMAC_params Curl_HMAC_MD5[] = { + { + /* Hash initialization function. */ + CURLX_FUNCTION_CAST(HMAC_hinit_func, my_md5_init), + /* Hash update function. */ + CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_md5_update), + /* Hash computation end function. */ + CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_md5_final), + /* Size of hash context structure. */ + sizeof(my_md5_ctx), + /* Maximum key length. */ + 64, + /* Result size. */ + 16 + } +}; + +const struct MD5_params Curl_DIGEST_MD5[] = { + { + /* Digest initialization function */ + CURLX_FUNCTION_CAST(Curl_MD5_init_func, my_md5_init), + /* Digest update function */ + CURLX_FUNCTION_CAST(Curl_MD5_update_func, my_md5_update), + /* Digest computation end function */ + CURLX_FUNCTION_CAST(Curl_MD5_final_func, my_md5_final), + /* Size of digest context struct */ + sizeof(my_md5_ctx), + /* Result size */ + 16 + } +}; + +/* + * @unittest: 1601 + * Returns CURLE_OK on success. + */ +CURLcode Curl_md5it(unsigned char *outbuffer, const unsigned char *input, + const size_t len) +{ + CURLcode result; + my_md5_ctx ctx; + + result = my_md5_init(&ctx); + if(!result) { + my_md5_update(&ctx, input, curlx_uztoui(len)); + my_md5_final(outbuffer, &ctx); + } + return result; +} + +struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params) +{ + struct MD5_context *ctxt; + + /* Create MD5 context */ + ctxt = malloc(sizeof(*ctxt)); + + if(!ctxt) + return ctxt; + + ctxt->md5_hashctx = malloc(md5params->md5_ctxtsize); + + if(!ctxt->md5_hashctx) { + free(ctxt); + return NULL; + } + + ctxt->md5_hash = md5params; + + if((*md5params->md5_init_func)(ctxt->md5_hashctx)) { + free(ctxt->md5_hashctx); + free(ctxt); + return NULL; + } + + return ctxt; +} + +CURLcode Curl_MD5_update(struct MD5_context *context, + const unsigned char *data, + unsigned int len) +{ + (*context->md5_hash->md5_update_func)(context->md5_hashctx, data, len); + + return CURLE_OK; +} + +CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result) +{ + (*context->md5_hash->md5_final_func)(result, context->md5_hashctx); + + free(context->md5_hashctx); + free(context); + + return CURLE_OK; +} + +#endif /* Using NTLM (without SSPI) || Digest */ diff --git a/third_party/curl/lib/memdebug.c b/third_party/curl/lib/memdebug.c new file mode 100644 index 0000000000..57fe591edf --- /dev/null +++ b/third_party/curl/lib/memdebug.c @@ -0,0 +1,463 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef CURLDEBUG + +#include + +#include "urldata.h" + +#define MEMDEBUG_NODEFINES /* do not redefine the standard functions */ + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +struct memdebug { + size_t size; + union { + curl_off_t o; + double d; + void *p; + } mem[1]; + /* I am hoping this is the thing with the strictest alignment + * requirements. That also means we waste some space :-( */ +}; + +/* + * Note that these debug functions are very simple and they are meant to + * remain so. For advanced analysis, record a log file and write perl scripts + * to analyze them! + * + * Do not use these with multithreaded test programs! + */ + +FILE *curl_dbg_logfile = NULL; +static bool registered_cleanup = FALSE; /* atexit registered cleanup */ +static bool memlimit = FALSE; /* enable memory limit */ +static long memsize = 0; /* set number of mallocs allowed */ + +/* LeakSantizier (LSAN) calls _exit() instead of exit() when a leak is detected + on exit so the logfile must be closed explicitly or data could be lost. + Though _exit() does not call atexit handlers such as this, LSAN's call to + _exit() comes after the atexit handlers are called. curl/curl#6620 */ +static void curl_dbg_cleanup(void) +{ + if(curl_dbg_logfile && + curl_dbg_logfile != stderr && + curl_dbg_logfile != stdout) { + fclose(curl_dbg_logfile); + } + curl_dbg_logfile = NULL; +} + +/* this sets the log filename */ +void curl_dbg_memdebug(const char *logname) +{ + if(!curl_dbg_logfile) { + if(logname && *logname) + curl_dbg_logfile = fopen(logname, FOPEN_WRITETEXT); + else + curl_dbg_logfile = stderr; +#ifdef MEMDEBUG_LOG_SYNC + /* Flush the log file after every line so the log is not lost in a crash */ + if(curl_dbg_logfile) + setbuf(curl_dbg_logfile, (char *)NULL); +#endif + } + if(!registered_cleanup) + registered_cleanup = !atexit(curl_dbg_cleanup); +} + +/* This function sets the number of malloc() calls that should return + successfully! */ +void curl_dbg_memlimit(long limit) +{ + if(!memlimit) { + memlimit = TRUE; + memsize = limit; + } +} + +/* returns TRUE if this is not allowed! */ +static bool countcheck(const char *func, int line, const char *source) +{ + /* if source is NULL, then the call is made internally and this check + should not be made */ + if(memlimit && source) { + if(!memsize) { + /* log to file */ + curl_dbg_log("LIMIT %s:%d %s reached memlimit\n", + source, line, func); + /* log to stderr also */ + fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n", + source, line, func); + fflush(curl_dbg_logfile); /* because it might crash now */ + errno = ENOMEM; + return TRUE; /* RETURN ERROR! */ + } + else + memsize--; /* countdown */ + + + } + + return FALSE; /* allow this */ +} + +ALLOC_FUNC void *curl_dbg_malloc(size_t wantedsize, + int line, const char *source) +{ + struct memdebug *mem; + size_t size; + + DEBUGASSERT(wantedsize != 0); + + if(countcheck("malloc", line, source)) + return NULL; + + /* alloc at least 64 bytes */ + size = sizeof(struct memdebug) + wantedsize; + + mem = (Curl_cmalloc)(size); + if(mem) { + mem->size = wantedsize; + } + + if(source) + curl_dbg_log("MEM %s:%d malloc(%zu) = %p\n", + source, line, wantedsize, + mem ? (void *)mem->mem : (void *)0); + + return (mem ? mem->mem : NULL); +} + +ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size, + int line, const char *source) +{ + struct memdebug *mem; + size_t size, user_size; + + DEBUGASSERT(wanted_elements != 0); + DEBUGASSERT(wanted_size != 0); + + if(countcheck("calloc", line, source)) + return NULL; + + /* alloc at least 64 bytes */ + user_size = wanted_size * wanted_elements; + size = sizeof(struct memdebug) + user_size; + + mem = (Curl_ccalloc)(1, size); + if(mem) + mem->size = user_size; + + if(source) + curl_dbg_log("MEM %s:%d calloc(%zu,%zu) = %p\n", + source, line, wanted_elements, wanted_size, + mem ? (void *)mem->mem : (void *)0); + + return (mem ? mem->mem : NULL); +} + +ALLOC_FUNC char *curl_dbg_strdup(const char *str, + int line, const char *source) +{ + char *mem; + size_t len; + + DEBUGASSERT(str != NULL); + + if(countcheck("strdup", line, source)) + return NULL; + + len = strlen(str) + 1; + + mem = curl_dbg_malloc(len, 0, NULL); /* NULL prevents logging */ + if(mem) + memcpy(mem, str, len); + + if(source) + curl_dbg_log("MEM %s:%d strdup(%p) (%zu) = %p\n", + source, line, (const void *)str, len, (const void *)mem); + + return mem; +} + +#if defined(_WIN32) && defined(UNICODE) +ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str, + int line, const char *source) +{ + wchar_t *mem; + size_t wsiz, bsiz; + + DEBUGASSERT(str != NULL); + + if(countcheck("wcsdup", line, source)) + return NULL; + + wsiz = wcslen(str) + 1; + bsiz = wsiz * sizeof(wchar_t); + + mem = curl_dbg_malloc(bsiz, 0, NULL); /* NULL prevents logging */ + if(mem) + memcpy(mem, str, bsiz); + + if(source) + curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n", + source, line, (void *)str, bsiz, (void *)mem); + + return mem; +} +#endif + +/* We provide a realloc() that accepts a NULL as pointer, which then + performs a malloc(). In order to work with ares. */ +void *curl_dbg_realloc(void *ptr, size_t wantedsize, + int line, const char *source) +{ + struct memdebug *mem = NULL; + + size_t size = sizeof(struct memdebug) + wantedsize; + + DEBUGASSERT(wantedsize != 0); + + if(countcheck("realloc", line, source)) + return NULL; + +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:1684) + /* 1684: conversion from pointer to same-sized integral type */ +#endif + + if(ptr) + mem = (void *)((char *)ptr - offsetof(struct memdebug, mem)); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif + + mem = (Curl_crealloc)(mem, size); + if(source) + curl_dbg_log("MEM %s:%d realloc(%p, %zu) = %p\n", + source, line, (void *)ptr, wantedsize, + mem ? (void *)mem->mem : (void *)0); + + if(mem) { + mem->size = wantedsize; + return mem->mem; + } + + return NULL; +} + +void curl_dbg_free(void *ptr, int line, const char *source) +{ + if(ptr) { + struct memdebug *mem; + +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:1684) + /* 1684: conversion from pointer to same-sized integral type */ +#endif + + mem = (void *)((char *)ptr - offsetof(struct memdebug, mem)); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif + + /* free for real */ + (Curl_cfree)(mem); + } + + if(source && ptr) + curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr); +} + +curl_socket_t curl_dbg_socket(int domain, int type, int protocol, + int line, const char *source) +{ + curl_socket_t sockfd; + + if(countcheck("socket", line, source)) + return CURL_SOCKET_BAD; + + sockfd = socket(domain, type, protocol); + + if(source && (sockfd != CURL_SOCKET_BAD)) + curl_dbg_log("FD %s:%d socket() = %" CURL_FORMAT_SOCKET_T "\n", + source, line, sockfd); + + return sockfd; +} + +SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd, + SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf, + SEND_TYPE_ARG3 len, SEND_TYPE_ARG4 flags, int line, + const char *source) +{ + SEND_TYPE_RETV rc; + if(countcheck("send", line, source)) + return -1; + rc = send(sockfd, buf, len, flags); + if(source) + curl_dbg_log("SEND %s:%d send(%lu) = %ld\n", + source, line, (unsigned long)len, (long)rc); + return rc; +} + +RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf, + RECV_TYPE_ARG3 len, RECV_TYPE_ARG4 flags, int line, + const char *source) +{ + RECV_TYPE_RETV rc; + if(countcheck("recv", line, source)) + return -1; + rc = recv(sockfd, buf, len, flags); + if(source) + curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n", + source, line, (unsigned long)len, (long)rc); + return rc; +} + +#ifdef HAVE_SOCKETPAIR +int curl_dbg_socketpair(int domain, int type, int protocol, + curl_socket_t socket_vector[2], + int line, const char *source) +{ + int res = socketpair(domain, type, protocol, socket_vector); + + if(source && (0 == res)) + curl_dbg_log("FD %s:%d socketpair() = " + "%" CURL_FORMAT_SOCKET_T " %" CURL_FORMAT_SOCKET_T "\n", + source, line, socket_vector[0], socket_vector[1]); + + return res; +} +#endif + +curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen, + int line, const char *source) +{ + struct sockaddr *addr = (struct sockaddr *)saddr; + curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; + + curl_socket_t sockfd = accept(s, addr, addrlen); + + if(source && (sockfd != CURL_SOCKET_BAD)) + curl_dbg_log("FD %s:%d accept() = %" CURL_FORMAT_SOCKET_T "\n", + source, line, sockfd); + + return sockfd; +} + +/* separate function to allow libcurl to mark a "faked" close */ +void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source) +{ + if(source) + curl_dbg_log("FD %s:%d sclose(%" CURL_FORMAT_SOCKET_T ")\n", + source, line, sockfd); +} + +/* this is our own defined way to close sockets on *ALL* platforms */ +int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source) +{ + int res = sclose(sockfd); + curl_dbg_mark_sclose(sockfd, line, source); + return res; +} + +ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode, + int line, const char *source) +{ + FILE *res = fopen(file, mode); + + if(source) + curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n", + source, line, file, mode, (void *)res); + + return res; +} + +ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, + int line, const char *source) +{ + FILE *res = fdopen(filedes, mode); + if(source) + curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n", + source, line, filedes, mode, (void *)res); + return res; +} + +int curl_dbg_fclose(FILE *file, int line, const char *source) +{ + int res; + + DEBUGASSERT(file != NULL); + + if(source) + curl_dbg_log("FILE %s:%d fclose(%p)\n", + source, line, (void *)file); + + res = fclose(file); + + return res; +} + +#define LOGLINE_BUFSIZE 1024 + +/* this does the writing to the memory tracking log file */ +void curl_dbg_log(const char *format, ...) +{ + char *buf; + int nchars; + va_list ap; + + if(!curl_dbg_logfile) + return; + + buf = (Curl_cmalloc)(LOGLINE_BUFSIZE); + if(!buf) + return; + + va_start(ap, format); + nchars = mvsnprintf(buf, LOGLINE_BUFSIZE, format, ap); + va_end(ap); + + if(nchars > LOGLINE_BUFSIZE - 1) + nchars = LOGLINE_BUFSIZE - 1; + + if(nchars > 0) + fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile); + + (Curl_cfree)(buf); +} + +#endif /* CURLDEBUG */ diff --git a/third_party/curl/lib/memdebug.h b/third_party/curl/lib/memdebug.h new file mode 100644 index 0000000000..fd5cc2c94d --- /dev/null +++ b/third_party/curl/lib/memdebug.h @@ -0,0 +1,203 @@ +#ifndef HEADER_CURL_MEMDEBUG_H +#define HEADER_CURL_MEMDEBUG_H +#ifdef CURLDEBUG +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * CAUTION: this header is designed to work when included by the app-side + * as well as the library. Do not mix with library internals! + */ + +#include +#include "functypes.h" + +#if defined(__GNUC__) && __GNUC__ >= 3 +# define ALLOC_FUNC __attribute__((malloc)) +# define ALLOC_SIZE(s) __attribute__((alloc_size(s))) +# define ALLOC_SIZE2(n, s) __attribute__((alloc_size(n, s))) +#elif defined(_MSC_VER) +# define ALLOC_FUNC __declspec(restrict) +# define ALLOC_SIZE(s) +# define ALLOC_SIZE2(n, s) +#else +# define ALLOC_FUNC +# define ALLOC_SIZE(s) +# define ALLOC_SIZE2(n, s) +#endif + +#define CURL_MT_LOGFNAME_BUFSIZE 512 + +extern FILE *curl_dbg_logfile; + +/* memory functions */ +CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1) void *curl_dbg_malloc(size_t size, + int line, + const char *source); +CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2) void *curl_dbg_calloc(size_t elements, + size_t size, int line, const char *source); +CURL_EXTERN ALLOC_SIZE(2) void *curl_dbg_realloc(void *ptr, + size_t size, + int line, + const char *source); +CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source); +CURL_EXTERN ALLOC_FUNC char *curl_dbg_strdup(const char *str, int line, + const char *src); +#if defined(_WIN32) && defined(UNICODE) +CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str, + int line, + const char *source); +#endif + +CURL_EXTERN void curl_dbg_memdebug(const char *logname); +CURL_EXTERN void curl_dbg_memlimit(long limit); +CURL_EXTERN void curl_dbg_log(const char *format, ...) CURL_PRINTF(1, 2); + +/* file descriptor manipulators */ +CURL_EXTERN curl_socket_t curl_dbg_socket(int domain, int type, int protocol, + int line, const char *source); +CURL_EXTERN void curl_dbg_mark_sclose(curl_socket_t sockfd, + int line, const char *source); +CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd, + int line, const char *source); +CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen, + int line, const char *source); +#ifdef HAVE_SOCKETPAIR +CURL_EXTERN int curl_dbg_socketpair(int domain, int type, int protocol, + curl_socket_t socket_vector[2], + int line, const char *source); +#endif + +/* send/receive sockets */ +CURL_EXTERN SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd, + SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf, + SEND_TYPE_ARG3 len, + SEND_TYPE_ARG4 flags, int line, + const char *source); +CURL_EXTERN RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, + RECV_TYPE_ARG2 buf, + RECV_TYPE_ARG3 len, + RECV_TYPE_ARG4 flags, int line, + const char *source); + +/* FILE functions */ +CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode, + int line, const char *source); +CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, + int line, const char *source); + +CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source); + +#ifndef MEMDEBUG_NODEFINES + +/* Set this symbol on the command-line, recompile all lib-sources */ +#undef strdup +#define strdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) +#define malloc(size) curl_dbg_malloc(size, __LINE__, __FILE__) +#define calloc(nbelem,size) curl_dbg_calloc(nbelem, size, __LINE__, __FILE__) +#define realloc(ptr,size) curl_dbg_realloc(ptr, size, __LINE__, __FILE__) +#define free(ptr) curl_dbg_free(ptr, __LINE__, __FILE__) +#define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__) +#define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__) + +#ifdef _WIN32 +# ifdef UNICODE +# undef wcsdup +# define wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) +# undef _wcsdup +# define _wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) +# undef _tcsdup +# define _tcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) +# else +# undef _tcsdup +# define _tcsdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) +# endif +#endif + +#undef socket +#define socket(domain,type,protocol)\ + curl_dbg_socket((int)domain, type, protocol, __LINE__, __FILE__) +#undef accept /* for those with accept as a macro */ +#define accept(sock,addr,len)\ + curl_dbg_accept(sock, addr, len, __LINE__, __FILE__) +#ifdef HAVE_SOCKETPAIR +#define socketpair(domain,type,protocol,socket_vector)\ + curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \ + __LINE__, __FILE__) +#endif + +#ifdef HAVE_GETADDRINFO +#if defined(getaddrinfo) && defined(__osf__) +/* OSF/1 and Tru64 have getaddrinfo as a define already, so we cannot define + our macro as for other platforms. Instead, we redefine the new name they + define getaddrinfo to become! */ +#define ogetaddrinfo(host,serv,hint,res) \ + curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__) +#else +#undef getaddrinfo +#define getaddrinfo(host,serv,hint,res) \ + curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__) +#endif +#endif /* HAVE_GETADDRINFO */ + +#ifdef HAVE_FREEADDRINFO +#undef freeaddrinfo +#define freeaddrinfo(data) \ + curl_dbg_freeaddrinfo(data, __LINE__, __FILE__) +#endif /* HAVE_FREEADDRINFO */ + +/* sclose is probably already defined, redefine it! */ +#undef sclose +#define sclose(sockfd) curl_dbg_sclose(sockfd,__LINE__,__FILE__) + +#define fake_sclose(sockfd) curl_dbg_mark_sclose(sockfd,__LINE__,__FILE__) + +#undef fopen +#define fopen(file,mode) curl_dbg_fopen(file,mode,__LINE__,__FILE__) +#undef fdopen +#define fdopen(file,mode) curl_dbg_fdopen(file,mode,__LINE__,__FILE__) +#define fclose(file) curl_dbg_fclose(file,__LINE__,__FILE__) + +#endif /* MEMDEBUG_NODEFINES */ + +#endif /* CURLDEBUG */ + +/* +** Following section applies even when CURLDEBUG is not defined. +*/ + +#ifndef fake_sclose +#define fake_sclose(x) Curl_nop_stmt +#endif + +/* + * Curl_safefree defined as a macro to allow MemoryTracking feature + * to log free() calls at same location where Curl_safefree is used. + * This macro also assigns NULL to given pointer when free'd. + */ + +#define Curl_safefree(ptr) \ + do { free((ptr)); (ptr) = NULL;} while(0) + +#endif /* HEADER_CURL_MEMDEBUG_H */ diff --git a/third_party/curl/lib/mime.c b/third_party/curl/lib/mime.c new file mode 100644 index 0000000000..e9eeb1bf7a --- /dev/null +++ b/third_party/curl/lib/mime.c @@ -0,0 +1,2248 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "mime.h" +#include "warnless.h" +#include "urldata.h" +#include "sendf.h" +#include "strdup.h" + +#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \ + !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_IMAP)) + +#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) +#include +#endif + +#include "rand.h" +#include "slist.h" +#include "strcase.h" +#include "dynbuf.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#ifdef _WIN32 +# ifndef R_OK +# define R_OK 4 +# endif +#endif + + +#define READ_ERROR ((size_t) -1) +#define STOP_FILLING ((size_t) -2) + +static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, + void *instream, bool *hasread); + +/* Encoders. */ +static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, + curl_mimepart *part); +static curl_off_t encoder_nop_size(curl_mimepart *part); +static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, + curl_mimepart *part); +static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, + curl_mimepart *part); +static curl_off_t encoder_base64_size(curl_mimepart *part); +static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, + curl_mimepart *part); +static curl_off_t encoder_qp_size(curl_mimepart *part); +static curl_off_t mime_size(curl_mimepart *part); + +static const struct mime_encoder encoders[] = { + {"binary", encoder_nop_read, encoder_nop_size}, + {"8bit", encoder_nop_read, encoder_nop_size}, + {"7bit", encoder_7bit_read, encoder_nop_size}, + {"base64", encoder_base64_read, encoder_base64_size}, + {"quoted-printable", encoder_qp_read, encoder_qp_size}, + {ZERO_NULL, ZERO_NULL, ZERO_NULL} +}; + +/* Base64 encoding table */ +static const char base64enc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* Quoted-printable character class table. + * + * We cannot rely on ctype functions since quoted-printable input data + * is assumed to be ascii-compatible, even on non-ascii platforms. */ +#define QP_OK 1 /* Can be represented by itself. */ +#define QP_SP 2 /* Space or tab. */ +#define QP_CR 3 /* Carriage return. */ +#define QP_LF 4 /* Line-feed. */ +static const unsigned char qp_class[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 07 */ + 0, QP_SP, QP_LF, 0, 0, QP_CR, 0, 0, /* 08 - 0F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 18 - 1F */ + QP_SP, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 20 - 27 */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 28 - 2F */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 30 - 37 */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0 , QP_OK, QP_OK, /* 38 - 3F */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 40 - 47 */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 48 - 4F */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 50 - 57 */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 58 - 5F */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 60 - 67 */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 68 - 6F */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 70 - 77 */ + QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0, /* 78 - 7F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ +}; + + +/* Binary --> hexadecimal ASCII table. */ +static const char aschex[] = + "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x41\x42\x43\x44\x45\x46"; + + + +#ifndef __VMS +#define filesize(name, stat_data) (stat_data.st_size) +#define fopen_read fopen + +#else + +#include +/* + * get_vms_file_size does what it takes to get the real size of the file + * + * For fixed files, find out the size of the EOF block and adjust. + * + * For all others, have to read the entire file in, discarding the contents. + * Most posted text files will be small, and binary files like zlib archives + * and CD/DVD images should be either a STREAM_LF format or a fixed format. + * + */ +curl_off_t VmsRealFileSize(const char *name, + const struct_stat *stat_buf) +{ + char buffer[8192]; + curl_off_t count; + int ret_stat; + FILE * file; + + file = fopen(name, FOPEN_READTEXT); /* VMS */ + if(!file) + return 0; + + count = 0; + ret_stat = 1; + while(ret_stat > 0) { + ret_stat = fread(buffer, 1, sizeof(buffer), file); + if(ret_stat) + count += ret_stat; + } + fclose(file); + + return count; +} + +/* + * + * VmsSpecialSize checks to see if the stat st_size can be trusted and + * if not to call a routine to get the correct size. + * + */ +static curl_off_t VmsSpecialSize(const char *name, + const struct_stat *stat_buf) +{ + switch(stat_buf->st_fab_rfm) { + case FAB$C_VAR: + case FAB$C_VFC: + return VmsRealFileSize(name, stat_buf); + break; + default: + return stat_buf->st_size; + } +} + +#define filesize(name, stat_data) VmsSpecialSize(name, &stat_data) + +/* + * vmsfopenread + * + * For upload to work as expected on VMS, different optional + * parameters must be added to the fopen command based on + * record format of the file. + * + */ +static FILE * vmsfopenread(const char *file, const char *mode) +{ + struct_stat statbuf; + int result; + + result = stat(file, &statbuf); + + switch(statbuf.st_fab_rfm) { + case FAB$C_VAR: + case FAB$C_VFC: + case FAB$C_STMCR: + return fopen(file, FOPEN_READTEXT); /* VMS */ + break; + default: + return fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm"); + } +} + +#define fopen_read vmsfopenread +#endif + + +#ifndef HAVE_BASENAME +/* + (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 + Edition) + + The basename() function shall take the pathname pointed to by path and + return a pointer to the final component of the pathname, deleting any + trailing '/' characters. + + If the string pointed to by path consists entirely of the '/' character, + basename() shall return a pointer to the string "/". If the string pointed + to by path is exactly "//", it is implementation-defined whether '/' or "//" + is returned. + + If path is a null pointer or points to an empty string, basename() shall + return a pointer to the string ".". + + The basename() function may modify the string pointed to by path, and may + return a pointer to static storage that may then be overwritten by a + subsequent call to basename(). + + The basename() function need not be reentrant. A function that is not + required to be reentrant is not required to be thread-safe. + +*/ +static char *Curl_basename(char *path) +{ + /* Ignore all the details above for now and make a quick and simple + implementation here */ + char *s1; + char *s2; + + s1 = strrchr(path, '/'); + s2 = strrchr(path, '\\'); + + if(s1 && s2) { + path = (s1 > s2? s1 : s2) + 1; + } + else if(s1) + path = s1 + 1; + else if(s2) + path = s2 + 1; + + return path; +} + +#define basename(x) Curl_basename((x)) +#endif + + +/* Set readback state. */ +static void mimesetstate(struct mime_state *state, + enum mimestate tok, void *ptr) +{ + state->state = tok; + state->ptr = ptr; + state->offset = 0; +} + + +/* Escape header string into allocated memory. */ +static char *escape_string(struct Curl_easy *data, + const char *src, enum mimestrategy strategy) +{ + CURLcode result; + struct dynbuf db; + const char * const *table; + const char * const *p; + /* replace first character by rest of string. */ + static const char * const mimetable[] = { + "\\\\\\", + "\"\\\"", + NULL + }; + /* WHATWG HTML living standard 4.10.21.8 2 specifies: + For field names and filenames for file fields, the result of the + encoding in the previous bullet point must be escaped by replacing + any 0x0A (LF) bytes with the byte sequence `%0A`, 0x0D (CR) with `%0D` + and 0x22 (") with `%22`. + The user agent must not perform any other escapes. */ + static const char * const formtable[] = { + "\"%22", + "\r%0D", + "\n%0A", + NULL + }; + + table = formtable; + /* data can be NULL when this function is called indirectly from + curl_formget(). */ + if(strategy == MIMESTRATEGY_MAIL || (data && (data->set.mime_formescape))) + table = mimetable; + + Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH); + + for(result = Curl_dyn_addn(&db, STRCONST("")); !result && *src; src++) { + for(p = table; *p && **p != *src; p++) + ; + + if(*p) + result = Curl_dyn_add(&db, *p + 1); + else + result = Curl_dyn_addn(&db, src, 1); + } + + return Curl_dyn_ptr(&db); +} + +/* Check if header matches. */ +static char *match_header(struct curl_slist *hdr, const char *lbl, size_t len) +{ + char *value = NULL; + + if(strncasecompare(hdr->data, lbl, len) && hdr->data[len] == ':') + for(value = hdr->data + len + 1; *value == ' '; value++) + ; + return value; +} + +/* Get a header from an slist. */ +static char *search_header(struct curl_slist *hdrlist, + const char *hdr, size_t len) +{ + char *value = NULL; + + for(; !value && hdrlist; hdrlist = hdrlist->next) + value = match_header(hdrlist, hdr, len); + + return value; +} + +static char *strippath(const char *fullfile) +{ + char *filename; + char *base; + filename = strdup(fullfile); /* duplicate since basename() may ruin the + buffer it works on */ + if(!filename) + return NULL; + base = strdup(basename(filename)); + + free(filename); /* free temporary buffer */ + + return base; /* returns an allocated string or NULL ! */ +} + +/* Initialize data encoder state. */ +static void cleanup_encoder_state(struct mime_encoder_state *p) +{ + p->pos = 0; + p->bufbeg = 0; + p->bufend = 0; +} + + +/* Dummy encoder. This is used for 8bit and binary content encodings. */ +static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, + struct curl_mimepart *part) +{ + struct mime_encoder_state *st = &part->encstate; + size_t insize = st->bufend - st->bufbeg; + + (void) ateof; + + if(!size) + return STOP_FILLING; + + if(size > insize) + size = insize; + + if(size) + memcpy(buffer, st->buf + st->bufbeg, size); + + st->bufbeg += size; + return size; +} + +static curl_off_t encoder_nop_size(curl_mimepart *part) +{ + return part->datasize; +} + + +/* 7bit encoder: the encoder is just a data validity check. */ +static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, + curl_mimepart *part) +{ + struct mime_encoder_state *st = &part->encstate; + size_t cursize = st->bufend - st->bufbeg; + + (void) ateof; + + if(!size) + return STOP_FILLING; + + if(size > cursize) + size = cursize; + + for(cursize = 0; cursize < size; cursize++) { + *buffer = st->buf[st->bufbeg]; + if(*buffer++ & 0x80) + return cursize? cursize: READ_ERROR; + st->bufbeg++; + } + + return cursize; +} + + +/* Base64 content encoder. */ +static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, + curl_mimepart *part) +{ + struct mime_encoder_state *st = &part->encstate; + size_t cursize = 0; + int i; + char *ptr = buffer; + + while(st->bufbeg < st->bufend) { + /* Line full ? */ + if(st->pos > MAX_ENCODED_LINE_LENGTH - 4) { + /* Yes, we need 2 characters for CRLF. */ + if(size < 2) { + if(!cursize) + return STOP_FILLING; + break; + } + *ptr++ = '\r'; + *ptr++ = '\n'; + st->pos = 0; + cursize += 2; + size -= 2; + } + + /* Be sure there is enough space and input data for a base64 group. */ + if(size < 4) { + if(!cursize) + return STOP_FILLING; + break; + } + if(st->bufend - st->bufbeg < 3) + break; + + /* Encode three bytes as four characters. */ + i = st->buf[st->bufbeg++] & 0xFF; + i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); + i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); + *ptr++ = base64enc[(i >> 18) & 0x3F]; + *ptr++ = base64enc[(i >> 12) & 0x3F]; + *ptr++ = base64enc[(i >> 6) & 0x3F]; + *ptr++ = base64enc[i & 0x3F]; + cursize += 4; + st->pos += 4; + size -= 4; + } + + /* If at eof, we have to flush the buffered data. */ + if(ateof) { + if(size < 4) { + if(!cursize) + return STOP_FILLING; + } + else { + /* Buffered data size can only be 0, 1 or 2. */ + ptr[2] = ptr[3] = '='; + i = 0; + + /* If there is buffered data */ + if(st->bufend != st->bufbeg) { + + if(st->bufend - st->bufbeg == 2) + i = (st->buf[st->bufbeg + 1] & 0xFF) << 8; + + i |= (st->buf[st->bufbeg] & 0xFF) << 16; + ptr[0] = base64enc[(i >> 18) & 0x3F]; + ptr[1] = base64enc[(i >> 12) & 0x3F]; + if(++st->bufbeg != st->bufend) { + ptr[2] = base64enc[(i >> 6) & 0x3F]; + st->bufbeg++; + } + cursize += 4; + st->pos += 4; + } + } + } + + return cursize; +} + +static curl_off_t encoder_base64_size(curl_mimepart *part) +{ + curl_off_t size = part->datasize; + + if(size <= 0) + return size; /* Unknown size or no data. */ + + /* Compute base64 character count. */ + size = 4 * (1 + (size - 1) / 3); + + /* Effective character count must include CRLFs. */ + return size + 2 * ((size - 1) / MAX_ENCODED_LINE_LENGTH); +} + + +/* Quoted-printable lookahead. + * + * Check if a CRLF or end of data is in input buffer at current position + n. + * Return -1 if more data needed, 1 if CRLF or end of data, else 0. + */ +static int qp_lookahead_eol(struct mime_encoder_state *st, int ateof, size_t n) +{ + n += st->bufbeg; + if(n >= st->bufend && ateof) + return 1; + if(n + 2 > st->bufend) + return ateof? 0: -1; + if(qp_class[st->buf[n] & 0xFF] == QP_CR && + qp_class[st->buf[n + 1] & 0xFF] == QP_LF) + return 1; + return 0; +} + +/* Quoted-printable encoder. */ +static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, + curl_mimepart *part) +{ + struct mime_encoder_state *st = &part->encstate; + char *ptr = buffer; + size_t cursize = 0; + int softlinebreak; + char buf[4]; + + /* On all platforms, input is supposed to be ASCII compatible: for this + reason, we use hexadecimal ASCII codes in this function rather than + character constants that can be interpreted as non-ascii on some + platforms. Preserve ASCII encoding on output too. */ + while(st->bufbeg < st->bufend) { + size_t len = 1; + size_t consumed = 1; + int i = st->buf[st->bufbeg]; + buf[0] = (char) i; + buf[1] = aschex[(i >> 4) & 0xF]; + buf[2] = aschex[i & 0xF]; + + switch(qp_class[st->buf[st->bufbeg] & 0xFF]) { + case QP_OK: /* Not a special character. */ + break; + case QP_SP: /* Space or tab. */ + /* Spacing must be escaped if followed by CRLF. */ + switch(qp_lookahead_eol(st, ateof, 1)) { + case -1: /* More input data needed. */ + return cursize; + case 0: /* No encoding needed. */ + break; + default: /* CRLF after space or tab. */ + buf[0] = '\x3D'; /* '=' */ + len = 3; + break; + } + break; + case QP_CR: /* Carriage return. */ + /* If followed by a line-feed, output the CRLF pair. + Else escape it. */ + switch(qp_lookahead_eol(st, ateof, 0)) { + case -1: /* Need more data. */ + return cursize; + case 1: /* CRLF found. */ + buf[len++] = '\x0A'; /* Append '\n'. */ + consumed = 2; + break; + default: /* Not followed by LF: escape. */ + buf[0] = '\x3D'; /* '=' */ + len = 3; + break; + } + break; + default: /* Character must be escaped. */ + buf[0] = '\x3D'; /* '=' */ + len = 3; + break; + } + + /* Be sure the encoded character fits within maximum line length. */ + if(buf[len - 1] != '\x0A') { /* '\n' */ + softlinebreak = st->pos + len > MAX_ENCODED_LINE_LENGTH; + if(!softlinebreak && st->pos + len == MAX_ENCODED_LINE_LENGTH) { + /* We may use the current line only if end of data or followed by + a CRLF. */ + switch(qp_lookahead_eol(st, ateof, consumed)) { + case -1: /* Need more data. */ + return cursize; + case 0: /* Not followed by a CRLF. */ + softlinebreak = 1; + break; + } + } + if(softlinebreak) { + strcpy(buf, "\x3D\x0D\x0A"); /* "=\r\n" */ + len = 3; + consumed = 0; + } + } + + /* If the output buffer would overflow, do not store. */ + if(len > size) { + if(!cursize) + return STOP_FILLING; + break; + } + + /* Append to output buffer. */ + memcpy(ptr, buf, len); + cursize += len; + ptr += len; + size -= len; + st->pos += len; + if(buf[len - 1] == '\x0A') /* '\n' */ + st->pos = 0; + st->bufbeg += consumed; + } + + return cursize; +} + +static curl_off_t encoder_qp_size(curl_mimepart *part) +{ + /* Determining the size can only be done by reading the data: unless the + data size is 0, we return it as unknown (-1). */ + return part->datasize? -1: 0; +} + + +/* In-memory data callbacks. */ +/* Argument is a pointer to the mime part. */ +static size_t mime_mem_read(char *buffer, size_t size, size_t nitems, + void *instream) +{ + curl_mimepart *part = (curl_mimepart *) instream; + size_t sz = curlx_sotouz(part->datasize - part->state.offset); + (void) size; /* Always 1.*/ + + if(!nitems) + return STOP_FILLING; + + if(sz > nitems) + sz = nitems; + + if(sz) + memcpy(buffer, part->data + curlx_sotouz(part->state.offset), sz); + + return sz; +} + +static int mime_mem_seek(void *instream, curl_off_t offset, int whence) +{ + curl_mimepart *part = (curl_mimepart *) instream; + + switch(whence) { + case SEEK_CUR: + offset += part->state.offset; + break; + case SEEK_END: + offset += part->datasize; + break; + } + + if(offset < 0 || offset > part->datasize) + return CURL_SEEKFUNC_FAIL; + + part->state.offset = offset; + return CURL_SEEKFUNC_OK; +} + +static void mime_mem_free(void *ptr) +{ + Curl_safefree(((curl_mimepart *) ptr)->data); +} + + +/* Named file callbacks. */ +/* Argument is a pointer to the mime part. */ +static int mime_open_file(curl_mimepart *part) +{ + /* Open a MIMEKIND_FILE part. */ + + if(part->fp) + return 0; + part->fp = fopen_read(part->data, "rb"); + return part->fp? 0: -1; +} + +static size_t mime_file_read(char *buffer, size_t size, size_t nitems, + void *instream) +{ + curl_mimepart *part = (curl_mimepart *) instream; + + if(!nitems) + return STOP_FILLING; + + if(mime_open_file(part)) + return READ_ERROR; + + return fread(buffer, size, nitems, part->fp); +} + +static int mime_file_seek(void *instream, curl_off_t offset, int whence) +{ + curl_mimepart *part = (curl_mimepart *) instream; + + if(whence == SEEK_SET && !offset && !part->fp) + return CURL_SEEKFUNC_OK; /* Not open: implicitly already at BOF. */ + + if(mime_open_file(part)) + return CURL_SEEKFUNC_FAIL; + + return fseek(part->fp, (long) offset, whence)? + CURL_SEEKFUNC_CANTSEEK: CURL_SEEKFUNC_OK; +} + +static void mime_file_free(void *ptr) +{ + curl_mimepart *part = (curl_mimepart *) ptr; + + if(part->fp) { + fclose(part->fp); + part->fp = NULL; + } + Curl_safefree(part->data); +} + + +/* Subparts callbacks. */ +/* Argument is a pointer to the mime structure. */ + +/* Readback a byte string segment. */ +static size_t readback_bytes(struct mime_state *state, + char *buffer, size_t bufsize, + const char *bytes, size_t numbytes, + const char *trail, size_t traillen) +{ + size_t sz; + size_t offset = curlx_sotouz(state->offset); + + if(numbytes > offset) { + sz = numbytes - offset; + bytes += offset; + } + else { + sz = offset - numbytes; + if(sz >= traillen) + return 0; + bytes = trail + sz; + sz = traillen - sz; + } + + if(sz > bufsize) + sz = bufsize; + + memcpy(buffer, bytes, sz); + state->offset += sz; + return sz; +} + +/* Read a non-encoded part content. */ +static size_t read_part_content(curl_mimepart *part, + char *buffer, size_t bufsize, bool *hasread) +{ + size_t sz = 0; + + switch(part->lastreadstatus) { + case 0: + case CURL_READFUNC_ABORT: + case CURL_READFUNC_PAUSE: + case READ_ERROR: + return part->lastreadstatus; + default: + break; + } + + /* If we can determine we are at end of part data, spare a read. */ + if(part->datasize != (curl_off_t) -1 && + part->state.offset >= part->datasize) { + /* sz is already zero. */ + } + else { + switch(part->kind) { + case MIMEKIND_MULTIPART: + /* + * Cannot be processed as other kinds since read function requires + * an additional parameter and is highly recursive. + */ + sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread); + break; + case MIMEKIND_FILE: + if(part->fp && feof(part->fp)) + break; /* At EOF. */ + FALLTHROUGH(); + default: + if(part->readfunc) { + if(!(part->flags & MIME_FAST_READ)) { + if(*hasread) + return STOP_FILLING; + *hasread = TRUE; + } + sz = part->readfunc(buffer, 1, bufsize, part->arg); + } + break; + } + } + + switch(sz) { + case STOP_FILLING: + break; + case 0: + case CURL_READFUNC_ABORT: + case CURL_READFUNC_PAUSE: + case READ_ERROR: + part->lastreadstatus = sz; + break; + default: + part->state.offset += sz; + part->lastreadstatus = sz; + break; + } + + return sz; +} + +/* Read and encode part content. */ +static size_t read_encoded_part_content(curl_mimepart *part, char *buffer, + size_t bufsize, bool *hasread) +{ + struct mime_encoder_state *st = &part->encstate; + size_t cursize = 0; + size_t sz; + bool ateof = FALSE; + + for(;;) { + if(st->bufbeg < st->bufend || ateof) { + /* Encode buffered data. */ + sz = part->encoder->encodefunc(buffer, bufsize, ateof, part); + switch(sz) { + case 0: + if(ateof) + return cursize; + break; + case READ_ERROR: + case STOP_FILLING: + return cursize? cursize: sz; + default: + cursize += sz; + buffer += sz; + bufsize -= sz; + continue; + } + } + + /* We need more data in input buffer. */ + if(st->bufbeg) { + size_t len = st->bufend - st->bufbeg; + + if(len) + memmove(st->buf, st->buf + st->bufbeg, len); + st->bufbeg = 0; + st->bufend = len; + } + if(st->bufend >= sizeof(st->buf)) + return cursize? cursize: READ_ERROR; /* Buffer full. */ + sz = read_part_content(part, st->buf + st->bufend, + sizeof(st->buf) - st->bufend, hasread); + switch(sz) { + case 0: + ateof = TRUE; + break; + case CURL_READFUNC_ABORT: + case CURL_READFUNC_PAUSE: + case READ_ERROR: + case STOP_FILLING: + return cursize? cursize: sz; + default: + st->bufend += sz; + break; + } + } + + /* NOTREACHED */ +} + +/* Readback a mime part. */ +static size_t readback_part(curl_mimepart *part, + char *buffer, size_t bufsize, bool *hasread) +{ + size_t cursize = 0; + + /* Readback from part. */ + + while(bufsize) { + size_t sz = 0; + struct curl_slist *hdr = (struct curl_slist *) part->state.ptr; + switch(part->state.state) { + case MIMESTATE_BEGIN: + mimesetstate(&part->state, + (part->flags & MIME_BODY_ONLY)? + MIMESTATE_BODY: MIMESTATE_CURLHEADERS, + part->curlheaders); + break; + case MIMESTATE_USERHEADERS: + if(!hdr) { + mimesetstate(&part->state, MIMESTATE_EOH, NULL); + break; + } + if(match_header(hdr, "Content-Type", 12)) { + mimesetstate(&part->state, MIMESTATE_USERHEADERS, hdr->next); + break; + } + FALLTHROUGH(); + case MIMESTATE_CURLHEADERS: + if(!hdr) + mimesetstate(&part->state, MIMESTATE_USERHEADERS, part->userheaders); + else { + sz = readback_bytes(&part->state, buffer, bufsize, + hdr->data, strlen(hdr->data), STRCONST("\r\n")); + if(!sz) + mimesetstate(&part->state, part->state.state, hdr->next); + } + break; + case MIMESTATE_EOH: + sz = readback_bytes(&part->state, buffer, bufsize, STRCONST("\r\n"), + STRCONST("")); + if(!sz) + mimesetstate(&part->state, MIMESTATE_BODY, NULL); + break; + case MIMESTATE_BODY: + cleanup_encoder_state(&part->encstate); + mimesetstate(&part->state, MIMESTATE_CONTENT, NULL); + break; + case MIMESTATE_CONTENT: + if(part->encoder) + sz = read_encoded_part_content(part, buffer, bufsize, hasread); + else + sz = read_part_content(part, buffer, bufsize, hasread); + switch(sz) { + case 0: + mimesetstate(&part->state, MIMESTATE_END, NULL); + /* Try sparing open file descriptors. */ + if(part->kind == MIMEKIND_FILE && part->fp) { + fclose(part->fp); + part->fp = NULL; + } + FALLTHROUGH(); + case CURL_READFUNC_ABORT: + case CURL_READFUNC_PAUSE: + case READ_ERROR: + case STOP_FILLING: + return cursize? cursize: sz; + } + break; + case MIMESTATE_END: + return cursize; + default: + break; /* Other values not in part state. */ + } + + /* Bump buffer and counters according to read size. */ + cursize += sz; + buffer += sz; + bufsize -= sz; + } + + return cursize; +} + +/* Readback from mime. Warning: not a read callback function. */ +static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, + void *instream, bool *hasread) +{ + curl_mime *mime = (curl_mime *) instream; + size_t cursize = 0; + (void) size; /* Always 1. */ + + while(nitems) { + size_t sz = 0; + curl_mimepart *part = mime->state.ptr; + switch(mime->state.state) { + case MIMESTATE_BEGIN: + case MIMESTATE_BODY: + mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, mime->firstpart); + /* The first boundary always follows the header termination empty line, + so is always preceded by a CRLF. We can then spare 2 characters + by skipping the leading CRLF in boundary. */ + mime->state.offset += 2; + break; + case MIMESTATE_BOUNDARY1: + sz = readback_bytes(&mime->state, buffer, nitems, STRCONST("\r\n--"), + STRCONST("")); + if(!sz) + mimesetstate(&mime->state, MIMESTATE_BOUNDARY2, part); + break; + case MIMESTATE_BOUNDARY2: + if(part) + sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary, + MIME_BOUNDARY_LEN, STRCONST("\r\n")); + else + sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary, + MIME_BOUNDARY_LEN, STRCONST("--\r\n")); + if(!sz) { + mimesetstate(&mime->state, MIMESTATE_CONTENT, part); + } + break; + case MIMESTATE_CONTENT: + if(!part) { + mimesetstate(&mime->state, MIMESTATE_END, NULL); + break; + } + sz = readback_part(part, buffer, nitems, hasread); + switch(sz) { + case CURL_READFUNC_ABORT: + case CURL_READFUNC_PAUSE: + case READ_ERROR: + case STOP_FILLING: + return cursize? cursize: sz; + case 0: + mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, part->nextpart); + break; + } + break; + case MIMESTATE_END: + return cursize; + default: + break; /* other values not used in mime state. */ + } + + /* Bump buffer and counters according to read size. */ + cursize += sz; + buffer += sz; + nitems -= sz; + } + + return cursize; +} + +static int mime_part_rewind(curl_mimepart *part) +{ + int res = CURL_SEEKFUNC_OK; + enum mimestate targetstate = MIMESTATE_BEGIN; + + if(part->flags & MIME_BODY_ONLY) + targetstate = MIMESTATE_BODY; + cleanup_encoder_state(&part->encstate); + if(part->state.state > targetstate) { + res = CURL_SEEKFUNC_CANTSEEK; + if(part->seekfunc) { + res = part->seekfunc(part->arg, (curl_off_t) 0, SEEK_SET); + switch(res) { + case CURL_SEEKFUNC_OK: + case CURL_SEEKFUNC_FAIL: + case CURL_SEEKFUNC_CANTSEEK: + break; + case -1: /* For fseek() error. */ + res = CURL_SEEKFUNC_CANTSEEK; + break; + default: + res = CURL_SEEKFUNC_FAIL; + break; + } + } + } + + if(res == CURL_SEEKFUNC_OK) + mimesetstate(&part->state, targetstate, NULL); + + part->lastreadstatus = 1; /* Successful read status. */ + return res; +} + +static int mime_subparts_seek(void *instream, curl_off_t offset, int whence) +{ + curl_mime *mime = (curl_mime *) instream; + curl_mimepart *part; + int result = CURL_SEEKFUNC_OK; + + if(whence != SEEK_SET || offset) + return CURL_SEEKFUNC_CANTSEEK; /* Only support full rewind. */ + + if(mime->state.state == MIMESTATE_BEGIN) + return CURL_SEEKFUNC_OK; /* Already rewound. */ + + for(part = mime->firstpart; part; part = part->nextpart) { + int res = mime_part_rewind(part); + if(res != CURL_SEEKFUNC_OK) + result = res; + } + + if(result == CURL_SEEKFUNC_OK) + mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL); + + return result; +} + +/* Release part content. */ +static void cleanup_part_content(curl_mimepart *part) +{ + if(part->freefunc) + part->freefunc(part->arg); + + part->readfunc = NULL; + part->seekfunc = NULL; + part->freefunc = NULL; + part->arg = (void *) part; /* Defaults to part itself. */ + part->data = NULL; + part->fp = NULL; + part->datasize = (curl_off_t) 0; /* No size yet. */ + cleanup_encoder_state(&part->encstate); + part->kind = MIMEKIND_NONE; + part->flags &= ~(unsigned int)MIME_FAST_READ; + part->lastreadstatus = 1; /* Successful read status. */ + part->state.state = MIMESTATE_BEGIN; +} + +static void mime_subparts_free(void *ptr) +{ + curl_mime *mime = (curl_mime *) ptr; + + if(mime && mime->parent) { + mime->parent->freefunc = NULL; /* Be sure we will not be called again. */ + cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */ + } + curl_mime_free(mime); +} + +/* Do not free subparts: unbind them. This is used for the top level only. */ +static void mime_subparts_unbind(void *ptr) +{ + curl_mime *mime = (curl_mime *) ptr; + + if(mime && mime->parent) { + mime->parent->freefunc = NULL; /* Be sure we will not be called again. */ + cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */ + mime->parent = NULL; + } +} + + +void Curl_mime_cleanpart(curl_mimepart *part) +{ + if(part) { + cleanup_part_content(part); + curl_slist_free_all(part->curlheaders); + if(part->flags & MIME_USERHEADERS_OWNER) + curl_slist_free_all(part->userheaders); + Curl_safefree(part->mimetype); + Curl_safefree(part->name); + Curl_safefree(part->filename); + Curl_mime_initpart(part); + } +} + +/* Recursively delete a mime handle and its parts. */ +void curl_mime_free(curl_mime *mime) +{ + curl_mimepart *part; + + if(mime) { + mime_subparts_unbind(mime); /* Be sure it is not referenced anymore. */ + while(mime->firstpart) { + part = mime->firstpart; + mime->firstpart = part->nextpart; + Curl_mime_cleanpart(part); + free(part); + } + free(mime); + } +} + +CURLcode Curl_mime_duppart(struct Curl_easy *data, + curl_mimepart *dst, const curl_mimepart *src) +{ + curl_mime *mime; + curl_mimepart *d; + const curl_mimepart *s; + CURLcode res = CURLE_OK; + + DEBUGASSERT(dst); + + /* Duplicate content. */ + switch(src->kind) { + case MIMEKIND_NONE: + break; + case MIMEKIND_DATA: + res = curl_mime_data(dst, src->data, (size_t) src->datasize); + break; + case MIMEKIND_FILE: + res = curl_mime_filedata(dst, src->data); + /* Do not abort duplication if file is not readable. */ + if(res == CURLE_READ_ERROR) + res = CURLE_OK; + break; + case MIMEKIND_CALLBACK: + res = curl_mime_data_cb(dst, src->datasize, src->readfunc, + src->seekfunc, src->freefunc, src->arg); + break; + case MIMEKIND_MULTIPART: + /* No one knows about the cloned subparts, thus always attach ownership + to the part. */ + mime = curl_mime_init(data); + res = mime? curl_mime_subparts(dst, mime): CURLE_OUT_OF_MEMORY; + + /* Duplicate subparts. */ + for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) { + d = curl_mime_addpart(mime); + res = d? Curl_mime_duppart(data, d, s): CURLE_OUT_OF_MEMORY; + } + break; + default: /* Invalid kind: should not occur. */ + DEBUGF(infof(data, "invalid MIMEKIND* attempt")); + res = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */ + break; + } + + /* Duplicate headers. */ + if(!res && src->userheaders) { + struct curl_slist *hdrs = Curl_slist_duplicate(src->userheaders); + + if(!hdrs) + res = CURLE_OUT_OF_MEMORY; + else { + /* No one but this procedure knows about the new header list, + so always take ownership. */ + res = curl_mime_headers(dst, hdrs, TRUE); + if(res) + curl_slist_free_all(hdrs); + } + } + + if(!res) { + /* Duplicate other fields. */ + dst->encoder = src->encoder; + res = curl_mime_type(dst, src->mimetype); + } + if(!res) + res = curl_mime_name(dst, src->name); + if(!res) + res = curl_mime_filename(dst, src->filename); + + /* If an error occurred, rollback. */ + if(res) + Curl_mime_cleanpart(dst); + + return res; +} + +/* + * Mime build functions. + */ + +/* Create a mime handle. */ +curl_mime *curl_mime_init(struct Curl_easy *easy) +{ + curl_mime *mime; + + mime = (curl_mime *) malloc(sizeof(*mime)); + + if(mime) { + mime->parent = NULL; + mime->firstpart = NULL; + mime->lastpart = NULL; + + memset(mime->boundary, '-', MIME_BOUNDARY_DASHES); + if(Curl_rand_alnum(easy, + (unsigned char *) &mime->boundary[MIME_BOUNDARY_DASHES], + MIME_RAND_BOUNDARY_CHARS + 1)) { + /* failed to get random separator, bail out */ + free(mime); + return NULL; + } + mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL); + } + + return mime; +} + +/* Initialize a mime part. */ +void Curl_mime_initpart(curl_mimepart *part) +{ + memset((char *) part, 0, sizeof(*part)); + part->lastreadstatus = 1; /* Successful read status. */ + mimesetstate(&part->state, MIMESTATE_BEGIN, NULL); +} + +/* Create a mime part and append it to a mime handle's part list. */ +curl_mimepart *curl_mime_addpart(curl_mime *mime) +{ + curl_mimepart *part; + + if(!mime) + return NULL; + + part = (curl_mimepart *) malloc(sizeof(*part)); + + if(part) { + Curl_mime_initpart(part); + part->parent = mime; + + if(mime->lastpart) + mime->lastpart->nextpart = part; + else + mime->firstpart = part; + + mime->lastpart = part; + } + + return part; +} + +/* Set mime part name. */ +CURLcode curl_mime_name(curl_mimepart *part, const char *name) +{ + if(!part) + return CURLE_BAD_FUNCTION_ARGUMENT; + + Curl_safefree(part->name); + + if(name) { + part->name = strdup(name); + if(!part->name) + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +/* Set mime part remote filename. */ +CURLcode curl_mime_filename(curl_mimepart *part, const char *filename) +{ + if(!part) + return CURLE_BAD_FUNCTION_ARGUMENT; + + Curl_safefree(part->filename); + + if(filename) { + part->filename = strdup(filename); + if(!part->filename) + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +/* Set mime part content from memory data. */ +CURLcode curl_mime_data(curl_mimepart *part, + const char *ptr, size_t datasize) +{ + if(!part) + return CURLE_BAD_FUNCTION_ARGUMENT; + + cleanup_part_content(part); + + if(ptr) { + if(datasize == CURL_ZERO_TERMINATED) + datasize = strlen(ptr); + + part->data = Curl_memdup0(ptr, datasize); + if(!part->data) + return CURLE_OUT_OF_MEMORY; + + part->datasize = datasize; + part->readfunc = mime_mem_read; + part->seekfunc = mime_mem_seek; + part->freefunc = mime_mem_free; + part->flags |= MIME_FAST_READ; + part->kind = MIMEKIND_DATA; + } + + return CURLE_OK; +} + +/* Set mime part content from named local file. */ +CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename) +{ + CURLcode result = CURLE_OK; + + if(!part) + return CURLE_BAD_FUNCTION_ARGUMENT; + + cleanup_part_content(part); + + if(filename) { + char *base; + struct_stat sbuf; + + if(stat(filename, &sbuf)) + result = CURLE_READ_ERROR; + else { + part->data = strdup(filename); + if(!part->data) + result = CURLE_OUT_OF_MEMORY; + else { + part->datasize = -1; + if(S_ISREG(sbuf.st_mode)) { + part->datasize = filesize(filename, sbuf); + part->seekfunc = mime_file_seek; + } + + part->readfunc = mime_file_read; + part->freefunc = mime_file_free; + part->kind = MIMEKIND_FILE; + + /* As a side effect, set the filename to the current file's base name. + It is possible to withdraw this by explicitly calling + curl_mime_filename() with a NULL filename argument after the current + call. */ + base = strippath(filename); + if(!base) + result = CURLE_OUT_OF_MEMORY; + else { + result = curl_mime_filename(part, base); + free(base); + } + } + } + } + return result; +} + +/* Set mime part type. */ +CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype) +{ + if(!part) + return CURLE_BAD_FUNCTION_ARGUMENT; + + Curl_safefree(part->mimetype); + + if(mimetype) { + part->mimetype = strdup(mimetype); + if(!part->mimetype) + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +/* Set mime data transfer encoder. */ +CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding) +{ + CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; + const struct mime_encoder *mep; + + if(!part) + return result; + + part->encoder = NULL; + + if(!encoding) + return CURLE_OK; /* Removing current encoder. */ + + for(mep = encoders; mep->name; mep++) + if(strcasecompare(encoding, mep->name)) { + part->encoder = mep; + result = CURLE_OK; + } + + return result; +} + +/* Set mime part headers. */ +CURLcode curl_mime_headers(curl_mimepart *part, + struct curl_slist *headers, int take_ownership) +{ + if(!part) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(part->flags & MIME_USERHEADERS_OWNER) { + if(part->userheaders != headers) /* Allow setting twice the same list. */ + curl_slist_free_all(part->userheaders); + part->flags &= ~(unsigned int)MIME_USERHEADERS_OWNER; + } + part->userheaders = headers; + if(headers && take_ownership) + part->flags |= MIME_USERHEADERS_OWNER; + return CURLE_OK; +} + +/* Set mime part content from callback. */ +CURLcode curl_mime_data_cb(curl_mimepart *part, curl_off_t datasize, + curl_read_callback readfunc, + curl_seek_callback seekfunc, + curl_free_callback freefunc, void *arg) +{ + if(!part) + return CURLE_BAD_FUNCTION_ARGUMENT; + + cleanup_part_content(part); + + if(readfunc) { + part->readfunc = readfunc; + part->seekfunc = seekfunc; + part->freefunc = freefunc; + part->arg = arg; + part->datasize = datasize; + part->kind = MIMEKIND_CALLBACK; + } + + return CURLE_OK; +} + +/* Set mime part content from subparts. */ +CURLcode Curl_mime_set_subparts(curl_mimepart *part, + curl_mime *subparts, int take_ownership) +{ + curl_mime *root; + + if(!part) + return CURLE_BAD_FUNCTION_ARGUMENT; + + /* Accept setting twice the same subparts. */ + if(part->kind == MIMEKIND_MULTIPART && part->arg == subparts) + return CURLE_OK; + + cleanup_part_content(part); + + if(subparts) { + /* Should not have been attached already. */ + if(subparts->parent) + return CURLE_BAD_FUNCTION_ARGUMENT; + + /* Should not be the part's root. */ + root = part->parent; + if(root) { + while(root->parent && root->parent->parent) + root = root->parent->parent; + if(subparts == root) { + /* cannot add as a subpart of itself. */ + return CURLE_BAD_FUNCTION_ARGUMENT; + } + } + + subparts->parent = part; + /* Subparts are processed internally: no read callback. */ + part->seekfunc = mime_subparts_seek; + part->freefunc = take_ownership? mime_subparts_free: mime_subparts_unbind; + part->arg = subparts; + part->datasize = -1; + part->kind = MIMEKIND_MULTIPART; + } + + return CURLE_OK; +} + +CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts) +{ + return Curl_mime_set_subparts(part, subparts, TRUE); +} + + +/* Readback from top mime. */ +/* Argument is the dummy top part. */ +size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream) +{ + curl_mimepart *part = (curl_mimepart *) instream; + size_t ret; + bool hasread; + + (void) size; /* Always 1. */ + + do { + hasread = FALSE; + ret = readback_part(part, buffer, nitems, &hasread); + /* + * If this is not possible to get some data without calling more than + * one read callback (probably because a content encoder is not able to + * deliver a new bunch for the few data accumulated so far), force another + * read until we get enough data or a special exit code. + */ + } while(ret == STOP_FILLING); + + return ret; +} + +/* Rewind mime stream. */ +static CURLcode mime_rewind(curl_mimepart *part) +{ + return mime_part_rewind(part) == CURL_SEEKFUNC_OK? + CURLE_OK: CURLE_SEND_FAIL_REWIND; +} + +/* Compute header list size. */ +static size_t slist_size(struct curl_slist *s, + size_t overhead, const char *skip, size_t skiplen) +{ + size_t size = 0; + + for(; s; s = s->next) + if(!skip || !match_header(s, skip, skiplen)) + size += strlen(s->data) + overhead; + return size; +} + +/* Get/compute multipart size. */ +static curl_off_t multipart_size(curl_mime *mime) +{ + curl_off_t size; + curl_off_t boundarysize; + curl_mimepart *part; + + if(!mime) + return 0; /* Not present -> empty. */ + + boundarysize = 4 + MIME_BOUNDARY_LEN + 2; + size = boundarysize; /* Final boundary - CRLF after headers. */ + + for(part = mime->firstpart; part; part = part->nextpart) { + curl_off_t sz = mime_size(part); + + if(sz < 0) + size = sz; + + if(size >= 0) + size += boundarysize + sz; + } + + return size; +} + +/* Get/compute mime size. */ +static curl_off_t mime_size(curl_mimepart *part) +{ + curl_off_t size; + + if(part->kind == MIMEKIND_MULTIPART) + part->datasize = multipart_size(part->arg); + + size = part->datasize; + + if(part->encoder) + size = part->encoder->sizefunc(part); + + if(size >= 0 && !(part->flags & MIME_BODY_ONLY)) { + /* Compute total part size. */ + size += slist_size(part->curlheaders, 2, NULL, 0); + size += slist_size(part->userheaders, 2, + STRCONST("Content-Type")); + size += 2; /* CRLF after headers. */ + } + return size; +} + +/* Add a header. */ +/* VARARGS2 */ +CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...) +{ + struct curl_slist *hdr = NULL; + char *s = NULL; + va_list ap; + + va_start(ap, fmt); + s = curl_mvaprintf(fmt, ap); + va_end(ap); + + if(s) { + hdr = Curl_slist_append_nodup(*slp, s); + if(hdr) + *slp = hdr; + else + free(s); + } + + return hdr? CURLE_OK: CURLE_OUT_OF_MEMORY; +} + +/* Add a content type header. */ +static CURLcode add_content_type(struct curl_slist **slp, + const char *type, const char *boundary) +{ + return Curl_mime_add_header(slp, "Content-Type: %s%s%s", type, + boundary? "; boundary=": "", + boundary? boundary: ""); +} + +const char *Curl_mime_contenttype(const char *filename) +{ + /* + * If no content type was specified, we scan through a few well-known + * extensions and pick the first we match! + */ + struct ContentType { + const char *extension; + const char *type; + }; + static const struct ContentType ctts[] = { + {".gif", "image/gif"}, + {".jpg", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".png", "image/png"}, + {".svg", "image/svg+xml"}, + {".txt", "text/plain"}, + {".htm", "text/html"}, + {".html", "text/html"}, + {".pdf", "application/pdf"}, + {".xml", "application/xml"} + }; + + if(filename) { + size_t len1 = strlen(filename); + const char *nameend = filename + len1; + unsigned int i; + + for(i = 0; i < sizeof(ctts) / sizeof(ctts[0]); i++) { + size_t len2 = strlen(ctts[i].extension); + + if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension)) + return ctts[i].type; + } + } + return NULL; +} + +static bool content_type_match(const char *contenttype, + const char *target, size_t len) +{ + if(contenttype && strncasecompare(contenttype, target, len)) + switch(contenttype[len]) { + case '\0': + case '\t': + case '\r': + case '\n': + case ' ': + case ';': + return TRUE; + } + return FALSE; +} + +CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, + curl_mimepart *part, + const char *contenttype, + const char *disposition, + enum mimestrategy strategy) +{ + curl_mime *mime = NULL; + const char *boundary = NULL; + char *customct; + const char *cte = NULL; + CURLcode ret = CURLE_OK; + + /* Get rid of previously prepared headers. */ + curl_slist_free_all(part->curlheaders); + part->curlheaders = NULL; + + /* Be sure we will not access old headers later. */ + if(part->state.state == MIMESTATE_CURLHEADERS) + mimesetstate(&part->state, MIMESTATE_CURLHEADERS, NULL); + + /* Check if content type is specified. */ + customct = part->mimetype; + if(!customct) + customct = search_header(part->userheaders, STRCONST("Content-Type")); + if(customct) + contenttype = customct; + + /* If content type is not specified, try to determine it. */ + if(!contenttype) { + switch(part->kind) { + case MIMEKIND_MULTIPART: + contenttype = MULTIPART_CONTENTTYPE_DEFAULT; + break; + case MIMEKIND_FILE: + contenttype = Curl_mime_contenttype(part->filename); + if(!contenttype) + contenttype = Curl_mime_contenttype(part->data); + if(!contenttype && part->filename) + contenttype = FILE_CONTENTTYPE_DEFAULT; + break; + default: + contenttype = Curl_mime_contenttype(part->filename); + break; + } + } + + if(part->kind == MIMEKIND_MULTIPART) { + mime = (curl_mime *) part->arg; + if(mime) + boundary = mime->boundary; + } + else if(contenttype && !customct && + content_type_match(contenttype, STRCONST("text/plain"))) + if(strategy == MIMESTRATEGY_MAIL || !part->filename) + contenttype = NULL; + + /* Issue content-disposition header only if not already set by caller. */ + if(!search_header(part->userheaders, STRCONST("Content-Disposition"))) { + if(!disposition) + if(part->filename || part->name || + (contenttype && !strncasecompare(contenttype, "multipart/", 10))) + disposition = DISPOSITION_DEFAULT; + if(disposition && curl_strequal(disposition, "attachment") && + !part->name && !part->filename) + disposition = NULL; + if(disposition) { + char *name = NULL; + char *filename = NULL; + + if(part->name) { + name = escape_string(data, part->name, strategy); + if(!name) + ret = CURLE_OUT_OF_MEMORY; + } + if(!ret && part->filename) { + filename = escape_string(data, part->filename, strategy); + if(!filename) + ret = CURLE_OUT_OF_MEMORY; + } + if(!ret) + ret = Curl_mime_add_header(&part->curlheaders, + "Content-Disposition: %s%s%s%s%s%s%s", + disposition, + name? "; name=\"": "", + name? name: "", + name? "\"": "", + filename? "; filename=\"": "", + filename? filename: "", + filename? "\"": ""); + Curl_safefree(name); + Curl_safefree(filename); + if(ret) + return ret; + } + } + + /* Issue Content-Type header. */ + if(contenttype) { + ret = add_content_type(&part->curlheaders, contenttype, boundary); + if(ret) + return ret; + } + + /* Content-Transfer-Encoding header. */ + if(!search_header(part->userheaders, + STRCONST("Content-Transfer-Encoding"))) { + if(part->encoder) + cte = part->encoder->name; + else if(contenttype && strategy == MIMESTRATEGY_MAIL && + part->kind != MIMEKIND_MULTIPART) + cte = "8bit"; + if(cte) { + ret = Curl_mime_add_header(&part->curlheaders, + "Content-Transfer-Encoding: %s", cte); + if(ret) + return ret; + } + } + + /* If we were reading curl-generated headers, restart with new ones (this + should not occur). */ + if(part->state.state == MIMESTATE_CURLHEADERS) + mimesetstate(&part->state, MIMESTATE_CURLHEADERS, part->curlheaders); + + /* Process subparts. */ + if(part->kind == MIMEKIND_MULTIPART && mime) { + curl_mimepart *subpart; + + disposition = NULL; + if(content_type_match(contenttype, STRCONST("multipart/form-data"))) + disposition = "form-data"; + for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) { + ret = Curl_mime_prepare_headers(data, subpart, NULL, + disposition, strategy); + if(ret) + return ret; + } + } + return ret; +} + +/* Recursively reset paused status in the given part. */ +static void mime_unpause(curl_mimepart *part) +{ + if(part) { + if(part->lastreadstatus == CURL_READFUNC_PAUSE) + part->lastreadstatus = 1; /* Successful read status. */ + if(part->kind == MIMEKIND_MULTIPART) { + curl_mime *mime = (curl_mime *) part->arg; + + if(mime) { + curl_mimepart *subpart; + + for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) + mime_unpause(subpart); + } + } + } +} + +struct cr_mime_ctx { + struct Curl_creader super; + curl_mimepart *part; + curl_off_t total_len; + curl_off_t read_len; + CURLcode error_result; + BIT(seen_eos); + BIT(errored); +}; + +static CURLcode cr_mime_init(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + ctx->total_len = -1; + ctx->read_len = 0; + return CURLE_OK; +} + +/* Real client reader to installed client callbacks. */ +static CURLcode cr_mime_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_mime_ctx *ctx = reader->ctx; + size_t nread; + + /* Once we have errored, we will return the same error forever */ + if(ctx->errored) { + *pnread = 0; + *peos = FALSE; + return ctx->error_result; + } + if(ctx->seen_eos) { + *pnread = 0; + *peos = TRUE; + return CURLE_OK; + } + /* respect length limitations */ + if(ctx->total_len >= 0) { + curl_off_t remain = ctx->total_len - ctx->read_len; + if(remain <= 0) + blen = 0; + else if(remain < (curl_off_t)blen) + blen = (size_t)remain; + } + nread = 0; + if(blen) { + nread = Curl_mime_read(buf, 1, blen, ctx->part); + } + + switch(nread) { + case 0: + if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { + failf(data, "client mime read EOF fail, " + "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T + " of needed bytes read", ctx->read_len, ctx->total_len); + return CURLE_READ_ERROR; + } + *pnread = 0; + *peos = TRUE; + ctx->seen_eos = TRUE; + break; + + case CURL_READFUNC_ABORT: + failf(data, "operation aborted by callback"); + *pnread = 0; + *peos = FALSE; + ctx->errored = TRUE; + ctx->error_result = CURLE_ABORTED_BY_CALLBACK; + return CURLE_ABORTED_BY_CALLBACK; + + case CURL_READFUNC_PAUSE: + /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ + data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ + *pnread = 0; + *peos = FALSE; + break; /* nothing was read */ + + default: + if(nread > blen) { + /* the read function returned a too large value */ + failf(data, "read function returned funny value"); + *pnread = 0; + *peos = FALSE; + ctx->errored = TRUE; + ctx->error_result = CURLE_READ_ERROR; + return CURLE_READ_ERROR; + } + ctx->read_len += nread; + if(ctx->total_len >= 0) + ctx->seen_eos = (ctx->read_len >= ctx->total_len); + *pnread = nread; + *peos = ctx->seen_eos; + break; + } + DEBUGF(infof(data, "cr_mime_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T + ", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, %zu, %d", + blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos)); + return CURLE_OK; +} + +static bool cr_mime_needs_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + return ctx->read_len > 0; +} + +static curl_off_t cr_mime_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + return ctx->total_len; +} + +static CURLcode cr_mime_resume_from(struct Curl_easy *data, + struct Curl_creader *reader, + curl_off_t offset) +{ + struct cr_mime_ctx *ctx = reader->ctx; + + if(offset > 0) { + curl_off_t passed = 0; + + do { + char scratch[4*1024]; + size_t readthisamountnow = + (offset - passed > (curl_off_t)sizeof(scratch)) ? + sizeof(scratch) : + curlx_sotouz(offset - passed); + size_t nread; + + nread = Curl_mime_read(scratch, 1, readthisamountnow, ctx->part); + passed += (curl_off_t)nread; + if((nread == 0) || (nread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T + " bytes from the mime post", passed); + return CURLE_READ_ERROR; + } + } while(passed < offset); + + /* now, decrease the size of the read */ + if(ctx->total_len > 0) { + ctx->total_len -= offset; + + if(ctx->total_len <= 0) { + failf(data, "Mime post already completely uploaded"); + return CURLE_PARTIAL_FILE; + } + } + /* we have passed, proceed as normal */ + } + return CURLE_OK; +} + +static CURLcode cr_mime_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + CURLcode result = mime_rewind(ctx->part); + if(result) + failf(data, "Cannot rewind mime/post data"); + return result; +} + +static CURLcode cr_mime_unpause(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + mime_unpause(ctx->part); + return CURLE_OK; +} + +static bool cr_mime_is_paused(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + return (ctx->part && ctx->part->lastreadstatus == CURL_READFUNC_PAUSE); +} + +static const struct Curl_crtype cr_mime = { + "cr-mime", + cr_mime_init, + cr_mime_read, + Curl_creader_def_close, + cr_mime_needs_rewind, + cr_mime_total_length, + cr_mime_resume_from, + cr_mime_rewind, + cr_mime_unpause, + cr_mime_is_paused, + Curl_creader_def_done, + sizeof(struct cr_mime_ctx) +}; + +CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part) +{ + struct Curl_creader *r; + struct cr_mime_ctx *ctx; + CURLcode result; + + result = Curl_creader_create(&r, data, &cr_mime, CURL_CR_CLIENT); + if(result) + return result; + ctx = r->ctx; + ctx->part = part; + /* Make sure we will read the entire mime structure. */ + result = mime_rewind(ctx->part); + if(result) { + Curl_creader_free(data, r); + return result; + } + ctx->total_len = mime_size(ctx->part); + + return Curl_creader_set(data, r); +} + +#else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP || + !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */ + +/* Mime not compiled in: define stubs for externally-referenced functions. */ +curl_mime *curl_mime_init(CURL *easy) +{ + (void) easy; + return NULL; +} + +void curl_mime_free(curl_mime *mime) +{ + (void) mime; +} + +curl_mimepart *curl_mime_addpart(curl_mime *mime) +{ + (void) mime; + return NULL; +} + +CURLcode curl_mime_name(curl_mimepart *part, const char *name) +{ + (void) part; + (void) name; + return CURLE_NOT_BUILT_IN; +} + +CURLcode curl_mime_filename(curl_mimepart *part, const char *filename) +{ + (void) part; + (void) filename; + return CURLE_NOT_BUILT_IN; +} + +CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype) +{ + (void) part; + (void) mimetype; + return CURLE_NOT_BUILT_IN; +} + +CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding) +{ + (void) part; + (void) encoding; + return CURLE_NOT_BUILT_IN; +} + +CURLcode curl_mime_data(curl_mimepart *part, + const char *data, size_t datasize) +{ + (void) part; + (void) data; + (void) datasize; + return CURLE_NOT_BUILT_IN; +} + +CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename) +{ + (void) part; + (void) filename; + return CURLE_NOT_BUILT_IN; +} + +CURLcode curl_mime_data_cb(curl_mimepart *part, + curl_off_t datasize, + curl_read_callback readfunc, + curl_seek_callback seekfunc, + curl_free_callback freefunc, + void *arg) +{ + (void) part; + (void) datasize; + (void) readfunc; + (void) seekfunc; + (void) freefunc; + (void) arg; + return CURLE_NOT_BUILT_IN; +} + +CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts) +{ + (void) part; + (void) subparts; + return CURLE_NOT_BUILT_IN; +} + +CURLcode curl_mime_headers(curl_mimepart *part, + struct curl_slist *headers, int take_ownership) +{ + (void) part; + (void) headers; + (void) take_ownership; + return CURLE_NOT_BUILT_IN; +} + +CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...) +{ + (void)slp; + (void)fmt; + return CURLE_NOT_BUILT_IN; +} + +#endif /* if disabled */ diff --git a/third_party/curl/lib/mime.h b/third_party/curl/lib/mime.h new file mode 100644 index 0000000000..5073a38f70 --- /dev/null +++ b/third_party/curl/lib/mime.h @@ -0,0 +1,176 @@ +#ifndef HEADER_CURL_MIME_H +#define HEADER_CURL_MIME_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#define MIME_BOUNDARY_DASHES 24 /* leading boundary dashes */ +#define MIME_RAND_BOUNDARY_CHARS 22 /* Nb. of random boundary chars. */ +#define MAX_ENCODED_LINE_LENGTH 76 /* Maximum encoded line length. */ +#define ENCODING_BUFFER_SIZE 256 /* Encoding temp buffers size. */ + +/* Part flags. */ +#define MIME_USERHEADERS_OWNER (1 << 0) +#define MIME_BODY_ONLY (1 << 1) +#define MIME_FAST_READ (1 << 2) + +#define FILE_CONTENTTYPE_DEFAULT "application/octet-stream" +#define MULTIPART_CONTENTTYPE_DEFAULT "multipart/mixed" +#define DISPOSITION_DEFAULT "attachment" + +/* Part source kinds. */ +enum mimekind { + MIMEKIND_NONE = 0, /* Part not set. */ + MIMEKIND_DATA, /* Allocated mime data. */ + MIMEKIND_FILE, /* Data from file. */ + MIMEKIND_CALLBACK, /* Data from `read' callback. */ + MIMEKIND_MULTIPART, /* Data is a mime subpart. */ + MIMEKIND_LAST +}; + +/* Readback state tokens. */ +enum mimestate { + MIMESTATE_BEGIN, /* Readback has not yet started. */ + MIMESTATE_CURLHEADERS, /* In curl-generated headers. */ + MIMESTATE_USERHEADERS, /* In caller's supplied headers. */ + MIMESTATE_EOH, /* End of headers. */ + MIMESTATE_BODY, /* Placeholder. */ + MIMESTATE_BOUNDARY1, /* In boundary prefix. */ + MIMESTATE_BOUNDARY2, /* In boundary. */ + MIMESTATE_CONTENT, /* In content. */ + MIMESTATE_END, /* End of part reached. */ + MIMESTATE_LAST +}; + +/* Mime headers strategies. */ +enum mimestrategy { + MIMESTRATEGY_MAIL, /* Mime mail. */ + MIMESTRATEGY_FORM, /* HTTP post form. */ + MIMESTRATEGY_LAST +}; + +/* Content transfer encoder. */ +struct mime_encoder { + const char * name; /* Encoding name. */ + size_t (*encodefunc)(char *buffer, size_t size, bool ateof, + curl_mimepart *part); /* Encoded read. */ + curl_off_t (*sizefunc)(curl_mimepart *part); /* Encoded size. */ +}; + +/* Content transfer encoder state. */ +struct mime_encoder_state { + size_t pos; /* Position on output line. */ + size_t bufbeg; /* Next data index in input buffer. */ + size_t bufend; /* First unused byte index in input buffer. */ + char buf[ENCODING_BUFFER_SIZE]; /* Input buffer. */ +}; + +/* Mime readback state. */ +struct mime_state { + enum mimestate state; /* Current state token. */ + void *ptr; /* State-dependent pointer. */ + curl_off_t offset; /* State-dependent offset. */ +}; + +/* Boundary string length. */ +#define MIME_BOUNDARY_LEN (MIME_BOUNDARY_DASHES + MIME_RAND_BOUNDARY_CHARS) + +/* A mime multipart. */ +struct curl_mime { + curl_mimepart *parent; /* Parent part. */ + curl_mimepart *firstpart; /* First part. */ + curl_mimepart *lastpart; /* Last part. */ + char boundary[MIME_BOUNDARY_LEN + 1]; /* The part boundary. */ + struct mime_state state; /* Current readback state. */ +}; + +/* A mime part. */ +struct curl_mimepart { + curl_mime *parent; /* Parent mime structure. */ + curl_mimepart *nextpart; /* Forward linked list. */ + enum mimekind kind; /* The part kind. */ + unsigned int flags; /* Flags. */ + char *data; /* Memory data or filename. */ + curl_read_callback readfunc; /* Read function. */ + curl_seek_callback seekfunc; /* Seek function. */ + curl_free_callback freefunc; /* Argument free function. */ + void *arg; /* Argument to callback functions. */ + FILE *fp; /* File pointer. */ + struct curl_slist *curlheaders; /* Part headers. */ + struct curl_slist *userheaders; /* Part headers. */ + char *mimetype; /* Part mime type. */ + char *filename; /* Remote filename. */ + char *name; /* Data name. */ + curl_off_t datasize; /* Expected data size. */ + struct mime_state state; /* Current readback state. */ + const struct mime_encoder *encoder; /* Content data encoder. */ + struct mime_encoder_state encstate; /* Data encoder state. */ + size_t lastreadstatus; /* Last read callback returned status. */ +}; + +CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...) + CURL_PRINTF(2, 3); + +#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \ + !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_IMAP)) + +/* Prototypes. */ +void Curl_mime_initpart(struct curl_mimepart *part); +void Curl_mime_cleanpart(struct curl_mimepart *part); +CURLcode Curl_mime_duppart(struct Curl_easy *data, + struct curl_mimepart *dst, + const curl_mimepart *src); +CURLcode Curl_mime_set_subparts(struct curl_mimepart *part, + struct curl_mime *subparts, + int take_ownership); +CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, + struct curl_mimepart *part, + const char *contenttype, + const char *disposition, + enum mimestrategy strategy); +size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, + void *instream); +const char *Curl_mime_contenttype(const char *filename); + +/** + * Install a client reader as upload source that reads the given + * mime part. + */ +CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part); + +#else +/* if disabled */ +#define Curl_mime_initpart(x) +#define Curl_mime_cleanpart(x) +#define Curl_mime_duppart(x,y,z) CURLE_OK /* Nothing to duplicate. Succeed */ +#define Curl_mime_set_subparts(a,b,c) CURLE_NOT_BUILT_IN +#define Curl_mime_prepare_headers(a,b,c,d,e) CURLE_NOT_BUILT_IN +#define Curl_mime_read NULL +#define Curl_creader_set_mime(x,y) ((void)x, CURLE_NOT_BUILT_IN) +#endif + + +#endif /* HEADER_CURL_MIME_H */ diff --git a/third_party/curl/lib/mprintf.c b/third_party/curl/lib/mprintf.c new file mode 100644 index 0000000000..89cbb87ce4 --- /dev/null +++ b/third_party/curl/lib/mprintf.c @@ -0,0 +1,1204 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + */ + +#include "curl_setup.h" +#include "dynbuf.h" +#include "curl_printf.h" +#include + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* + * If SIZEOF_SIZE_T has not been defined, default to the size of long. + */ + +#ifdef HAVE_LONGLONG +# define LONG_LONG_TYPE long long +# define HAVE_LONG_LONG_TYPE +#else +# if defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# define LONG_LONG_TYPE __int64 +# define HAVE_LONG_LONG_TYPE +# else +# undef LONG_LONG_TYPE +# undef HAVE_LONG_LONG_TYPE +# endif +#endif + +/* + * Max integer data types that mprintf.c is capable + */ + +#ifdef HAVE_LONG_LONG_TYPE +# define mp_intmax_t LONG_LONG_TYPE +# define mp_uintmax_t unsigned LONG_LONG_TYPE +#else +# define mp_intmax_t long +# define mp_uintmax_t unsigned long +#endif + +#define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should + fit negative DBL_MAX (317 letters) */ +#define MAX_PARAMETERS 128 /* number of input arguments */ +#define MAX_SEGMENTS 128 /* number of output segments */ + +#ifdef __AMIGA__ +# undef FORMAT_INT +#endif + +/* Lower-case digits. */ +static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +/* Upper-case digits. */ +static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +#define OUTCHAR(x) \ + do { \ + if(!stream((unsigned char)x, userp)) \ + done++; \ + else \ + return done; /* return on failure */ \ + } while(0) + +/* Data type to read from the arglist */ +typedef enum { + FORMAT_STRING, + FORMAT_PTR, + FORMAT_INTPTR, + FORMAT_INT, + FORMAT_LONG, + FORMAT_LONGLONG, + FORMAT_INTU, + FORMAT_LONGU, + FORMAT_LONGLONGU, + FORMAT_DOUBLE, + FORMAT_LONGDOUBLE, + FORMAT_WIDTH, + FORMAT_PRECISION +} FormatType; + +/* conversion and display flags */ +enum { + FLAGS_SPACE = 1<<0, + FLAGS_SHOWSIGN = 1<<1, + FLAGS_LEFT = 1<<2, + FLAGS_ALT = 1<<3, + FLAGS_SHORT = 1<<4, + FLAGS_LONG = 1<<5, + FLAGS_LONGLONG = 1<<6, + FLAGS_LONGDOUBLE = 1<<7, + FLAGS_PAD_NIL = 1<<8, + FLAGS_UNSIGNED = 1<<9, + FLAGS_OCTAL = 1<<10, + FLAGS_HEX = 1<<11, + FLAGS_UPPER = 1<<12, + FLAGS_WIDTH = 1<<13, /* '*' or '*$' used */ + FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */ + FLAGS_PREC = 1<<15, /* precision was specified */ + FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */ + FLAGS_CHAR = 1<<17, /* %c story */ + FLAGS_FLOATE = 1<<18, /* %e or %E */ + FLAGS_FLOATG = 1<<19, /* %g or %G */ + FLAGS_SUBSTR = 1<<20 /* no input, only substring */ +}; + +enum { + DOLLAR_UNKNOWN, + DOLLAR_NOPE, + DOLLAR_USE +}; + +/* + * Describes an input va_arg type and hold its value. + */ +struct va_input { + FormatType type; /* FormatType */ + union { + char *str; + void *ptr; + mp_intmax_t nums; /* signed */ + mp_uintmax_t numu; /* unsigned */ + double dnum; + } val; +}; + +/* + * Describes an output segment. + */ +struct outsegment { + int width; /* width OR width parameter number */ + int precision; /* precision OR precision parameter number */ + unsigned int flags; + unsigned int input; /* input argument array index */ + char *start; /* format string start to output */ + size_t outlen; /* number of bytes from the format string to output */ +}; + +struct nsprintf { + char *buffer; + size_t length; + size_t max; +}; + +struct asprintf { + struct dynbuf *b; + char merr; +}; + +/* the provided input number is 1-based but this returns the number 0-based. + + returns -1 if no valid number was provided. +*/ +static int dollarstring(char *input, char **end) +{ + if(ISDIGIT(*input)) { + int number = 0; + do { + if(number < MAX_PARAMETERS) { + number *= 10; + number += *input - '0'; + } + input++; + } while(ISDIGIT(*input)); + + if(number && (number <= MAX_PARAMETERS) && ('$' == *input)) { + *end = ++input; + return number - 1; + } + } + return -1; +} + +/* + * Parse the format string. + * + * Create two arrays. One describes the inputs, one describes the outputs. + * + * Returns zero on success. + */ + +#define PFMT_OK 0 +#define PFMT_DOLLAR 1 /* bad dollar for main param */ +#define PFMT_DOLLARWIDTH 2 /* bad dollar use for width */ +#define PFMT_DOLLARPREC 3 /* bad dollar use for precision */ +#define PFMT_MANYARGS 4 /* too many input arguments used */ +#define PFMT_PREC 5 /* precision overflow */ +#define PFMT_PRECMIX 6 /* bad mix of precision specifiers */ +#define PFMT_WIDTH 7 /* width overflow */ +#define PFMT_INPUTGAP 8 /* gap in arguments */ +#define PFMT_WIDTHARG 9 /* attempted to use same arg twice, for width */ +#define PFMT_PRECARG 10 /* attempted to use same arg twice, for prec */ +#define PFMT_MANYSEGS 11 /* maxed out output segments */ + +static int parsefmt(const char *format, + struct outsegment *out, + struct va_input *in, + int *opieces, + int *ipieces, va_list arglist) +{ + char *fmt = (char *)format; + int param_num = 0; + int param; + int width; + int precision; + unsigned int flags; + FormatType type; + int max_param = -1; + int i; + int ocount = 0; + unsigned char usedinput[MAX_PARAMETERS/8]; + size_t outlen = 0; + struct outsegment *optr; + int use_dollar = DOLLAR_UNKNOWN; + char *start = fmt; + + /* clear, set a bit for each used input */ + memset(usedinput, 0, sizeof(usedinput)); + + while(*fmt) { + if(*fmt == '%') { + struct va_input *iptr; + bool loopit = TRUE; + fmt++; + outlen = (size_t)(fmt - start - 1); + if(*fmt == '%') { + /* this means a %% that should be output only as %. Create an output + segment. */ + if(outlen) { + optr = &out[ocount++]; + if(ocount > MAX_SEGMENTS) + return PFMT_MANYSEGS; + optr->input = 0; + optr->flags = FLAGS_SUBSTR; + optr->start = start; + optr->outlen = outlen; + } + start = fmt; + fmt++; + continue; /* while */ + } + + flags = 0; + width = precision = 0; + + if(use_dollar != DOLLAR_NOPE) { + param = dollarstring(fmt, &fmt); + if(param < 0) { + if(use_dollar == DOLLAR_USE) + /* illegal combo */ + return PFMT_DOLLAR; + + /* we got no positional, just get the next arg */ + param = -1; + use_dollar = DOLLAR_NOPE; + } + else + use_dollar = DOLLAR_USE; + } + else + param = -1; + + /* Handle the flags */ + while(loopit) { + switch(*fmt++) { + case ' ': + flags |= FLAGS_SPACE; + break; + case '+': + flags |= FLAGS_SHOWSIGN; + break; + case '-': + flags |= FLAGS_LEFT; + flags &= ~(unsigned int)FLAGS_PAD_NIL; + break; + case '#': + flags |= FLAGS_ALT; + break; + case '.': + if('*' == *fmt) { + /* The precision is picked from a specified parameter */ + flags |= FLAGS_PRECPARAM; + fmt++; + + if(use_dollar == DOLLAR_USE) { + precision = dollarstring(fmt, &fmt); + if(precision < 0) + /* illegal combo */ + return PFMT_DOLLARPREC; + } + else + /* get it from the next argument */ + precision = -1; + } + else { + bool is_neg = FALSE; + flags |= FLAGS_PREC; + precision = 0; + if('-' == *fmt) { + is_neg = TRUE; + fmt++; + } + while(ISDIGIT(*fmt)) { + if(precision > INT_MAX/10) + return PFMT_PREC; + precision *= 10; + precision += *fmt - '0'; + fmt++; + } + if(is_neg) + precision = -precision; + } + if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) == + (FLAGS_PREC | FLAGS_PRECPARAM)) + /* it is not permitted to use both kinds of precision for the same + argument */ + return PFMT_PRECMIX; + break; + case 'h': + flags |= FLAGS_SHORT; + break; +#if defined(_WIN32) || defined(_WIN32_WCE) + case 'I': + /* Non-ANSI integer extensions I32 I64 */ + if((fmt[0] == '3') && (fmt[1] == '2')) { + flags |= FLAGS_LONG; + fmt += 2; + } + else if((fmt[0] == '6') && (fmt[1] == '4')) { + flags |= FLAGS_LONGLONG; + fmt += 2; + } + else { +#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) + flags |= FLAGS_LONGLONG; +#else + flags |= FLAGS_LONG; +#endif + } + break; +#endif /* _WIN32 || _WIN32_WCE */ + case 'l': + if(flags & FLAGS_LONG) + flags |= FLAGS_LONGLONG; + else + flags |= FLAGS_LONG; + break; + case 'L': + flags |= FLAGS_LONGDOUBLE; + break; + case 'q': + flags |= FLAGS_LONGLONG; + break; + case 'z': + /* the code below generates a warning if -Wunreachable-code is + used */ +#if (SIZEOF_SIZE_T > SIZEOF_LONG) + flags |= FLAGS_LONGLONG; +#else + flags |= FLAGS_LONG; +#endif + break; + case 'O': +#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) + flags |= FLAGS_LONGLONG; +#else + flags |= FLAGS_LONG; +#endif + break; + case '0': + if(!(flags & FLAGS_LEFT)) + flags |= FLAGS_PAD_NIL; + FALLTHROUGH(); + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + flags |= FLAGS_WIDTH; + width = 0; + fmt--; + do { + if(width > INT_MAX/10) + return PFMT_WIDTH; + width *= 10; + width += *fmt - '0'; + fmt++; + } while(ISDIGIT(*fmt)); + break; + case '*': /* read width from argument list */ + flags |= FLAGS_WIDTHPARAM; + if(use_dollar == DOLLAR_USE) { + width = dollarstring(fmt, &fmt); + if(width < 0) + /* illegal combo */ + return PFMT_DOLLARWIDTH; + } + else + /* pick from the next argument */ + width = -1; + break; + default: + loopit = FALSE; + fmt--; + break; + } /* switch */ + } /* while */ + + switch(*fmt) { + case 'S': + flags |= FLAGS_ALT; + FALLTHROUGH(); + case 's': + type = FORMAT_STRING; + break; + case 'n': + type = FORMAT_INTPTR; + break; + case 'p': + type = FORMAT_PTR; + break; + case 'd': + case 'i': + if(flags & FLAGS_LONGLONG) + type = FORMAT_LONGLONG; + else if(flags & FLAGS_LONG) + type = FORMAT_LONG; + else + type = FORMAT_INT; + break; + case 'u': + if(flags & FLAGS_LONGLONG) + type = FORMAT_LONGLONGU; + else if(flags & FLAGS_LONG) + type = FORMAT_LONGU; + else + type = FORMAT_INTU; + flags |= FLAGS_UNSIGNED; + break; + case 'o': + type = FORMAT_INT; + flags |= FLAGS_OCTAL; + break; + case 'x': + type = FORMAT_INTU; + flags |= FLAGS_HEX|FLAGS_UNSIGNED; + break; + case 'X': + type = FORMAT_INTU; + flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED; + break; + case 'c': + type = FORMAT_INT; + flags |= FLAGS_CHAR; + break; + case 'f': + type = FORMAT_DOUBLE; + break; + case 'e': + type = FORMAT_DOUBLE; + flags |= FLAGS_FLOATE; + break; + case 'E': + type = FORMAT_DOUBLE; + flags |= FLAGS_FLOATE|FLAGS_UPPER; + break; + case 'g': + type = FORMAT_DOUBLE; + flags |= FLAGS_FLOATG; + break; + case 'G': + type = FORMAT_DOUBLE; + flags |= FLAGS_FLOATG|FLAGS_UPPER; + break; + default: + /* invalid instruction, disregard and continue */ + continue; + } /* switch */ + + if(flags & FLAGS_WIDTHPARAM) { + if(width < 0) + width = param_num++; + else { + /* if this identifies a parameter already used, this + is illegal */ + if(usedinput[width/8] & (1 << (width&7))) + return PFMT_WIDTHARG; + } + if(width >= MAX_PARAMETERS) + return PFMT_MANYARGS; + if(width >= max_param) + max_param = width; + + in[width].type = FORMAT_WIDTH; + /* mark as used */ + usedinput[width/8] |= (unsigned char)(1 << (width&7)); + } + + if(flags & FLAGS_PRECPARAM) { + if(precision < 0) + precision = param_num++; + else { + /* if this identifies a parameter already used, this + is illegal */ + if(usedinput[precision/8] & (1 << (precision&7))) + return PFMT_PRECARG; + } + if(precision >= MAX_PARAMETERS) + return PFMT_MANYARGS; + if(precision >= max_param) + max_param = precision; + + in[precision].type = FORMAT_PRECISION; + usedinput[precision/8] |= (unsigned char)(1 << (precision&7)); + } + + /* Handle the specifier */ + if(param < 0) + param = param_num++; + if(param >= MAX_PARAMETERS) + return PFMT_MANYARGS; + if(param >= max_param) + max_param = param; + + iptr = &in[param]; + iptr->type = type; + + /* mark this input as used */ + usedinput[param/8] |= (unsigned char)(1 << (param&7)); + + fmt++; + optr = &out[ocount++]; + if(ocount > MAX_SEGMENTS) + return PFMT_MANYSEGS; + optr->input = (unsigned int)param; + optr->flags = flags; + optr->width = width; + optr->precision = precision; + optr->start = start; + optr->outlen = outlen; + start = fmt; + } + else + fmt++; + } + + /* is there a trailing piece */ + outlen = (size_t)(fmt - start); + if(outlen) { + optr = &out[ocount++]; + if(ocount > MAX_SEGMENTS) + return PFMT_MANYSEGS; + optr->input = 0; + optr->flags = FLAGS_SUBSTR; + optr->start = start; + optr->outlen = outlen; + } + + /* Read the arg list parameters into our data list */ + for(i = 0; i < max_param + 1; i++) { + struct va_input *iptr = &in[i]; + if(!(usedinput[i/8] & (1 << (i&7)))) + /* bad input */ + return PFMT_INPUTGAP; + + /* based on the type, read the correct argument */ + switch(iptr->type) { + case FORMAT_STRING: + iptr->val.str = va_arg(arglist, char *); + break; + + case FORMAT_INTPTR: + case FORMAT_PTR: + iptr->val.ptr = va_arg(arglist, void *); + break; + + case FORMAT_LONGLONGU: + iptr->val.numu = (mp_uintmax_t)va_arg(arglist, mp_uintmax_t); + break; + + case FORMAT_LONGLONG: + iptr->val.nums = (mp_intmax_t)va_arg(arglist, mp_intmax_t); + break; + + case FORMAT_LONGU: + iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned long); + break; + + case FORMAT_LONG: + iptr->val.nums = (mp_intmax_t)va_arg(arglist, long); + break; + + case FORMAT_INTU: + iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned int); + break; + + case FORMAT_INT: + case FORMAT_WIDTH: + case FORMAT_PRECISION: + iptr->val.nums = (mp_intmax_t)va_arg(arglist, int); + break; + + case FORMAT_DOUBLE: + iptr->val.dnum = va_arg(arglist, double); + break; + + default: + DEBUGASSERT(NULL); /* unexpected */ + break; + } + } + *ipieces = max_param + 1; + *opieces = ocount; + + return PFMT_OK; +} + +/* + * formatf() - the general printf function. + * + * It calls parsefmt() to parse the format string. It populates two arrays; + * one that describes the input arguments and one that describes a number of + * output segments. + * + * On success, the input array describes the type of all arguments and their + * values. + * + * The function then iterates over the output segments and outputs them one + * by one until done. Using the appropriate input arguments (if any). + * + * All output is sent to the 'stream()' callback, one byte at a time. + */ + +static int formatf( + void *userp, /* untouched by format(), just sent to the stream() function in + the second argument */ + /* function pointer called for each output character */ + int (*stream)(unsigned char, void *), + const char *format, /* %-formatted string */ + va_list ap_save) /* list of parameters */ +{ + static const char nilstr[] = "(nil)"; + const char *digits = lower_digits; /* Base-36 digits for numbers. */ + int done = 0; /* number of characters written */ + int i; + int ocount = 0; /* number of output segments */ + int icount = 0; /* number of input arguments */ + + struct outsegment output[MAX_SEGMENTS]; + struct va_input input[MAX_PARAMETERS]; + char work[BUFFSIZE]; + + /* 'workend' points to the final buffer byte position, but with an extra + byte as margin to avoid the (false?) warning Coverity gives us + otherwise */ + char *workend = &work[sizeof(work) - 2]; + + /* Parse the format string */ + if(parsefmt(format, output, input, &ocount, &icount, ap_save)) + return 0; + + for(i = 0; i < ocount; i++) { + struct outsegment *optr = &output[i]; + struct va_input *iptr; + bool is_alt; /* Format spec modifiers. */ + int width; /* Width of a field. */ + int prec; /* Precision of a field. */ + bool is_neg; /* Decimal integer is negative. */ + unsigned long base; /* Base of a number to be written. */ + mp_uintmax_t num; /* Integral values to be written. */ + mp_intmax_t signed_num; /* Used to convert negative in positive. */ + char *w; + size_t outlen = optr->outlen; + unsigned int flags = optr->flags; + + if(outlen) { + char *str = optr->start; + for(; outlen && *str; outlen--) + OUTCHAR(*str++); + if(optr->flags & FLAGS_SUBSTR) + /* this is just a substring */ + continue; + } + + /* pick up the specified width */ + if(flags & FLAGS_WIDTHPARAM) { + width = (int)input[optr->width].val.nums; + if(width < 0) { + /* "A negative field width is taken as a '-' flag followed by a + positive field width." */ + if(width == INT_MIN) + width = INT_MAX; + else + width = -width; + flags |= FLAGS_LEFT; + flags &= ~(unsigned int)FLAGS_PAD_NIL; + } + } + else + width = optr->width; + + /* pick up the specified precision */ + if(flags & FLAGS_PRECPARAM) { + prec = (int)input[optr->precision].val.nums; + if(prec < 0) + /* "A negative precision is taken as if the precision were + omitted." */ + prec = -1; + } + else if(flags & FLAGS_PREC) + prec = optr->precision; + else + prec = -1; + + is_alt = (flags & FLAGS_ALT) ? 1 : 0; + iptr = &input[optr->input]; + + switch(iptr->type) { + case FORMAT_INTU: + case FORMAT_LONGU: + case FORMAT_LONGLONGU: + flags |= FLAGS_UNSIGNED; + FALLTHROUGH(); + case FORMAT_INT: + case FORMAT_LONG: + case FORMAT_LONGLONG: + num = iptr->val.numu; + if(flags & FLAGS_CHAR) { + /* Character. */ + if(!(flags & FLAGS_LEFT)) + while(--width > 0) + OUTCHAR(' '); + OUTCHAR((char) num); + if(flags & FLAGS_LEFT) + while(--width > 0) + OUTCHAR(' '); + break; + } + if(flags & FLAGS_OCTAL) { + /* Octal unsigned integer */ + base = 8; + is_neg = FALSE; + } + else if(flags & FLAGS_HEX) { + /* Hexadecimal unsigned integer */ + digits = (flags & FLAGS_UPPER)? upper_digits : lower_digits; + base = 16; + is_neg = FALSE; + } + else if(flags & FLAGS_UNSIGNED) { + /* Decimal unsigned integer */ + base = 10; + is_neg = FALSE; + } + else { + /* Decimal integer. */ + base = 10; + + is_neg = (iptr->val.nums < (mp_intmax_t)0); + if(is_neg) { + /* signed_num might fail to hold absolute negative minimum by 1 */ + signed_num = iptr->val.nums + (mp_intmax_t)1; + signed_num = -signed_num; + num = (mp_uintmax_t)signed_num; + num += (mp_uintmax_t)1; + } + } +number: + /* Supply a default precision if none was given. */ + if(prec == -1) + prec = 1; + + /* Put the number in WORK. */ + w = workend; + switch(base) { + case 10: + while(num > 0) { + *w-- = (char)('0' + (num % 10)); + num /= 10; + } + break; + default: + while(num > 0) { + *w-- = digits[num % base]; + num /= base; + } + break; + } + width -= (int)(workend - w); + prec -= (int)(workend - w); + + if(is_alt && base == 8 && prec <= 0) { + *w-- = '0'; + --width; + } + + if(prec > 0) { + width -= prec; + while(prec-- > 0 && w >= work) + *w-- = '0'; + } + + if(is_alt && base == 16) + width -= 2; + + if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE)) + --width; + + if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL)) + while(width-- > 0) + OUTCHAR(' '); + + if(is_neg) + OUTCHAR('-'); + else if(flags & FLAGS_SHOWSIGN) + OUTCHAR('+'); + else if(flags & FLAGS_SPACE) + OUTCHAR(' '); + + if(is_alt && base == 16) { + OUTCHAR('0'); + if(flags & FLAGS_UPPER) + OUTCHAR('X'); + else + OUTCHAR('x'); + } + + if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL)) + while(width-- > 0) + OUTCHAR('0'); + + /* Write the number. */ + while(++w <= workend) { + OUTCHAR(*w); + } + + if(flags & FLAGS_LEFT) + while(width-- > 0) + OUTCHAR(' '); + break; + + case FORMAT_STRING: { + const char *str; + size_t len; + + str = (char *)iptr->val.str; + if(!str) { + /* Write null string if there is space. */ + if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) { + str = nilstr; + len = sizeof(nilstr) - 1; + /* Disable quotes around (nil) */ + flags &= ~(unsigned int)FLAGS_ALT; + } + else { + str = ""; + len = 0; + } + } + else if(prec != -1) + len = (size_t)prec; + else if(*str == '\0') + len = 0; + else + len = strlen(str); + + width -= (len > INT_MAX) ? INT_MAX : (int)len; + + if(flags & FLAGS_ALT) + OUTCHAR('"'); + + if(!(flags & FLAGS_LEFT)) + while(width-- > 0) + OUTCHAR(' '); + + for(; len && *str; len--) + OUTCHAR(*str++); + if(flags & FLAGS_LEFT) + while(width-- > 0) + OUTCHAR(' '); + + if(flags & FLAGS_ALT) + OUTCHAR('"'); + break; + } + + case FORMAT_PTR: + /* Generic pointer. */ + if(iptr->val.ptr) { + /* If the pointer is not NULL, write it as a %#x spec. */ + base = 16; + digits = (flags & FLAGS_UPPER)? upper_digits : lower_digits; + is_alt = TRUE; + num = (size_t) iptr->val.ptr; + is_neg = FALSE; + goto number; + } + else { + /* Write "(nil)" for a nil pointer. */ + const char *point; + + width -= (int)(sizeof(nilstr) - 1); + if(flags & FLAGS_LEFT) + while(width-- > 0) + OUTCHAR(' '); + for(point = nilstr; *point != '\0'; ++point) + OUTCHAR(*point); + if(!(flags & FLAGS_LEFT)) + while(width-- > 0) + OUTCHAR(' '); + } + break; + + case FORMAT_DOUBLE: { + char formatbuf[32]="%"; + char *fptr = &formatbuf[1]; + size_t left = sizeof(formatbuf)-strlen(formatbuf); + int len; + + if(flags & FLAGS_WIDTH) + width = optr->width; + + if(flags & FLAGS_PREC) + prec = optr->precision; + + if(flags & FLAGS_LEFT) + *fptr++ = '-'; + if(flags & FLAGS_SHOWSIGN) + *fptr++ = '+'; + if(flags & FLAGS_SPACE) + *fptr++ = ' '; + if(flags & FLAGS_ALT) + *fptr++ = '#'; + + *fptr = 0; + + if(width >= 0) { + size_t dlen; + if(width >= (int)sizeof(work)) + width = sizeof(work)-1; + /* RECURSIVE USAGE */ + dlen = (size_t)curl_msnprintf(fptr, left, "%d", width); + fptr += dlen; + left -= dlen; + } + if(prec >= 0) { + /* for each digit in the integer part, we can have one less + precision */ + size_t maxprec = sizeof(work) - 2; + double val = iptr->val.dnum; + if(width > 0 && prec <= width) + maxprec -= (size_t)width; + while(val >= 10.0) { + val /= 10; + maxprec--; + } + + if(prec > (int)maxprec) + prec = (int)maxprec-1; + if(prec < 0) + prec = 0; + /* RECURSIVE USAGE */ + len = curl_msnprintf(fptr, left, ".%d", prec); + fptr += len; + } + if(flags & FLAGS_LONG) + *fptr++ = 'l'; + + if(flags & FLAGS_FLOATE) + *fptr++ = (char)((flags & FLAGS_UPPER) ? 'E':'e'); + else if(flags & FLAGS_FLOATG) + *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g'); + else + *fptr++ = 'f'; + + *fptr = 0; /* and a final null-termination */ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif + /* NOTE NOTE NOTE!! Not all sprintf implementations return number of + output characters */ +#ifdef HAVE_SNPRINTF + (snprintf)(work, sizeof(work), formatbuf, iptr->val.dnum); +#else + (sprintf)(work, formatbuf, iptr->val.dnum); +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + DEBUGASSERT(strlen(work) <= sizeof(work)); + for(fptr = work; *fptr; fptr++) + OUTCHAR(*fptr); + break; + } + + case FORMAT_INTPTR: + /* Answer the count of characters written. */ +#ifdef HAVE_LONG_LONG_TYPE + if(flags & FLAGS_LONGLONG) + *(LONG_LONG_TYPE *) iptr->val.ptr = (LONG_LONG_TYPE)done; + else +#endif + if(flags & FLAGS_LONG) + *(long *) iptr->val.ptr = (long)done; + else if(!(flags & FLAGS_SHORT)) + *(int *) iptr->val.ptr = (int)done; + else + *(short *) iptr->val.ptr = (short)done; + break; + + default: + break; + } + } + return done; +} + +/* fputc() look-alike */ +static int addbyter(unsigned char outc, void *f) +{ + struct nsprintf *infop = f; + if(infop->length < infop->max) { + /* only do this if we have not reached max length yet */ + *infop->buffer++ = (char)outc; /* store */ + infop->length++; /* we are now one byte larger */ + return 0; /* fputc() returns like this on success */ + } + return 1; +} + +int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, + va_list ap_save) +{ + int retcode; + struct nsprintf info; + + info.buffer = buffer; + info.length = 0; + info.max = maxlength; + + retcode = formatf(&info, addbyter, format, ap_save); + if(info.max) { + /* we terminate this with a zero byte */ + if(info.max == info.length) { + /* we are at maximum, scrap the last letter */ + info.buffer[-1] = 0; + DEBUGASSERT(retcode); + retcode--; /* do not count the nul byte */ + } + else + info.buffer[0] = 0; + } + return retcode; +} + +int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...) +{ + int retcode; + va_list ap_save; /* argument pointer */ + va_start(ap_save, format); + retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save); + va_end(ap_save); + return retcode; +} + +/* fputc() look-alike */ +static int alloc_addbyter(unsigned char outc, void *f) +{ + struct asprintf *infop = f; + CURLcode result = Curl_dyn_addn(infop->b, &outc, 1); + if(result) { + infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM; + return 1 ; /* fail */ + } + return 0; +} + +/* appends the formatted string, returns MERR error code */ +int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save) +{ + struct asprintf info; + info.b = dyn; + info.merr = MERR_OK; + + (void)formatf(&info, alloc_addbyter, format, ap_save); + if(info.merr) { + Curl_dyn_free(info.b); + return info.merr; + } + return 0; +} + +char *curl_mvaprintf(const char *format, va_list ap_save) +{ + struct asprintf info; + struct dynbuf dyn; + info.b = &dyn; + Curl_dyn_init(info.b, DYN_APRINTF); + info.merr = MERR_OK; + + (void)formatf(&info, alloc_addbyter, format, ap_save); + if(info.merr) { + Curl_dyn_free(info.b); + return NULL; + } + if(Curl_dyn_len(info.b)) + return Curl_dyn_ptr(info.b); + return strdup(""); +} + +char *curl_maprintf(const char *format, ...) +{ + va_list ap_save; + char *s; + va_start(ap_save, format); + s = curl_mvaprintf(format, ap_save); + va_end(ap_save); + return s; +} + +static int storebuffer(unsigned char outc, void *f) +{ + char **buffer = f; + **buffer = (char)outc; + (*buffer)++; + return 0; +} + +int curl_msprintf(char *buffer, const char *format, ...) +{ + va_list ap_save; /* argument pointer */ + int retcode; + va_start(ap_save, format); + retcode = formatf(&buffer, storebuffer, format, ap_save); + va_end(ap_save); + *buffer = 0; /* we terminate this with a zero byte */ + return retcode; +} + +static int fputc_wrapper(unsigned char outc, void *f) +{ + int out = outc; + FILE *s = f; + int rc = fputc(out, s); + return rc == EOF; +} + +int curl_mprintf(const char *format, ...) +{ + int retcode; + va_list ap_save; /* argument pointer */ + va_start(ap_save, format); + + retcode = formatf(stdout, fputc_wrapper, format, ap_save); + va_end(ap_save); + return retcode; +} + +int curl_mfprintf(FILE *whereto, const char *format, ...) +{ + int retcode; + va_list ap_save; /* argument pointer */ + va_start(ap_save, format); + retcode = formatf(whereto, fputc_wrapper, format, ap_save); + va_end(ap_save); + return retcode; +} + +int curl_mvsprintf(char *buffer, const char *format, va_list ap_save) +{ + int retcode = formatf(&buffer, storebuffer, format, ap_save); + *buffer = 0; /* we terminate this with a zero byte */ + return retcode; +} + +int curl_mvprintf(const char *format, va_list ap_save) +{ + return formatf(stdout, fputc_wrapper, format, ap_save); +} + +int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save) +{ + return formatf(whereto, fputc_wrapper, format, ap_save); +} diff --git a/third_party/curl/lib/mqtt.c b/third_party/curl/lib/mqtt.c new file mode 100644 index 0000000000..8c461e1630 --- /dev/null +++ b/third_party/curl/lib/mqtt.c @@ -0,0 +1,842 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Björn Stenberg, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_MQTT + +#include "urldata.h" +#include +#include "transfer.h" +#include "sendf.h" +#include "progress.h" +#include "mqtt.h" +#include "select.h" +#include "strdup.h" +#include "url.h" +#include "escape.h" +#include "warnless.h" +#include "curl_printf.h" +#include "curl_memory.h" +#include "multiif.h" +#include "rand.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +#define MQTT_MSG_CONNECT 0x10 +#define MQTT_MSG_CONNACK 0x20 +#define MQTT_MSG_PUBLISH 0x30 +#define MQTT_MSG_SUBSCRIBE 0x82 +#define MQTT_MSG_SUBACK 0x90 +#define MQTT_MSG_DISCONNECT 0xe0 + +#define MQTT_CONNACK_LEN 2 +#define MQTT_SUBACK_LEN 3 +#define MQTT_CLIENTID_LEN 12 /* "curl0123abcd" */ + +/* + * Forward declarations. + */ + +static CURLcode mqtt_do(struct Curl_easy *data, bool *done); +static CURLcode mqtt_done(struct Curl_easy *data, + CURLcode status, bool premature); +static CURLcode mqtt_doing(struct Curl_easy *data, bool *done); +static int mqtt_getsock(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t *sock); +static CURLcode mqtt_setup_conn(struct Curl_easy *data, + struct connectdata *conn); + +/* + * MQTT protocol handler. + */ + +const struct Curl_handler Curl_handler_mqtt = { + "mqtt", /* scheme */ + mqtt_setup_conn, /* setup_connection */ + mqtt_do, /* do_it */ + mqtt_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + mqtt_doing, /* doing */ + ZERO_NULL, /* proto_getsock */ + mqtt_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_MQTT, /* defport */ + CURLPROTO_MQTT, /* protocol */ + CURLPROTO_MQTT, /* family */ + PROTOPT_NONE /* flags */ +}; + +static CURLcode mqtt_setup_conn(struct Curl_easy *data, + struct connectdata *conn) +{ + /* allocate the HTTP-specific struct for the Curl_easy, only to survive + during this request */ + struct MQTT *mq; + (void)conn; + DEBUGASSERT(data->req.p.mqtt == NULL); + + mq = calloc(1, sizeof(struct MQTT)); + if(!mq) + return CURLE_OUT_OF_MEMORY; + Curl_dyn_init(&mq->recvbuf, DYN_MQTT_RECV); + data->req.p.mqtt = mq; + return CURLE_OK; +} + +static CURLcode mqtt_send(struct Curl_easy *data, + char *buf, size_t len) +{ + CURLcode result = CURLE_OK; + struct MQTT *mq = data->req.p.mqtt; + size_t n; + result = Curl_xfer_send(data, buf, len, &n); + if(result) + return result; + Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); + if(len != n) { + size_t nsend = len - n; + char *sendleftovers = Curl_memdup(&buf[n], nsend); + if(!sendleftovers) + return CURLE_OUT_OF_MEMORY; + mq->sendleftovers = sendleftovers; + mq->nsend = nsend; + } + else { + mq->sendleftovers = NULL; + mq->nsend = 0; + } + return result; +} + +/* Generic function called by the multi interface to figure out what socket(s) + to wait for and for what actions during the DOING and PROTOCONNECT + states */ +static int mqtt_getsock(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *sock) +{ + (void)data; + sock[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_READSOCK(FIRSTSOCKET); +} + +static int mqtt_encode_len(char *buf, size_t len) +{ + int i; + + for(i = 0; (len > 0) && (i<4); i++) { + unsigned char encoded; + encoded = len % 0x80; + len /= 0x80; + if(len) + encoded |= 0x80; + buf[i] = (char)encoded; + } + + return i; +} + +/* add the passwd to the CONNECT packet */ +static int add_passwd(const char *passwd, const size_t plen, + char *pkt, const size_t start, int remain_pos) +{ + /* magic number that need to be set properly */ + const size_t conn_flags_pos = remain_pos + 8; + if(plen > 0xffff) + return 1; + + /* set password flag */ + pkt[conn_flags_pos] |= 0x40; + + /* length of password provided */ + pkt[start] = (char)((plen >> 8) & 0xFF); + pkt[start + 1] = (char)(plen & 0xFF); + memcpy(&pkt[start + 2], passwd, plen); + return 0; +} + +/* add user to the CONNECT packet */ +static int add_user(const char *username, const size_t ulen, + unsigned char *pkt, const size_t start, int remain_pos) +{ + /* magic number that need to be set properly */ + const size_t conn_flags_pos = remain_pos + 8; + if(ulen > 0xffff) + return 1; + + /* set username flag */ + pkt[conn_flags_pos] |= 0x80; + /* length of username provided */ + pkt[start] = (unsigned char)((ulen >> 8) & 0xFF); + pkt[start + 1] = (unsigned char)(ulen & 0xFF); + memcpy(&pkt[start + 2], username, ulen); + return 0; +} + +/* add client ID to the CONNECT packet */ +static int add_client_id(const char *client_id, const size_t client_id_len, + char *pkt, const size_t start) +{ + if(client_id_len != MQTT_CLIENTID_LEN) + return 1; + pkt[start] = 0x00; + pkt[start + 1] = MQTT_CLIENTID_LEN; + memcpy(&pkt[start + 2], client_id, MQTT_CLIENTID_LEN); + return 0; +} + +/* Set initial values of CONNECT packet */ +static int init_connpack(char *packet, char *remain, int remain_pos) +{ + /* Fixed header starts */ + /* packet type */ + packet[0] = MQTT_MSG_CONNECT; + /* remaining length field */ + memcpy(&packet[1], remain, remain_pos); + /* Fixed header ends */ + + /* Variable header starts */ + /* protocol length */ + packet[remain_pos + 1] = 0x00; + packet[remain_pos + 2] = 0x04; + /* protocol name */ + packet[remain_pos + 3] = 'M'; + packet[remain_pos + 4] = 'Q'; + packet[remain_pos + 5] = 'T'; + packet[remain_pos + 6] = 'T'; + /* protocol level */ + packet[remain_pos + 7] = 0x04; + /* CONNECT flag: CleanSession */ + packet[remain_pos + 8] = 0x02; + /* keep-alive 0 = disabled */ + packet[remain_pos + 9] = 0x00; + packet[remain_pos + 10] = 0x3c; + /* end of variable header */ + return remain_pos + 10; +} + +static CURLcode mqtt_connect(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + int pos = 0; + int rc = 0; + /* remain length */ + int remain_pos = 0; + char remain[4] = {0}; + size_t packetlen = 0; + size_t payloadlen = 0; + size_t start_user = 0; + size_t start_pwd = 0; + char client_id[MQTT_CLIENTID_LEN + 1] = "curl"; + const size_t clen = strlen("curl"); + char *packet = NULL; + + /* extracting username from request */ + const char *username = data->state.aptr.user ? + data->state.aptr.user : ""; + const size_t ulen = strlen(username); + /* extracting password from request */ + const char *passwd = data->state.aptr.passwd ? + data->state.aptr.passwd : ""; + const size_t plen = strlen(passwd); + + payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2; + /* The plus 2 are for the MSB and LSB describing the length of the string to + * be added on the payload. Refer to spec 1.5.2 and 1.5.4 */ + if(ulen) + payloadlen += 2; + if(plen) + payloadlen += 2; + + /* getting how much occupy the remain length */ + remain_pos = mqtt_encode_len(remain, payloadlen + 10); + + /* 10 length of variable header and 1 the first byte of the fixed header */ + packetlen = payloadlen + 10 + remain_pos + 1; + + /* allocating packet */ + if(packetlen > 268435455) + return CURLE_WEIRD_SERVER_REPLY; + packet = malloc(packetlen); + if(!packet) + return CURLE_OUT_OF_MEMORY; + memset(packet, 0, packetlen); + + /* set initial values for the CONNECT packet */ + pos = init_connpack(packet, remain, remain_pos); + + result = Curl_rand_alnum(data, (unsigned char *)&client_id[clen], + MQTT_CLIENTID_LEN - clen + 1); + /* add client id */ + rc = add_client_id(client_id, strlen(client_id), packet, pos + 1); + if(rc) { + failf(data, "Client ID length mismatched: [%zu]", strlen(client_id)); + result = CURLE_WEIRD_SERVER_REPLY; + goto end; + } + infof(data, "Using client id '%s'", client_id); + + /* position where starts the user payload */ + start_user = pos + 3 + MQTT_CLIENTID_LEN; + /* position where starts the password payload */ + start_pwd = start_user + ulen; + /* if username was provided, add it to the packet */ + if(ulen) { + start_pwd += 2; + + rc = add_user(username, ulen, + (unsigned char *)packet, start_user, remain_pos); + if(rc) { + failf(data, "Username is too large: [%zu]", ulen); + result = CURLE_WEIRD_SERVER_REPLY; + goto end; + } + } + + /* if passwd was provided, add it to the packet */ + if(plen) { + rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos); + if(rc) { + failf(data, "Password is too large: [%zu]", plen); + result = CURLE_WEIRD_SERVER_REPLY; + goto end; + } + } + + if(!result) + result = mqtt_send(data, packet, packetlen); + +end: + if(packet) + free(packet); + Curl_safefree(data->state.aptr.user); + Curl_safefree(data->state.aptr.passwd); + return result; +} + +static CURLcode mqtt_disconnect(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct MQTT *mq = data->req.p.mqtt; + result = mqtt_send(data, (char *)"\xe0\x00", 2); + Curl_safefree(mq->sendleftovers); + Curl_dyn_free(&mq->recvbuf); + return result; +} + +static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes) +{ + struct MQTT *mq = data->req.p.mqtt; + size_t rlen = Curl_dyn_len(&mq->recvbuf); + CURLcode result; + + if(rlen < nbytes) { + unsigned char readbuf[1024]; + ssize_t nread; + + DEBUGASSERT(nbytes - rlen < sizeof(readbuf)); + result = Curl_xfer_recv(data, (char *)readbuf, nbytes - rlen, &nread); + if(result) + return result; + DEBUGASSERT(nread >= 0); + if(Curl_dyn_addn(&mq->recvbuf, readbuf, (size_t)nread)) + return CURLE_OUT_OF_MEMORY; + rlen = Curl_dyn_len(&mq->recvbuf); + } + return (rlen >= nbytes)? CURLE_OK : CURLE_AGAIN; +} + +static void mqtt_recv_consume(struct Curl_easy *data, size_t nbytes) +{ + struct MQTT *mq = data->req.p.mqtt; + size_t rlen = Curl_dyn_len(&mq->recvbuf); + if(rlen <= nbytes) + Curl_dyn_reset(&mq->recvbuf); + else + Curl_dyn_tail(&mq->recvbuf, rlen - nbytes); +} + +static CURLcode mqtt_verify_connack(struct Curl_easy *data) +{ + struct MQTT *mq = data->req.p.mqtt; + CURLcode result; + char *ptr; + + result = mqtt_recv_atleast(data, MQTT_CONNACK_LEN); + if(result) + goto fail; + + /* verify CONNACK */ + DEBUGASSERT(Curl_dyn_len(&mq->recvbuf) >= MQTT_CONNACK_LEN); + ptr = Curl_dyn_ptr(&mq->recvbuf); + Curl_debug(data, CURLINFO_HEADER_IN, ptr, MQTT_CONNACK_LEN); + + if(ptr[0] != 0x00 || ptr[1] != 0x00) { + failf(data, "Expected %02x%02x but got %02x%02x", + 0x00, 0x00, ptr[0], ptr[1]); + Curl_dyn_reset(&mq->recvbuf); + result = CURLE_WEIRD_SERVER_REPLY; + goto fail; + } + mqtt_recv_consume(data, MQTT_CONNACK_LEN); +fail: + return result; +} + +static CURLcode mqtt_get_topic(struct Curl_easy *data, + char **topic, size_t *topiclen) +{ + char *path = data->state.up.path; + CURLcode result = CURLE_URL_MALFORMAT; + if(strlen(path) > 1) { + result = Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_NADA); + if(!result && (*topiclen > 0xffff)) { + failf(data, "Too long MQTT topic"); + result = CURLE_URL_MALFORMAT; + } + } + else + failf(data, "No MQTT topic found. Forgot to URL encode it?"); + + return result; +} + +static CURLcode mqtt_subscribe(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + char *topic = NULL; + size_t topiclen; + unsigned char *packet = NULL; + size_t packetlen; + char encodedsize[4]; + size_t n; + struct connectdata *conn = data->conn; + + result = mqtt_get_topic(data, &topic, &topiclen); + if(result) + goto fail; + + conn->proto.mqtt.packetid++; + + packetlen = topiclen + 5; /* packetid + topic (has a two byte length field) + + 2 bytes topic length + QoS byte */ + n = mqtt_encode_len((char *)encodedsize, packetlen); + packetlen += n + 1; /* add one for the control packet type byte */ + + packet = malloc(packetlen); + if(!packet) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + packet[0] = MQTT_MSG_SUBSCRIBE; + memcpy(&packet[1], encodedsize, n); + packet[1 + n] = (conn->proto.mqtt.packetid >> 8) & 0xff; + packet[2 + n] = conn->proto.mqtt.packetid & 0xff; + packet[3 + n] = (topiclen >> 8) & 0xff; + packet[4 + n ] = topiclen & 0xff; + memcpy(&packet[5 + n], topic, topiclen); + packet[5 + n + topiclen] = 0; /* QoS zero */ + + result = mqtt_send(data, (char *)packet, packetlen); + +fail: + free(topic); + free(packet); + return result; +} + +/* + * Called when the first byte was already read. + */ +static CURLcode mqtt_verify_suback(struct Curl_easy *data) +{ + struct MQTT *mq = data->req.p.mqtt; + struct connectdata *conn = data->conn; + struct mqtt_conn *mqtt = &conn->proto.mqtt; + CURLcode result; + char *ptr; + + result = mqtt_recv_atleast(data, MQTT_SUBACK_LEN); + if(result) + goto fail; + + /* verify SUBACK */ + DEBUGASSERT(Curl_dyn_len(&mq->recvbuf) >= MQTT_SUBACK_LEN); + ptr = Curl_dyn_ptr(&mq->recvbuf); + Curl_debug(data, CURLINFO_HEADER_IN, ptr, MQTT_SUBACK_LEN); + + if(((unsigned char)ptr[0]) != ((mqtt->packetid >> 8) & 0xff) || + ((unsigned char)ptr[1]) != (mqtt->packetid & 0xff) || + ptr[2] != 0x00) { + Curl_dyn_reset(&mq->recvbuf); + result = CURLE_WEIRD_SERVER_REPLY; + goto fail; + } + mqtt_recv_consume(data, MQTT_SUBACK_LEN); +fail: + return result; +} + +static CURLcode mqtt_publish(struct Curl_easy *data) +{ + CURLcode result; + char *payload = data->set.postfields; + size_t payloadlen; + char *topic = NULL; + size_t topiclen; + unsigned char *pkt = NULL; + size_t i = 0; + size_t remaininglength; + size_t encodelen; + char encodedbytes[4]; + curl_off_t postfieldsize = data->set.postfieldsize; + + if(!payload) { + DEBUGF(infof(data, "mqtt_publish without payload, return bad arg")); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if(postfieldsize < 0) + payloadlen = strlen(payload); + else + payloadlen = (size_t)postfieldsize; + + result = mqtt_get_topic(data, &topic, &topiclen); + if(result) + goto fail; + + remaininglength = payloadlen + 2 + topiclen; + encodelen = mqtt_encode_len(encodedbytes, remaininglength); + + /* add the control byte and the encoded remaining length */ + pkt = malloc(remaininglength + 1 + encodelen); + if(!pkt) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + /* assemble packet */ + pkt[i++] = MQTT_MSG_PUBLISH; + memcpy(&pkt[i], encodedbytes, encodelen); + i += encodelen; + pkt[i++] = (topiclen >> 8) & 0xff; + pkt[i++] = (topiclen & 0xff); + memcpy(&pkt[i], topic, topiclen); + i += topiclen; + memcpy(&pkt[i], payload, payloadlen); + i += payloadlen; + result = mqtt_send(data, (char *)pkt, i); + +fail: + free(pkt); + free(topic); + return result; +} + +static size_t mqtt_decode_len(unsigned char *buf, + size_t buflen, size_t *lenbytes) +{ + size_t len = 0; + size_t mult = 1; + size_t i; + unsigned char encoded = 128; + + for(i = 0; (i < buflen) && (encoded & 128); i++) { + encoded = buf[i]; + len += (encoded & 127) * mult; + mult *= 128; + } + + if(lenbytes) + *lenbytes = i; + + return len; +} + +#ifdef DEBUGBUILD +static const char *statenames[]={ + "MQTT_FIRST", + "MQTT_REMAINING_LENGTH", + "MQTT_CONNACK", + "MQTT_SUBACK", + "MQTT_SUBACK_COMING", + "MQTT_PUBWAIT", + "MQTT_PUB_REMAIN", + + "NOT A STATE" +}; +#endif + +/* The only way to change state */ +static void mqstate(struct Curl_easy *data, + enum mqttstate state, + enum mqttstate nextstate) /* used if state == FIRST */ +{ + struct connectdata *conn = data->conn; + struct mqtt_conn *mqtt = &conn->proto.mqtt; +#ifdef DEBUGBUILD + infof(data, "%s (from %s) (next is %s)", + statenames[state], + statenames[mqtt->state], + (state == MQTT_FIRST)? statenames[nextstate] : ""); +#endif + mqtt->state = state; + if(state == MQTT_FIRST) + mqtt->nextstate = nextstate; +} + + +static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + ssize_t nread; + size_t remlen; + struct mqtt_conn *mqtt = &conn->proto.mqtt; + struct MQTT *mq = data->req.p.mqtt; + unsigned char packet; + + switch(mqtt->state) { +MQTT_SUBACK_COMING: + case MQTT_SUBACK_COMING: + result = mqtt_verify_suback(data); + if(result) + break; + + mqstate(data, MQTT_FIRST, MQTT_PUBWAIT); + break; + + case MQTT_SUBACK: + case MQTT_PUBWAIT: + /* we are expecting PUBLISH or SUBACK */ + packet = mq->firstbyte & 0xf0; + if(packet == MQTT_MSG_PUBLISH) + mqstate(data, MQTT_PUB_REMAIN, MQTT_NOSTATE); + else if(packet == MQTT_MSG_SUBACK) { + mqstate(data, MQTT_SUBACK_COMING, MQTT_NOSTATE); + goto MQTT_SUBACK_COMING; + } + else if(packet == MQTT_MSG_DISCONNECT) { + infof(data, "Got DISCONNECT"); + *done = TRUE; + goto end; + } + else { + result = CURLE_WEIRD_SERVER_REPLY; + goto end; + } + + /* -- switched state -- */ + remlen = mq->remaining_length; + infof(data, "Remaining length: %zu bytes", remlen); + if(data->set.max_filesize && + (curl_off_t)remlen > data->set.max_filesize) { + failf(data, "Maximum file size exceeded"); + result = CURLE_FILESIZE_EXCEEDED; + goto end; + } + Curl_pgrsSetDownloadSize(data, remlen); + data->req.bytecount = 0; + data->req.size = remlen; + mq->npacket = remlen; /* get this many bytes */ + FALLTHROUGH(); + case MQTT_PUB_REMAIN: { + /* read rest of packet, but no more. Cap to buffer size */ + char buffer[4*1024]; + size_t rest = mq->npacket; + if(rest > sizeof(buffer)) + rest = sizeof(buffer); + result = Curl_xfer_recv(data, buffer, rest, &nread); + if(result) { + if(CURLE_AGAIN == result) { + infof(data, "EEEE AAAAGAIN"); + } + goto end; + } + if(!nread) { + infof(data, "server disconnected"); + result = CURLE_PARTIAL_FILE; + goto end; + } + + /* if QoS is set, message contains packet id */ + result = Curl_client_write(data, CLIENTWRITE_BODY, buffer, nread); + if(result) + goto end; + + mq->npacket -= nread; + if(!mq->npacket) + /* no more PUBLISH payload, back to subscribe wait state */ + mqstate(data, MQTT_FIRST, MQTT_PUBWAIT); + break; + } + default: + DEBUGASSERT(NULL); /* illegal state */ + result = CURLE_WEIRD_SERVER_REPLY; + goto end; + } +end: + return result; +} + +static CURLcode mqtt_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + *done = FALSE; /* unconditionally */ + + result = mqtt_connect(data); + if(result) { + failf(data, "Error %d sending MQTT CONNECT request", result); + return result; + } + mqstate(data, MQTT_FIRST, MQTT_CONNACK); + return CURLE_OK; +} + +static CURLcode mqtt_done(struct Curl_easy *data, + CURLcode status, bool premature) +{ + struct MQTT *mq = data->req.p.mqtt; + (void)status; + (void)premature; + Curl_safefree(mq->sendleftovers); + Curl_dyn_free(&mq->recvbuf); + return CURLE_OK; +} + +static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct mqtt_conn *mqtt = &conn->proto.mqtt; + struct MQTT *mq = data->req.p.mqtt; + ssize_t nread; + unsigned char recvbyte; + + *done = FALSE; + + if(mq->nsend) { + /* send the remainder of an outgoing packet */ + char *ptr = mq->sendleftovers; + result = mqtt_send(data, mq->sendleftovers, mq->nsend); + free(ptr); + if(result) + return result; + } + + infof(data, "mqtt_doing: state [%d]", (int) mqtt->state); + switch(mqtt->state) { + case MQTT_FIRST: + /* Read the initial byte only */ + result = Curl_xfer_recv(data, (char *)&mq->firstbyte, 1, &nread); + if(result) + break; + else if(!nread) { + failf(data, "Connection disconnected"); + *done = TRUE; + result = CURLE_RECV_ERROR; + break; + } + Curl_debug(data, CURLINFO_HEADER_IN, (char *)&mq->firstbyte, 1); + /* remember the first byte */ + mq->npacket = 0; + mqstate(data, MQTT_REMAINING_LENGTH, MQTT_NOSTATE); + FALLTHROUGH(); + case MQTT_REMAINING_LENGTH: + do { + result = Curl_xfer_recv(data, (char *)&recvbyte, 1, &nread); + if(result || !nread) + break; + Curl_debug(data, CURLINFO_HEADER_IN, (char *)&recvbyte, 1); + mq->pkt_hd[mq->npacket++] = recvbyte; + } while((recvbyte & 0x80) && (mq->npacket < 4)); + if(!result && nread && (recvbyte & 0x80)) + /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 + + 127 * 128^3 bytes. server tried to send more */ + result = CURLE_WEIRD_SERVER_REPLY; + if(result) + break; + mq->remaining_length = mqtt_decode_len(mq->pkt_hd, mq->npacket, NULL); + mq->npacket = 0; + if(mq->remaining_length) { + mqstate(data, mqtt->nextstate, MQTT_NOSTATE); + break; + } + mqstate(data, MQTT_FIRST, MQTT_FIRST); + + if(mq->firstbyte == MQTT_MSG_DISCONNECT) { + infof(data, "Got DISCONNECT"); + *done = TRUE; + } + break; + case MQTT_CONNACK: + result = mqtt_verify_connack(data); + if(result) + break; + + if(data->state.httpreq == HTTPREQ_POST) { + result = mqtt_publish(data); + if(!result) { + result = mqtt_disconnect(data); + *done = TRUE; + } + mqtt->nextstate = MQTT_FIRST; + } + else { + result = mqtt_subscribe(data); + if(!result) { + mqstate(data, MQTT_FIRST, MQTT_SUBACK); + } + } + break; + + case MQTT_SUBACK: + case MQTT_PUBWAIT: + case MQTT_PUB_REMAIN: + result = mqtt_read_publish(data, done); + break; + + default: + failf(data, "State not handled yet"); + *done = TRUE; + break; + } + + if(result == CURLE_AGAIN) + result = CURLE_OK; + return result; +} + +#endif /* CURL_DISABLE_MQTT */ diff --git a/third_party/curl/lib/mqtt.h b/third_party/curl/lib/mqtt.h new file mode 100644 index 0000000000..99ab12a98a --- /dev/null +++ b/third_party/curl/lib/mqtt.h @@ -0,0 +1,63 @@ +#ifndef HEADER_CURL_MQTT_H +#define HEADER_CURL_MQTT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Björn Stenberg, + * + * 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 + * + ***************************************************************************/ + +#ifndef CURL_DISABLE_MQTT +extern const struct Curl_handler Curl_handler_mqtt; +#endif + +enum mqttstate { + MQTT_FIRST, /* 0 */ + MQTT_REMAINING_LENGTH, /* 1 */ + MQTT_CONNACK, /* 2 */ + MQTT_SUBACK, /* 3 */ + MQTT_SUBACK_COMING, /* 4 - the SUBACK remainder */ + MQTT_PUBWAIT, /* 5 - wait for publish */ + MQTT_PUB_REMAIN, /* 6 - wait for the remainder of the publish */ + + MQTT_NOSTATE /* 7 - never used an actual state */ +}; + +struct mqtt_conn { + enum mqttstate state; + enum mqttstate nextstate; /* switch to this after remaining length is + done */ + unsigned int packetid; +}; + +/* protocol-specific transfer-related data */ +struct MQTT { + char *sendleftovers; + size_t nsend; /* size of sendleftovers */ + + /* when receiving */ + size_t npacket; /* byte counter */ + unsigned char firstbyte; + size_t remaining_length; + struct dynbuf recvbuf; + unsigned char pkt_hd[4]; /* for decoding the arriving packet length */ +}; + +#endif /* HEADER_CURL_MQTT_H */ diff --git a/third_party/curl/lib/multi.c b/third_party/curl/lib/multi.c new file mode 100644 index 0000000000..896f730490 --- /dev/null +++ b/third_party/curl/lib/multi.c @@ -0,0 +1,3947 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "urldata.h" +#include "transfer.h" +#include "url.h" +#include "cfilters.h" +#include "connect.h" +#include "progress.h" +#include "easyif.h" +#include "share.h" +#include "psl.h" +#include "multiif.h" +#include "sendf.h" +#include "timeval.h" +#include "http.h" +#include "select.h" +#include "warnless.h" +#include "speedcheck.h" +#include "conncache.h" +#include "multihandle.h" +#include "sigpipe.h" +#include "vtls/vtls.h" +#include "http_proxy.h" +#include "http2.h" +#include "socketpair.h" +#include "socks.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 + to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every + CURL handle takes 45-50 K memory, therefore this 3K are not significant. +*/ +#ifndef CURL_SOCKET_HASH_TABLE_SIZE +#define CURL_SOCKET_HASH_TABLE_SIZE 911 +#endif + +#ifndef CURL_CONNECTION_HASH_SIZE +#define CURL_CONNECTION_HASH_SIZE 97 +#endif + +#ifndef CURL_DNS_HASH_SIZE +#define CURL_DNS_HASH_SIZE 71 +#endif + +#define CURL_MULTI_HANDLE 0x000bab1e + +#ifdef DEBUGBUILD +/* On a debug build, we want to fail hard on multi handles that + * are not NULL, but no longer have the MAGIC touch. This gives + * us early warning on things only discovered by valgrind otherwise. */ +#define GOOD_MULTI_HANDLE(x) \ + (((x) && (x)->magic == CURL_MULTI_HANDLE)? TRUE: \ + (DEBUGASSERT(!(x)), FALSE)) +#else +#define GOOD_MULTI_HANDLE(x) \ + ((x) && (x)->magic == CURL_MULTI_HANDLE) +#endif + +static void move_pending_to_connect(struct Curl_multi *multi, + struct Curl_easy *data); +static CURLMcode singlesocket(struct Curl_multi *multi, + struct Curl_easy *data); +static CURLMcode add_next_timeout(struct curltime now, + struct Curl_multi *multi, + struct Curl_easy *d); +static CURLMcode multi_timeout(struct Curl_multi *multi, + long *timeout_ms); +static void process_pending_handles(struct Curl_multi *multi); +static void multi_xfer_bufs_free(struct Curl_multi *multi); + +#ifdef DEBUGBUILD +static const char * const multi_statename[]={ + "INIT", + "PENDING", + "SETUP", + "CONNECT", + "RESOLVING", + "CONNECTING", + "TUNNELING", + "PROTOCONNECT", + "PROTOCONNECTING", + "DO", + "DOING", + "DOING_MORE", + "DID", + "PERFORMING", + "RATELIMITING", + "DONE", + "COMPLETED", + "MSGSENT", +}; +#endif + +/* function pointer called once when switching TO a state */ +typedef void (*init_multistate_func)(struct Curl_easy *data); + +/* called in DID state, before PERFORMING state */ +static void before_perform(struct Curl_easy *data) +{ + data->req.chunk = FALSE; + Curl_pgrsTime(data, TIMER_PRETRANSFER); +} + +static void init_completed(struct Curl_easy *data) +{ + /* this is a completed transfer */ + + /* Important: reset the conn pointer so that we do not point to memory + that could be freed anytime */ + Curl_detach_connection(data); + Curl_expire_clear(data); /* stop all timers */ +} + +/* always use this function to change state, to make debugging easier */ +static void mstate(struct Curl_easy *data, CURLMstate state +#ifdef DEBUGBUILD + , int lineno +#endif +) +{ + CURLMstate oldstate = data->mstate; + static const init_multistate_func finit[MSTATE_LAST] = { + NULL, /* INIT */ + NULL, /* PENDING */ + NULL, /* SETUP */ + Curl_init_CONNECT, /* CONNECT */ + NULL, /* RESOLVING */ + NULL, /* CONNECTING */ + NULL, /* TUNNELING */ + NULL, /* PROTOCONNECT */ + NULL, /* PROTOCONNECTING */ + NULL, /* DO */ + NULL, /* DOING */ + NULL, /* DOING_MORE */ + before_perform, /* DID */ + NULL, /* PERFORMING */ + NULL, /* RATELIMITING */ + NULL, /* DONE */ + init_completed, /* COMPLETED */ + NULL /* MSGSENT */ + }; + +#if defined(DEBUGBUILD) && defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) lineno; +#endif + + if(oldstate == state) + /* do not bother when the new state is the same as the old state */ + return; + + data->mstate = state; + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + if(data->mstate >= MSTATE_PENDING && + data->mstate < MSTATE_COMPLETED) { + infof(data, + "STATE: %s => %s handle %p; line %d", + multi_statename[oldstate], multi_statename[data->mstate], + (void *)data, lineno); + } +#endif + + if(state == MSTATE_COMPLETED) { + /* changing to COMPLETED means there is one less easy handle 'alive' */ + DEBUGASSERT(data->multi->num_alive > 0); + data->multi->num_alive--; + if(!data->multi->num_alive) { + /* free the transfer buffer when we have no more active transfers */ + multi_xfer_bufs_free(data->multi); + } + } + + /* if this state has an init-function, run it */ + if(finit[state]) + finit[state](data); +} + +#ifndef DEBUGBUILD +#define multistate(x,y) mstate(x,y) +#else +#define multistate(x,y) mstate(x,y, __LINE__) +#endif + +/* + * We add one of these structs to the sockhash for each socket + */ + +struct Curl_sh_entry { + struct Curl_hash transfers; /* hash of transfers using this socket */ + unsigned int action; /* what combined action READ/WRITE this socket waits + for */ + unsigned int users; /* number of transfers using this */ + void *socketp; /* settable by users with curl_multi_assign() */ + unsigned int readers; /* this many transfers want to read */ + unsigned int writers; /* this many transfers want to write */ +}; + +/* look up a given socket in the socket hash, skip invalid sockets */ +static struct Curl_sh_entry *sh_getentry(struct Curl_hash *sh, + curl_socket_t s) +{ + if(s != CURL_SOCKET_BAD) { + /* only look for proper sockets */ + return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t)); + } + return NULL; +} + +#define TRHASH_SIZE 13 +static size_t trhash(void *key, size_t key_length, size_t slots_num) +{ + size_t keyval = (size_t)*(struct Curl_easy **)key; + (void) key_length; + + return (keyval % slots_num); +} + +static size_t trhash_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) +{ + (void)k1_len; + (void)k2_len; + + return *(struct Curl_easy **)k1 == *(struct Curl_easy **)k2; +} + +static void trhash_dtor(void *nada) +{ + (void)nada; +} + +/* + * The sockhash has its own separate subhash in each entry that need to be + * safely destroyed first. + */ +static void sockhash_destroy(struct Curl_hash *h) +{ + struct Curl_hash_iterator iter; + struct Curl_hash_element *he; + + DEBUGASSERT(h); + Curl_hash_start_iterate(h, &iter); + he = Curl_hash_next_element(&iter); + while(he) { + struct Curl_sh_entry *sh = (struct Curl_sh_entry *)he->ptr; + Curl_hash_destroy(&sh->transfers); + he = Curl_hash_next_element(&iter); + } + Curl_hash_destroy(h); +} + + +/* make sure this socket is present in the hash for this handle */ +static struct Curl_sh_entry *sh_addentry(struct Curl_hash *sh, + curl_socket_t s) +{ + struct Curl_sh_entry *there = sh_getentry(sh, s); + struct Curl_sh_entry *check; + + if(there) { + /* it is present, return fine */ + return there; + } + + /* not present, add it */ + check = calloc(1, sizeof(struct Curl_sh_entry)); + if(!check) + return NULL; /* major failure */ + + Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash, trhash_compare, + trhash_dtor); + + /* make/add new hash entry */ + if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { + Curl_hash_destroy(&check->transfers); + free(check); + return NULL; /* major failure */ + } + + return check; /* things are good in sockhash land */ +} + + +/* delete the given socket + handle from the hash */ +static void sh_delentry(struct Curl_sh_entry *entry, + struct Curl_hash *sh, curl_socket_t s) +{ + Curl_hash_destroy(&entry->transfers); + + /* We remove the hash entry. This will end up in a call to + sh_freeentry(). */ + Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t)); +} + +/* + * free a sockhash entry + */ +static void sh_freeentry(void *freethis) +{ + struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis; + + free(p); +} + +static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) +{ + (void) k1_len; (void) k2_len; + + return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2)); +} + +static size_t hash_fd(void *key, size_t key_length, size_t slots_num) +{ + curl_socket_t fd = *((curl_socket_t *) key); + (void) key_length; + + return (fd % (curl_socket_t)slots_num); +} + +/* + * sh_init() creates a new socket hash and returns the handle for it. + * + * Quote from README.multi_socket: + * + * "Some tests at 7000 and 9000 connections showed that the socket hash lookup + * is somewhat of a bottle neck. Its current implementation may be a bit too + * limiting. It simply has a fixed-size array, and on each entry in the array + * it has a linked list with entries. The hash only checks which list to scan + * through. The code I had used so for used a list with merely 7 slots (as + * that is what the DNS hash uses) but with 7000 connections that would make + * an average of 1000 nodes in each list to run through. I upped that to 97 + * slots (I believe a prime is suitable) and noticed a significant speed + * increase. I need to reconsider the hash implementation or use a rather + * large default value like this. At 9000 connections I was still below 10us + * per call." + * + */ +static void sh_init(struct Curl_hash *hash, size_t hashsize) +{ + Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare, + sh_freeentry); +} + +/* multi->proto_hash destructor. Should never be called as elements + * MUST be added with their own destructor */ +static void ph_freeentry(void *p) +{ + (void)p; + /* Will always be FALSE. Cannot use a 0 assert here since compilers + * are not in agreement if they then want a NORETURN attribute or + * not. *sigh* */ + DEBUGASSERT(p == NULL); +} + +/* + * multi_addmsg() + * + * Called when a transfer is completed. Adds the given msg pointer to + * the list kept in the multi handle. + */ +static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg) +{ + Curl_llist_append(&multi->msglist, msg, &msg->list); +} + +struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */ + size_t chashsize, /* connection hash */ + size_t dnssize) /* dns hash */ +{ + struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); + + if(!multi) + return NULL; + + multi->magic = CURL_MULTI_HANDLE; + + Curl_init_dnscache(&multi->hostcache, dnssize); + + sh_init(&multi->sockhash, hashsize); + + Curl_hash_init(&multi->proto_hash, 23, + Curl_hash_str, Curl_str_key_compare, ph_freeentry); + + if(Curl_conncache_init(&multi->conn_cache, multi, chashsize)) + goto error; + + Curl_llist_init(&multi->msglist, NULL); + Curl_llist_init(&multi->pending, NULL); + Curl_llist_init(&multi->msgsent, NULL); + + multi->multiplexing = TRUE; + multi->max_concurrent_streams = 100; + +#ifdef USE_WINSOCK + multi->wsa_event = WSACreateEvent(); + if(multi->wsa_event == WSA_INVALID_EVENT) + goto error; +#else +#ifdef ENABLE_WAKEUP + if(wakeup_create(multi->wakeup_pair, TRUE) < 0) { + multi->wakeup_pair[0] = CURL_SOCKET_BAD; + multi->wakeup_pair[1] = CURL_SOCKET_BAD; + } +#endif +#endif + + return multi; + +error: + + sockhash_destroy(&multi->sockhash); + Curl_hash_destroy(&multi->proto_hash); + Curl_hash_destroy(&multi->hostcache); + Curl_conncache_destroy(&multi->conn_cache); + free(multi); + return NULL; +} + +struct Curl_multi *curl_multi_init(void) +{ + return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE, + CURL_CONNECTION_HASH_SIZE, + CURL_DNS_HASH_SIZE); +} + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data) +{ + if(!multi->warned) { + infof(data, "!!! WARNING !!!"); + infof(data, "This is a debug build of libcurl, " + "do not use in production."); + multi->warned = true; + } +} +#else +#define multi_warn_debug(x,y) Curl_nop_stmt +#endif + +/* returns TRUE if the easy handle is supposed to be present in the main link + list */ +static bool in_main_list(struct Curl_easy *data) +{ + return ((data->mstate != MSTATE_PENDING) && + (data->mstate != MSTATE_MSGSENT)); +} + +static void link_easy(struct Curl_multi *multi, + struct Curl_easy *data) +{ + /* We add the new easy entry last in the list. */ + data->next = NULL; /* end of the line */ + if(multi->easyp) { + struct Curl_easy *last = multi->easylp; + last->next = data; + data->prev = last; + multi->easylp = data; /* the new last node */ + } + else { + /* first node, make prev NULL! */ + data->prev = NULL; + multi->easylp = multi->easyp = data; /* both first and last */ + } +} + +/* unlink the given easy handle from the linked list of easy handles */ +static void unlink_easy(struct Curl_multi *multi, + struct Curl_easy *data) +{ + /* make the previous node point to our next */ + if(data->prev) + data->prev->next = data->next; + else + multi->easyp = data->next; /* point to first node */ + + /* make our next point to our previous node */ + if(data->next) + data->next->prev = data->prev; + else + multi->easylp = data->prev; /* point to last node */ + + data->prev = data->next = NULL; +} + + +CURLMcode curl_multi_add_handle(struct Curl_multi *multi, + struct Curl_easy *data) +{ + CURLMcode rc; + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Verify that we got a somewhat good easy handle too */ + if(!GOOD_EASY_HANDLE(data)) + return CURLM_BAD_EASY_HANDLE; + + /* Prevent users from adding same easy handle more than once and prevent + adding to more than one multi stack */ + if(data->multi) + return CURLM_ADDED_ALREADY; + + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + if(multi->dead) { + /* a "dead" handle cannot get added transfers while any existing easy + handles are still alive - but if there are none alive anymore, it is + fine to start over and unmark the "deadness" of this handle */ + if(multi->num_alive) + return CURLM_ABORTED_BY_CALLBACK; + multi->dead = FALSE; + } + + if(data->multi_easy) { + /* if this easy handle was previously used for curl_easy_perform(), there + is a private multi handle here that we can kill */ + curl_multi_cleanup(data->multi_easy); + data->multi_easy = NULL; + } + + /* Initialize timeout list for this handle */ + Curl_llist_init(&data->state.timeoutlist, NULL); + + /* + * No failure allowed in this function beyond this point. No modification of + * easy nor multi handle allowed before this except for potential multi's + * connection cache growing which will not be undone in this function no + * matter what. + */ + if(data->set.errorbuffer) + data->set.errorbuffer[0] = 0; + + data->state.os_errno = 0; + + /* make the Curl_easy refer back to this multi handle - before Curl_expire() + is called. */ + data->multi = multi; + + /* Set the timeout for this handle to expire really soon so that it will + be taken care of even when this handle is added in the midst of operation + when only the curl_multi_socket() API is used. During that flow, only + sockets that time-out or have actions will be dealt with. Since this + handle has no action yet, we make sure it times out to get things to + happen. */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + + /* A somewhat crude work-around for a little glitch in Curl_update_timer() + that happens if the lastcall time is set to the same time when the handle + is removed as when the next handle is added, as then the check in + Curl_update_timer() that prevents calling the application multiple times + with the same timer info will not trigger and then the new handle's + timeout will not be notified to the app. + + The work-around is thus simply to clear the 'lastcall' variable to force + Curl_update_timer() to always trigger a callback to the app when a new + easy handle is added */ + memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); + + rc = Curl_update_timer(multi); + if(rc) + return rc; + + /* set the easy handle */ + multistate(data, MSTATE_INIT); + + /* for multi interface connections, we share DNS cache automatically if the + easy handle's one is currently not set. */ + if(!data->dns.hostcache || + (data->dns.hostcachetype == HCACHE_NONE)) { + data->dns.hostcache = &multi->hostcache; + data->dns.hostcachetype = HCACHE_MULTI; + } + + /* Point to the shared or multi handle connection cache */ + if(data->share && (data->share->specifier & (1<< CURL_LOCK_DATA_CONNECT))) + data->state.conn_cache = &data->share->conn_cache; + else + data->state.conn_cache = &multi->conn_cache; + data->state.lastconnect_id = -1; + +#ifdef USE_LIBPSL + /* Do the same for PSL. */ + if(data->share && (data->share->specifier & (1 << CURL_LOCK_DATA_PSL))) + data->psl = &data->share->psl; + else + data->psl = &multi->psl; +#endif + + link_easy(multi, data); + + /* increase the node-counter */ + multi->num_easy++; + + /* increase the alive-counter */ + multi->num_alive++; + + CONNCACHE_LOCK(data); + /* The closure handle only ever has default timeouts set. To improve the + state somewhat we clone the timeouts from each added handle so that the + closure handle always has the same timeouts as the most recently added + easy handle. */ + data->state.conn_cache->closure_handle->set.timeout = data->set.timeout; + data->state.conn_cache->closure_handle->set.server_response_timeout = + data->set.server_response_timeout; + data->state.conn_cache->closure_handle->set.no_signal = + data->set.no_signal; + data->id = data->state.conn_cache->next_easy_id++; + if(data->state.conn_cache->next_easy_id <= 0) + data->state.conn_cache->next_easy_id = 0; + CONNCACHE_UNLOCK(data); + + multi_warn_debug(multi, data); + + return CURLM_OK; +} + +#if 0 +/* Debug-function, used like this: + * + * Curl_hash_print(&multi->sockhash, debug_print_sock_hash); + * + * Enable the hash print function first by editing hash.c + */ +static void debug_print_sock_hash(void *p) +{ + struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; + + fprintf(stderr, " [readers %u][writers %u]", + sh->readers, sh->writers); +} +#endif + +static CURLcode multi_done(struct Curl_easy *data, + CURLcode status, /* an error if this is called + after an error was detected */ + bool premature) +{ + CURLcode result, r2; + struct connectdata *conn = data->conn; + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + DEBUGF(infof(data, "multi_done[%s]: status: %d prem: %d done: %d", + multi_statename[data->mstate], + (int)status, (int)premature, data->state.done)); +#else + DEBUGF(infof(data, "multi_done: status: %d prem: %d done: %d", + (int)status, (int)premature, data->state.done)); +#endif + + if(data->state.done) + /* Stop if multi_done() has already been called */ + return CURLE_OK; + + /* Stop the resolver and free its own resources (but not dns_entry yet). */ + Curl_resolver_kill(data); + + /* Cleanup possible redirect junk */ + Curl_safefree(data->req.newurl); + Curl_safefree(data->req.location); + + switch(status) { + case CURLE_ABORTED_BY_CALLBACK: + case CURLE_READ_ERROR: + case CURLE_WRITE_ERROR: + /* When we are aborted due to a callback return code it basically have to + be counted as premature as there is trouble ahead if we do not. We have + many callbacks and protocols work differently, we could potentially do + this more fine-grained in the future. */ + premature = TRUE; + FALLTHROUGH(); + default: + break; + } + + /* this calls the protocol-specific function pointer previously set */ + if(conn->handler->done) + result = conn->handler->done(data, status, premature); + else + result = status; + + if(CURLE_ABORTED_BY_CALLBACK != result) { + /* avoid this if we already aborted by callback to avoid this calling + another callback */ + int rc = Curl_pgrsDone(data); + if(!result && rc) + result = CURLE_ABORTED_BY_CALLBACK; + } + + /* Make sure that transfer client writes are really done now. */ + r2 = Curl_xfer_write_done(data, premature); + if(r2 && !result) + result = r2; + + /* Inform connection filters that this transfer is done */ + Curl_conn_ev_data_done(data, premature); + + process_pending_handles(data->multi); /* connection / multiplex */ + + if(!result) + result = Curl_req_done(&data->req, data, premature); + + CONNCACHE_LOCK(data); + Curl_detach_connection(data); + if(CONN_INUSE(conn)) { + /* Stop if still used. */ + CONNCACHE_UNLOCK(data); + DEBUGF(infof(data, "Connection still in use %zu, " + "no more multi_done now!", + conn->easyq.size)); + return CURLE_OK; + } + + data->state.done = TRUE; /* called just now! */ + + if(conn->dns_entry) { + Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ + conn->dns_entry = NULL; + } + Curl_hostcache_prune(data); + + /* if data->set.reuse_forbid is TRUE, it means the libcurl client has + forced us to close this connection. This is ignored for requests taking + place in a NTLM/NEGOTIATE authentication handshake + + if conn->bits.close is TRUE, it means that the connection should be + closed in spite of all our efforts to be nice, due to protocol + restrictions in our or the server's end + + if premature is TRUE, it means this connection was said to be DONE before + the entire request operation is complete and thus we cannot know in what + state it is for reusing, so we are forced to close it. In a perfect world + we can add code that keep track of if we really must close it here or not, + but currently we have no such detail knowledge. + */ + + data->state.recent_conn_id = conn->connection_id; + if((data->set.reuse_forbid +#if defined(USE_NTLM) + && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 || + conn->proxy_ntlm_state == NTLMSTATE_TYPE2) +#endif +#if defined(USE_SPNEGO) + && !(conn->http_negotiate_state == GSS_AUTHRECV || + conn->proxy_negotiate_state == GSS_AUTHRECV) +#endif + ) || conn->bits.close + || (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) { + DEBUGF(infof(data, "multi_done, not reusing connection=%" + CURL_FORMAT_CURL_OFF_T ", forbid=%d" + ", close=%d, premature=%d, conn_multiplex=%d", + conn->connection_id, + data->set.reuse_forbid, conn->bits.close, premature, + Curl_conn_is_multiplex(conn, FIRSTSOCKET))); + connclose(conn, "disconnecting"); + Curl_conncache_remove_conn(data, conn, FALSE); + CONNCACHE_UNLOCK(data); + Curl_disconnect(data, conn, premature); + } + else { + char buffer[256]; + const char *host = +#ifndef CURL_DISABLE_PROXY + conn->bits.socksproxy ? + conn->socks_proxy.host.dispname : + conn->bits.httpproxy ? conn->http_proxy.host.dispname : +#endif + conn->bits.conn_to_host ? conn->conn_to_host.dispname : + conn->host.dispname; + /* create string before returning the connection */ + curl_off_t connection_id = conn->connection_id; + msnprintf(buffer, sizeof(buffer), + "Connection #%" CURL_FORMAT_CURL_OFF_T " to host %s left intact", + connection_id, host); + /* the connection is no longer in use by this transfer */ + CONNCACHE_UNLOCK(data); + if(Curl_conncache_return_conn(data, conn)) { + /* remember the most recently used connection */ + data->state.lastconnect_id = connection_id; + data->state.recent_conn_id = connection_id; + infof(data, "%s", buffer); + } + else + data->state.lastconnect_id = -1; + } + + return result; +} + +static int close_connect_only(struct Curl_easy *data, + struct connectdata *conn, void *param) +{ + (void)param; + if(data->state.lastconnect_id != conn->connection_id) + return 0; + + if(!conn->connect_only) + return 1; + + connclose(conn, "Removing connect-only easy handle"); + + return 1; +} + +CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, + struct Curl_easy *data) +{ + struct Curl_easy *easy = data; + bool premature; + struct Curl_llist_element *e; + CURLMcode rc; + + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Verify that we got a somewhat good easy handle too */ + if(!GOOD_EASY_HANDLE(data)) + return CURLM_BAD_EASY_HANDLE; + + /* Prevent users from trying to remove same easy handle more than once */ + if(!data->multi) + return CURLM_OK; /* it is already removed so let's say it is fine! */ + + /* Prevent users from trying to remove an easy handle from the wrong multi */ + if(data->multi != multi) + return CURLM_BAD_EASY_HANDLE; + + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + premature = (data->mstate < MSTATE_COMPLETED) ? TRUE : FALSE; + + /* If the 'state' is not INIT or COMPLETED, we might need to do something + nice to put the easy_handle in a good known state when this returns. */ + if(premature) { + /* this handle is "alive" so we need to count down the total number of + alive connections when this is removed */ + multi->num_alive--; + } + + if(data->conn && + data->mstate > MSTATE_DO && + data->mstate < MSTATE_COMPLETED) { + /* Set connection owner so that the DONE function closes it. We can + safely do this here since connection is killed. */ + streamclose(data->conn, "Removed with partial response"); + } + + if(data->conn) { + /* multi_done() clears the association between the easy handle and the + connection. + + Note that this ignores the return code simply because there is + nothing really useful to do with it anyway! */ + (void)multi_done(data, data->result, premature); + } + + /* The timer must be shut down before data->multi is set to NULL, else the + timenode will remain in the splay tree after curl_easy_cleanup is + called. Do it after multi_done() in case that sets another time! */ + Curl_expire_clear(data); + + if(data->connect_queue.ptr) { + /* the handle is in the pending or msgsent lists, so go ahead and remove + it */ + if(data->mstate == MSTATE_PENDING) + Curl_llist_remove(&multi->pending, &data->connect_queue, NULL); + else + Curl_llist_remove(&multi->msgsent, &data->connect_queue, NULL); + } + if(in_main_list(data)) + unlink_easy(multi, data); + + if(data->dns.hostcachetype == HCACHE_MULTI) { + /* stop using the multi handle's DNS cache, *after* the possible + multi_done() call above */ + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; + } + + Curl_wildcard_dtor(&data->wildcard); + + /* change state without using multistate(), only to make singlesocket() do + what we want */ + data->mstate = MSTATE_COMPLETED; + + /* This ignores the return code even in case of problems because there is + nothing more to do about that, here */ + (void)singlesocket(multi, easy); /* to let the application know what sockets + that vanish with this handle */ + + /* Remove the association between the connection and the handle */ + Curl_detach_connection(data); + + if(data->set.connect_only && !data->multi_easy) { + /* This removes a handle that was part the multi interface that used + CONNECT_ONLY, that connection is now left alive but since this handle + has bits.close set nothing can use that transfer anymore and it is + forbidden from reuse. This easy handle cannot find the connection + anymore once removed from the multi handle + + Better close the connection here, at once. + */ + struct connectdata *c; + curl_socket_t s; + s = Curl_getconnectinfo(data, &c); + if((s != CURL_SOCKET_BAD) && c) { + Curl_conncache_remove_conn(data, c, TRUE); + Curl_disconnect(data, c, TRUE); + } + } + + if(data->state.lastconnect_id != -1) { + /* Mark any connect-only connection for closure */ + Curl_conncache_foreach(data, data->state.conn_cache, + NULL, close_connect_only); + } + +#ifdef USE_LIBPSL + /* Remove the PSL association. */ + if(data->psl == &multi->psl) + data->psl = NULL; +#endif + + /* as this was using a shared connection cache we clear the pointer to that + since we are not part of that multi handle anymore */ + data->state.conn_cache = NULL; + + data->multi = NULL; /* clear the association to this multi handle */ + + /* make sure there is no pending message in the queue sent from this easy + handle */ + for(e = multi->msglist.head; e; e = e->next) { + struct Curl_message *msg = e->ptr; + + if(msg->extmsg.easy_handle == easy) { + Curl_llist_remove(&multi->msglist, e, NULL); + /* there can only be one from this specific handle */ + break; + } + } + + /* NOTE NOTE NOTE + We do not touch the easy handle here! */ + multi->num_easy--; /* one less to care about now */ + + process_pending_handles(multi); + + rc = Curl_update_timer(multi); + if(rc) + return rc; + return CURLM_OK; +} + +/* Return TRUE if the application asked for multiplexing */ +bool Curl_multiplex_wanted(const struct Curl_multi *multi) +{ + return (multi && (multi->multiplexing)); +} + +/* + * Curl_detach_connection() removes the given transfer from the connection. + * + * This is the only function that should clear data->conn. This will + * occasionally be called with the data->conn pointer already cleared. + */ +void Curl_detach_connection(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + if(conn) { + Curl_conn_ev_data_detach(conn, data); + Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); + } + data->conn = NULL; +} + +/* + * Curl_attach_connection() attaches this transfer to this connection. + * + * This is the only function that should assign data->conn + */ +void Curl_attach_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + DEBUGASSERT(data); + DEBUGASSERT(!data->conn); + DEBUGASSERT(conn); + data->conn = conn; + Curl_llist_append(&conn->easyq, data, &data->conn_queue); + if(conn->handler && conn->handler->attach) + conn->handler->attach(data, conn); + Curl_conn_ev_data_attach(conn, data); +} + +static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks) +{ + struct connectdata *conn = data->conn; + curl_socket_t sockfd; + + if(!conn) + return GETSOCK_BLANK; + sockfd = Curl_conn_get_socket(data, FIRSTSOCKET); + if(sockfd != CURL_SOCKET_BAD) { + /* Default is to wait to something from the server */ + socks[0] = sockfd; + return GETSOCK_READSOCK(0); + } + return GETSOCK_BLANK; +} + +static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks) +{ + struct connectdata *conn = data->conn; + curl_socket_t sockfd; + + if(!conn) + return GETSOCK_BLANK; + if(conn->handler->proto_getsock) + return conn->handler->proto_getsock(data, conn, socks); + sockfd = Curl_conn_get_socket(data, FIRSTSOCKET); + if(sockfd != CURL_SOCKET_BAD) { + /* Default is to wait to something from the server */ + socks[0] = sockfd; + return GETSOCK_READSOCK(0); + } + return GETSOCK_BLANK; +} + +static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks) +{ + struct connectdata *conn = data->conn; + if(!conn) + return GETSOCK_BLANK; + if(conn->handler->domore_getsock) + return conn->handler->domore_getsock(data, conn, socks); + else if(conn->sockfd != CURL_SOCKET_BAD) { + /* Default is that we want to send something to the server */ + socks[0] = conn->sockfd; + return GETSOCK_WRITESOCK(0); + } + return GETSOCK_BLANK; +} + +static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks) +{ + struct connectdata *conn = data->conn; + if(!conn) + return GETSOCK_BLANK; + if(conn->handler->doing_getsock) + return conn->handler->doing_getsock(data, conn, socks); + else if(conn->sockfd != CURL_SOCKET_BAD) { + /* Default is that we want to send something to the server */ + socks[0] = conn->sockfd; + return GETSOCK_WRITESOCK(0); + } + return GETSOCK_BLANK; +} + +static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock) +{ + struct connectdata *conn = data->conn; + if(!conn) + return GETSOCK_BLANK; + else if(conn->handler->perform_getsock) + return conn->handler->perform_getsock(data, conn, sock); + else { + /* Default is to obey the data->req.keepon flags for send/recv */ + int bitmap = GETSOCK_BLANK; + unsigned sockindex = 0; + if(CURL_WANT_RECV(data)) { + DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD); + bitmap |= GETSOCK_READSOCK(sockindex); + sock[sockindex] = conn->sockfd; + } + + if(CURL_WANT_SEND(data)) { + if((conn->sockfd != conn->writesockfd) || + bitmap == GETSOCK_BLANK) { + /* only if they are not the same socket and we have a readable + one, we increase index */ + if(bitmap != GETSOCK_BLANK) + sockindex++; /* increase index if we need two entries */ + + DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD); + sock[sockindex] = conn->writesockfd; + } + bitmap |= GETSOCK_WRITESOCK(sockindex); + } + return bitmap; + } +} + +/* Initializes `poll_set` with the current socket poll actions needed + * for transfer `data`. */ +static void multi_getsock(struct Curl_easy *data, + struct easy_pollset *ps) +{ + bool expect_sockets = TRUE; + /* The no connection case can happen when this is called from + curl_multi_remove_handle() => singlesocket() => multi_getsock(). + */ + Curl_pollset_reset(data, ps); + if(!data->conn) + return; + + switch(data->mstate) { + case MSTATE_INIT: + case MSTATE_PENDING: + case MSTATE_SETUP: + case MSTATE_CONNECT: + /* nothing to poll for yet */ + expect_sockets = FALSE; + break; + + case MSTATE_RESOLVING: + Curl_pollset_add_socks(data, ps, Curl_resolv_getsock); + /* connection filters are not involved in this phase. It's ok if we get no + * sockets to wait for. Resolving can wake up from other sources. */ + expect_sockets = FALSE; + break; + + case MSTATE_CONNECTING: + case MSTATE_TUNNELING: + Curl_pollset_add_socks(data, ps, connecting_getsock); + Curl_conn_adjust_pollset(data, ps); + break; + + case MSTATE_PROTOCONNECT: + case MSTATE_PROTOCONNECTING: + Curl_pollset_add_socks(data, ps, protocol_getsock); + Curl_conn_adjust_pollset(data, ps); + break; + + case MSTATE_DO: + case MSTATE_DOING: + Curl_pollset_add_socks(data, ps, doing_getsock); + Curl_conn_adjust_pollset(data, ps); + break; + + case MSTATE_DOING_MORE: + Curl_pollset_add_socks(data, ps, domore_getsock); + Curl_conn_adjust_pollset(data, ps); + break; + + case MSTATE_DID: /* same as PERFORMING in regard to polling */ + case MSTATE_PERFORMING: + Curl_pollset_add_socks(data, ps, perform_getsock); + Curl_conn_adjust_pollset(data, ps); + break; + + case MSTATE_RATELIMITING: + /* we need to let time pass, ignore socket(s) */ + expect_sockets = FALSE; + break; + + case MSTATE_DONE: + case MSTATE_COMPLETED: + case MSTATE_MSGSENT: + /* nothing more to poll for */ + expect_sockets = FALSE; + break; + + default: + failf(data, "multi_getsock: unexpected multi state %d", data->mstate); + DEBUGASSERT(0); + expect_sockets = FALSE; + break; + } + + if(expect_sockets && !ps->num && + !(data->req.keepon & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) && + Curl_conn_is_ip_connected(data, FIRSTSOCKET)) { + infof(data, "WARNING: no socket in pollset, transfer may stall!"); + DEBUGASSERT(0); + } +} + +CURLMcode curl_multi_fdset(struct Curl_multi *multi, + fd_set *read_fd_set, fd_set *write_fd_set, + fd_set *exc_fd_set, int *max_fd) +{ + /* Scan through all the easy handles to get the file descriptors set. + Some easy handles may not have connected to the remote host yet, + and then we must make sure that is done. */ + struct Curl_easy *data; + int this_max_fd = -1; + struct easy_pollset ps; + unsigned int i; + (void)exc_fd_set; /* not used */ + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + memset(&ps, 0, sizeof(ps)); + for(data = multi->easyp; data; data = data->next) { + multi_getsock(data, &ps); + + for(i = 0; i < ps.num; i++) { + if(!FDSET_SOCK(ps.sockets[i])) + /* pretend it does not exist */ + continue; + if(ps.actions[i] & CURL_POLL_IN) + FD_SET(ps.sockets[i], read_fd_set); + if(ps.actions[i] & CURL_POLL_OUT) + FD_SET(ps.sockets[i], write_fd_set); + if((int)ps.sockets[i] > this_max_fd) + this_max_fd = (int)ps.sockets[i]; + } + } + + *max_fd = this_max_fd; + + return CURLM_OK; +} + +CURLMcode curl_multi_waitfds(struct Curl_multi *multi, + struct curl_waitfd *ufds, + unsigned int size, + unsigned int *fd_count) +{ + struct Curl_easy *data; + struct curl_waitfds cwfds; + struct easy_pollset ps; + CURLMcode result = CURLM_OK; + + if(!ufds) + return CURLM_BAD_FUNCTION_ARGUMENT; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + Curl_waitfds_init(&cwfds, ufds, size); + memset(&ps, 0, sizeof(ps)); + for(data = multi->easyp; data; data = data->next) { + multi_getsock(data, &ps); + if(Curl_waitfds_add_ps(&cwfds, &ps)) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } + } + + if(Curl_conncache_add_waitfds(&multi->conn_cache, &cwfds)) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } + +out: + if(fd_count) + *fd_count = cwfds.n; + return result; +} + +#ifdef USE_WINSOCK +/* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets cannot + * be reset this way because an empty datagram would be sent. #9203 + * + * "On Windows the internal state of FD_WRITE as returned from + * WSAEnumNetworkEvents is only reset after successful send()." + */ +static void reset_socket_fdwrite(curl_socket_t s) +{ + int t; + int l = (int)sizeof(t); + if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM) + send(s, NULL, 0, 0); +} +#endif + +#define NUM_POLLS_ON_STACK 10 + +static CURLMcode multi_wait(struct Curl_multi *multi, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret, + bool extrawait, /* when no socket, wait */ + bool use_wakeup) +{ + struct Curl_easy *data; + struct easy_pollset ps; + size_t i; + long timeout_internal; + int retcode = 0; + struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; + struct curl_pollfds cpfds; + unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */ + CURLMcode result = CURLM_OK; +#ifdef USE_WINSOCK + WSANETWORKEVENTS wsa_events; + DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); +#endif +#ifndef ENABLE_WAKEUP + (void)use_wakeup; +#endif + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + if(timeout_ms < 0) + return CURLM_BAD_FUNCTION_ARGUMENT; + + Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK); + memset(&ps, 0, sizeof(ps)); + + /* Add the curl handles to our pollfds first */ + for(data = multi->easyp; data; data = data->next) { + multi_getsock(data, &ps); + if(Curl_pollfds_add_ps(&cpfds, &ps)) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } + } + + if(Curl_conncache_add_pollfds(&multi->conn_cache, &cpfds)) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } + + curl_nfds = cpfds.n; /* what curl internally uses in cpfds */ + /* Add external file descriptions from poll-like struct curl_waitfd */ + for(i = 0; i < extra_nfds; i++) { + unsigned short events = 0; + if(extra_fds[i].events & CURL_WAIT_POLLIN) + events |= POLLIN; + if(extra_fds[i].events & CURL_WAIT_POLLPRI) + events |= POLLPRI; + if(extra_fds[i].events & CURL_WAIT_POLLOUT) + events |= POLLOUT; + if(Curl_pollfds_add_sock(&cpfds, extra_fds[i].fd, events)) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } + } + +#ifdef USE_WINSOCK + /* Set the WSA events based on the collected pollds */ + for(i = 0; i < cpfds.n; i++) { + long mask = 0; + if(cpfds.pfds[i].events & POLLIN) + mask |= FD_READ|FD_ACCEPT|FD_CLOSE; + if(cpfds.pfds[i].events & POLLPRI) + mask |= FD_OOB; + if(cpfds.pfds[i].events & POLLOUT) { + mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; + reset_socket_fdwrite(cpfds.pfds[i].fd); + } + if(mask) { + if(WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, mask) != 0) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } + } + } +#endif + +#ifdef ENABLE_WAKEUP +#ifndef USE_WINSOCK + if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { + if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } + } +#endif +#endif + + /* We check the internal timeout *AFTER* we collected all sockets to + * poll. Collecting the sockets may install new timers by protocols + * and connection filters. + * Use the shorter one of the internal and the caller requested timeout. */ + (void)multi_timeout(multi, &timeout_internal); + if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) + timeout_ms = (int)timeout_internal; + +#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) + if(cpfds.n || use_wakeup) { +#else + if(cpfds.n) { +#endif + int pollrc; +#ifdef USE_WINSOCK + if(cpfds.n) /* just pre-check with WinSock */ + pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0); + else + pollrc = 0; +#else + pollrc = Curl_poll(cpfds.pfds, cpfds.n, timeout_ms); /* wait... */ +#endif + if(pollrc < 0) { + result = CURLM_UNRECOVERABLE_POLL; + goto out; + } + + if(pollrc > 0) { + retcode = pollrc; +#ifdef USE_WINSOCK + } + else { /* now wait... if not ready during the pre-check (pollrc == 0) */ + WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms, + FALSE); + } + /* With WinSock, we have to run the following section unconditionally + to call WSAEventSelect(fd, event, 0) on all the sockets */ + { +#endif + /* copy revents results from the poll to the curl_multi_wait poll + struct, the bit values of the actual underlying poll() implementation + may not be the same as the ones in the public libcurl API! */ + for(i = 0; i < extra_nfds; i++) { + unsigned r = (unsigned)cpfds.pfds[curl_nfds + i].revents; + unsigned short mask = 0; +#ifdef USE_WINSOCK + curl_socket_t s = extra_fds[i].fd; + wsa_events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) { + if(wsa_events.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE)) + mask |= CURL_WAIT_POLLIN; + if(wsa_events.lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE)) + mask |= CURL_WAIT_POLLOUT; + if(wsa_events.lNetworkEvents & FD_OOB) + mask |= CURL_WAIT_POLLPRI; + if(ret && !pollrc && wsa_events.lNetworkEvents) + retcode++; + } + WSAEventSelect(s, multi->wsa_event, 0); + if(!pollrc) { + extra_fds[i].revents = (short)mask; + continue; + } +#endif + if(r & POLLIN) + mask |= CURL_WAIT_POLLIN; + if(r & POLLOUT) + mask |= CURL_WAIT_POLLOUT; + if(r & POLLPRI) + mask |= CURL_WAIT_POLLPRI; + extra_fds[i].revents = (short)mask; + } + +#ifdef USE_WINSOCK + /* Count up all our own sockets that had activity, + and remove them from the event. */ + if(curl_nfds) { + + for(data = multi->easyp; data; data = data->next) { + multi_getsock(data, &ps); + + for(i = 0; i < ps.num; i++) { + wsa_events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(ps.sockets[i], NULL, + &wsa_events) == 0) { + if(ret && !pollrc && wsa_events.lNetworkEvents) + retcode++; + } + WSAEventSelect(ps.sockets[i], multi->wsa_event, 0); + } + } + } + + WSAResetEvent(multi->wsa_event); +#else +#ifdef ENABLE_WAKEUP + if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { + if(cpfds.pfds[curl_nfds + extra_nfds].revents & POLLIN) { + char buf[64]; + ssize_t nread; + while(1) { + /* the reading socket is non-blocking, try to read + data from it until it receives an error (except EINTR). + In normal cases it will get EAGAIN or EWOULDBLOCK + when there is no more data, breaking the loop. */ + nread = wakeup_read(multi->wakeup_pair[0], buf, sizeof(buf)); + if(nread <= 0) { + if(nread < 0 && EINTR == SOCKERRNO) + continue; + break; + } + } + /* do not count the wakeup socket into the returned value */ + retcode--; + } + } +#endif +#endif + } + } + + if(ret) + *ret = retcode; +#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) + if(extrawait && !cpfds.n && !use_wakeup) { +#else + if(extrawait && !cpfds.n) { +#endif + long sleep_ms = 0; + + /* Avoid busy-looping when there is nothing particular to wait for */ + if(!curl_multi_timeout(multi, &sleep_ms) && sleep_ms) { + if(sleep_ms > timeout_ms) + sleep_ms = timeout_ms; + /* when there are no easy handles in the multi, this holds a -1 + timeout */ + else if(sleep_ms < 0) + sleep_ms = timeout_ms; + Curl_wait_ms(sleep_ms); + } + } + +out: + Curl_pollfds_cleanup(&cpfds); + return result; +} + +CURLMcode curl_multi_wait(struct Curl_multi *multi, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret) +{ + return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE, + FALSE); +} + +CURLMcode curl_multi_poll(struct Curl_multi *multi, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret) +{ + return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE, + TRUE); +} + +CURLMcode curl_multi_wakeup(struct Curl_multi *multi) +{ + /* this function is usually called from another thread, + it has to be careful only to access parts of the + Curl_multi struct that are constant */ + +#if defined(ENABLE_WAKEUP) && !defined(USE_WINSOCK) +#ifdef USE_EVENTFD + const void *buf; + const uint64_t val = 1; +#else + char buf[1]; +#endif +#endif + + /* GOOD_MULTI_HANDLE can be safely called */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + +#ifdef ENABLE_WAKEUP +#ifdef USE_WINSOCK + if(WSASetEvent(multi->wsa_event)) + return CURLM_OK; +#else + /* the wakeup_pair variable is only written during init and cleanup, + making it safe to access from another thread after the init part + and before cleanup */ + if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) { +#ifdef USE_EVENTFD + buf = &val; +#else + buf[0] = 1; +#endif + while(1) { + /* swrite() is not thread-safe in general, because concurrent calls + can have their messages interleaved, but in this case the content + of the messages does not matter, which makes it ok to call. + + The write socket is set to non-blocking, this way this function + cannot block, making it safe to call even from the same thread + that will call curl_multi_wait(). If swrite() returns that it + would block, it is considered successful because it means that + previous calls to this function will wake up the poll(). */ + if(wakeup_write(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) { + int err = SOCKERRNO; + int return_success; +#ifdef USE_WINSOCK + return_success = WSAEWOULDBLOCK == err; +#else + if(EINTR == err) + continue; + return_success = EWOULDBLOCK == err || EAGAIN == err; +#endif + if(!return_success) + return CURLM_WAKEUP_FAILURE; + } + return CURLM_OK; + } + } +#endif +#endif + return CURLM_WAKEUP_FAILURE; +} + +/* + * multi_ischanged() is called + * + * Returns TRUE/FALSE whether the state is changed to trigger a CONNECT_PEND + * => CONNECT action. + * + * Set 'clear' to TRUE to have it also clear the state variable. + */ +static bool multi_ischanged(struct Curl_multi *multi, bool clear) +{ + bool retval = multi->recheckstate; + if(clear) + multi->recheckstate = FALSE; + return retval; +} + +/* + * Curl_multi_connchanged() is called to tell that there is a connection in + * this multi handle that has changed state (multiplexing become possible, the + * number of allowed streams changed or similar), and a subsequent use of this + * multi handle should move CONNECT_PEND handles back to CONNECT to have them + * retry. + */ +void Curl_multi_connchanged(struct Curl_multi *multi) +{ + multi->recheckstate = TRUE; +} + +CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, + struct Curl_easy *data, + struct connectdata *conn) +{ + CURLMcode rc; + + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + rc = curl_multi_add_handle(multi, data); + if(!rc) { + struct SingleRequest *k = &data->req; + + /* pass in NULL for 'conn' here since we do not want to init the + connection, only this transfer */ + Curl_init_do(data, NULL); + + /* take this handle to the perform state right away */ + multistate(data, MSTATE_PERFORMING); + Curl_attach_connection(data, conn); + k->keepon |= KEEP_RECV; /* setup to receive! */ + } + return rc; +} + +static CURLcode multi_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + DEBUGASSERT(conn); + DEBUGASSERT(conn->handler); + + if(conn->handler->do_it) + result = conn->handler->do_it(data, done); + + return result; +} + +/* + * multi_do_more() is called during the DO_MORE multi state. It is basically a + * second stage DO state which (wrongly) was introduced to support FTP's + * second connection. + * + * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to + * DOING state there is more work to do! + */ + +static CURLcode multi_do_more(struct Curl_easy *data, int *complete) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + *complete = 0; + + if(conn->handler->do_more) + result = conn->handler->do_more(data, complete); + + return result; +} + +/* + * Check whether a timeout occurred, and handle it if it did + */ +static bool multi_handle_timeout(struct Curl_easy *data, + struct curltime *now, + bool *stream_error, + CURLcode *result, + bool connect_timeout) +{ + timediff_t timeout_ms = Curl_timeleft(data, now, connect_timeout); + if(timeout_ms < 0) { + /* Handle timed out */ + struct curltime since; + if(connect_timeout) + since = data->progress.t_startsingle; + else + since = data->progress.t_startop; + if(data->mstate == MSTATE_RESOLVING) + failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds", Curl_timediff(*now, since)); + else if(data->mstate == MSTATE_CONNECTING) + failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds", Curl_timediff(*now, since)); + else { + struct SingleRequest *k = &data->req; + if(k->size != -1) { + failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" + CURL_FORMAT_CURL_OFF_T " bytes received", + Curl_timediff(*now, since), k->bytecount, k->size); + } + else { + failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds with %" CURL_FORMAT_CURL_OFF_T + " bytes received", Curl_timediff(*now, since), k->bytecount); + } + } + *result = CURLE_OPERATION_TIMEDOUT; + if(data->conn) { + /* Force connection closed if the connection has indeed been used */ + if(data->mstate > MSTATE_DO) { + streamclose(data->conn, "Disconnect due to timeout"); + *stream_error = TRUE; + } + (void)multi_done(data, *result, TRUE); + } + return TRUE; + } + + return FALSE; +} + +/* + * We are doing protocol-specific connecting and this is being called over and + * over from the multi interface until the connection phase is done on + * protocol layer. + */ + +static CURLcode protocol_connecting(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + if(conn && conn->handler->connecting) { + *done = FALSE; + result = conn->handler->connecting(data, done); + } + else + *done = TRUE; + + return result; +} + +/* + * We are DOING this is being called over and over from the multi interface + * until the DOING phase is done on protocol layer. + */ + +static CURLcode protocol_doing(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + if(conn && conn->handler->doing) { + *done = FALSE; + result = conn->handler->doing(data, done); + } + else + *done = TRUE; + + return result; +} + +/* + * We have discovered that the TCP connection has been successful, we can now + * proceed with some action. + * + */ +static CURLcode protocol_connect(struct Curl_easy *data, + bool *protocol_done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + DEBUGASSERT(conn); + DEBUGASSERT(protocol_done); + + *protocol_done = FALSE; + + if(Curl_conn_is_connected(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 do not know if the protocol is actually done. + + Unless this protocol does not have any protocol-connect callback, as + then we know we are done. */ + if(!conn->handler->connecting) + *protocol_done = TRUE; + + return CURLE_OK; + } + + if(!conn->bits.protoconnstart) { + if(conn->handler->connect_it) { + /* is there a protocol-specific connect() procedure? */ + + /* Call the protocol-specific connect function */ + result = conn->handler->connect_it(data, protocol_done); + } + else + *protocol_done = TRUE; + + /* it has started, possibly even completed but that knowledge is not stored + in this bit! */ + if(!result) + conn->bits.protoconnstart = TRUE; + } + + return result; /* pass back status */ +} + +static void set_in_callback(struct Curl_multi *multi, bool value) +{ + multi->in_callback = value; +} + +/* + * posttransfer() is called immediately after a transfer ends + */ +static void multi_posttransfer(struct Curl_easy *data) +{ +#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) + /* restore the signal handler for SIGPIPE before we get back */ + if(!data->set.no_signal) + signal(SIGPIPE, data->state.prev_signal); +#else + (void)data; /* unused parameter */ +#endif +} + +static CURLMcode multi_runsingle(struct Curl_multi *multi, + struct curltime *nowp, + struct Curl_easy *data) +{ + struct Curl_message *msg = NULL; + bool connected; + bool async; + bool protocol_connected = FALSE; + bool dophase_done = FALSE; + CURLMcode rc; + CURLcode result = CURLE_OK; + timediff_t recv_timeout_ms; + timediff_t send_timeout_ms; + int control; + + if(!GOOD_EASY_HANDLE(data)) + return CURLM_BAD_EASY_HANDLE; + + if(multi->dead) { + /* a multi-level callback returned error before, meaning every individual + transfer now has failed */ + result = CURLE_ABORTED_BY_CALLBACK; + multi_posttransfer(data); + multi_done(data, result, FALSE); + multistate(data, MSTATE_COMPLETED); + } + + multi_warn_debug(multi, data); + + do { + /* A "stream" here is a logical stream if the protocol can handle that + (HTTP/2), or the full connection for older protocols */ + bool stream_error = FALSE; + rc = CURLM_OK; + + if(multi_ischanged(multi, TRUE)) { + DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue")); + process_pending_handles(multi); /* multiplexed */ + } + + if(data->mstate > MSTATE_CONNECT && + data->mstate < MSTATE_COMPLETED) { + /* Make sure we set the connection's current owner */ + DEBUGASSERT(data->conn); + if(!data->conn) + return CURLM_INTERNAL_ERROR; + } + + /* Wait for the connect state as only then is the start time stored, but + we must not check already completed handles */ + if((data->mstate >= MSTATE_CONNECT) && (data->mstate < MSTATE_COMPLETED) && + multi_handle_timeout(data, nowp, &stream_error, &result, FALSE)) + /* Skip the statemachine and go directly to error handling section. */ + goto statemachine_end; + + switch(data->mstate) { + case MSTATE_INIT: + /* Transitional state. init this transfer. A handle never comes + back to this state. */ + result = Curl_pretransfer(data); + if(result) + break; + + /* after init, go SETUP */ + multistate(data, MSTATE_SETUP); + (void)Curl_pgrsTime(data, TIMER_STARTOP); + FALLTHROUGH(); + + case MSTATE_SETUP: + /* Transitional state. Setup things for a new transfer. The handle + can come back to this state on a redirect. */ + *nowp = Curl_pgrsTime(data, TIMER_STARTSINGLE); + if(data->set.timeout) + Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); + if(data->set.connecttimeout) + /* Since a connection might go to pending and back to CONNECT several + times before it actually takes off, we need to set the timeout once + in SETUP before we enter CONNECT the first time. */ + Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); + + multistate(data, MSTATE_CONNECT); + FALLTHROUGH(); + + case MSTATE_CONNECT: + /* Connect. We want to get a connection identifier filled in. This state + can be entered from SETUP and from PENDING. */ + 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. */ + multistate(data, MSTATE_PENDING); + + /* add this handle to the list of connect-pending handles */ + Curl_llist_append(&multi->pending, data, &data->connect_queue); + /* unlink from the main list */ + unlink_easy(multi, data); + result = CURLE_OK; + break; + } + else + process_pending_handles(data->multi); + + if(!result) { + *nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE); + if(async) + /* We are now waiting for an asynchronous name lookup */ + multistate(data, MSTATE_RESOLVING); + else { + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to + WAITDO or DO! */ + rc = CURLM_CALL_MULTI_PERFORM; + + if(connected) + multistate(data, MSTATE_PROTOCONNECT); + else { + multistate(data, MSTATE_CONNECTING); + } + } + } + break; + + case MSTATE_RESOLVING: + /* awaiting an asynch name resolve to complete */ + { + struct Curl_dns_entry *dns = NULL; + struct connectdata *conn = data->conn; + const char *hostname; + + DEBUGASSERT(conn); +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy) + hostname = conn->http_proxy.host.name; + else +#endif + if(conn->bits.conn_to_host) + hostname = conn->conn_to_host.name; + else + hostname = conn->host.name; + + /* check if we have the name resolved by now */ + dns = Curl_fetch_addr(data, hostname, conn->primary.remote_port); + + if(dns) { +#ifdef CURLRES_ASYNCH + data->state.async.dns = dns; + data->state.async.done = TRUE; +#endif + result = CURLE_OK; + infof(data, "Hostname '%s' was found in DNS cache", hostname); + } + + if(!dns) + result = Curl_resolv_check(data, &dns); + + /* Update sockets here, because the socket(s) may have been + closed and the application thus needs to be told, even if it + is likely that the same socket(s) will again be used further + down. If the name has not yet been resolved, it is likely + that new sockets have been opened in an attempt to contact + another resolver. */ + rc = singlesocket(multi, data); + if(rc) + return rc; + + if(dns) { + /* Perform the next step in the connection phase, and then move on + to the WAITCONNECT state */ + result = Curl_once_resolved(data, &connected); + + if(result) + /* if Curl_once_resolved() returns failure, the connection struct + is already freed and gone */ + data->conn = NULL; /* no more connection */ + else { + /* call again please so that we get the next socket setup */ + rc = CURLM_CALL_MULTI_PERFORM; + if(connected) + multistate(data, MSTATE_PROTOCONNECT); + else { + multistate(data, MSTATE_CONNECTING); + } + } + } + + if(result) { + /* failure detected */ + stream_error = TRUE; + break; + } + } + break; + +#ifndef CURL_DISABLE_HTTP + case MSTATE_TUNNELING: + /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ + DEBUGASSERT(data->conn); + result = Curl_http_connect(data, &protocol_connected); +#ifndef CURL_DISABLE_PROXY + if(data->conn->bits.proxy_connect_closed) { + rc = CURLM_CALL_MULTI_PERFORM; + /* connect back to proxy again */ + result = CURLE_OK; + multi_done(data, CURLE_OK, FALSE); + multistate(data, MSTATE_CONNECT); + } + else +#endif + if(!result) { + rc = CURLM_CALL_MULTI_PERFORM; + /* initiate protocol connect phase */ + multistate(data, MSTATE_PROTOCONNECT); + } + else + stream_error = TRUE; + break; +#endif + + case MSTATE_CONNECTING: + /* awaiting a completion of an asynch TCP connect */ + DEBUGASSERT(data->conn); + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); + if(connected && !result) { + rc = CURLM_CALL_MULTI_PERFORM; + multistate(data, MSTATE_PROTOCONNECT); + } + else if(result) { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, result, TRUE); + stream_error = TRUE; + break; + } + break; + + case MSTATE_PROTOCONNECT: + if(!result && data->conn->bits.reuse) { + /* ftp seems to hang when protoconnect on reused connection + * since we handle PROTOCONNECT in general inside the filers, it + * seems wrong to restart this on a reused connection. */ + multistate(data, MSTATE_DO); + rc = CURLM_CALL_MULTI_PERFORM; + break; + } + if(!result) + result = protocol_connect(data, &protocol_connected); + if(!result && !protocol_connected) { + /* switch to waiting state */ + multistate(data, MSTATE_PROTOCONNECTING); + rc = CURLM_CALL_MULTI_PERFORM; + } + else if(!result) { + /* protocol connect has completed, go WAITDO or DO */ + multistate(data, MSTATE_DO); + rc = CURLM_CALL_MULTI_PERFORM; + } + else { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, result, TRUE); + stream_error = TRUE; + } + break; + + case MSTATE_PROTOCONNECTING: + /* protocol-specific connect phase */ + result = protocol_connecting(data, &protocol_connected); + if(!result && protocol_connected) { + /* after the connect has completed, go WAITDO or DO */ + multistate(data, MSTATE_DO); + rc = CURLM_CALL_MULTI_PERFORM; + } + else if(result) { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, result, TRUE); + stream_error = TRUE; + } + break; + + case MSTATE_DO: + if(data->set.fprereq) { + int prereq_rc; + + /* call the prerequest callback function */ + Curl_set_in_callback(data, true); + prereq_rc = data->set.fprereq(data->set.prereq_userp, + data->info.primary.remote_ip, + data->info.primary.local_ip, + data->info.primary.remote_port, + data->info.primary.local_port); + Curl_set_in_callback(data, false); + if(prereq_rc != CURL_PREREQFUNC_OK) { + failf(data, "operation aborted by pre-request callback"); + /* failure in pre-request callback - do not do any other + processing */ + result = CURLE_ABORTED_BY_CALLBACK; + multi_posttransfer(data); + multi_done(data, result, FALSE); + stream_error = TRUE; + break; + } + } + + if(data->set.connect_only == 1) { + /* keep connection open for application to use the socket */ + connkeep(data->conn, "CONNECT_ONLY"); + multistate(data, MSTATE_DONE); + result = CURLE_OK; + rc = CURLM_CALL_MULTI_PERFORM; + } + else { + /* Perform the protocol's DO action */ + result = multi_do(data, &dophase_done); + + /* When multi_do() returns failure, data->conn might be NULL! */ + + if(!result) { + if(!dophase_done) { +#ifndef CURL_DISABLE_FTP + /* some steps needed for wildcard matching */ + if(data->state.wildcardmatch) { + struct WildcardData *wc = data->wildcard; + if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { + /* skip some states if it is important */ + multi_done(data, CURLE_OK, FALSE); + + /* if there is no connection left, skip the DONE state */ + multistate(data, data->conn ? + MSTATE_DONE : MSTATE_COMPLETED); + rc = CURLM_CALL_MULTI_PERFORM; + break; + } + } +#endif + /* DO was not completed in one function call, we must continue + DOING... */ + multistate(data, MSTATE_DOING); + rc = CURLM_CALL_MULTI_PERFORM; + } + + /* after DO, go DO_DONE... or DO_MORE */ + else if(data->conn->bits.do_more) { + /* we are supposed to do more, but we need to sit down, relax + and wait a little while first */ + multistate(data, MSTATE_DOING_MORE); + rc = CURLM_CALL_MULTI_PERFORM; + } + else { + /* we are done with the DO, now DID */ + multistate(data, MSTATE_DID); + rc = CURLM_CALL_MULTI_PERFORM; + } + } + else if((CURLE_SEND_ERROR == result) && + data->conn->bits.reuse) { + /* + * In this situation, a connection that we were trying to use + * may have unexpectedly died. If possible, send the connection + * back to the CONNECT phase so we can try again. + */ + char *newurl = NULL; + followtype follow = FOLLOW_NONE; + CURLcode drc; + + drc = Curl_retry_request(data, &newurl); + if(drc) { + /* a failure here pretty much implies an out of memory */ + result = drc; + stream_error = TRUE; + } + + multi_posttransfer(data); + drc = multi_done(data, result, FALSE); + + /* When set to retry the connection, we must go back to the CONNECT + * state */ + if(newurl) { + if(!drc || (drc == CURLE_SEND_ERROR)) { + follow = FOLLOW_RETRY; + drc = Curl_follow(data, newurl, follow); + if(!drc) { + multistate(data, MSTATE_SETUP); + rc = CURLM_CALL_MULTI_PERFORM; + result = CURLE_OK; + } + else { + /* Follow failed */ + result = drc; + } + } + else { + /* done did not return OK or SEND_ERROR */ + result = drc; + } + } + else { + /* Have error handler disconnect conn if we cannot retry */ + stream_error = TRUE; + } + free(newurl); + } + else { + /* failure detected */ + multi_posttransfer(data); + if(data->conn) + multi_done(data, result, FALSE); + stream_error = TRUE; + } + } + break; + + case MSTATE_DOING: + /* we continue DOING until the DO phase is complete */ + DEBUGASSERT(data->conn); + result = protocol_doing(data, &dophase_done); + if(!result) { + if(dophase_done) { + /* after DO, go DO_DONE or DO_MORE */ + multistate(data, data->conn->bits.do_more? + MSTATE_DOING_MORE : MSTATE_DID); + rc = CURLM_CALL_MULTI_PERFORM; + } /* dophase_done */ + } + else { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, result, FALSE); + stream_error = TRUE; + } + break; + + case MSTATE_DOING_MORE: + /* + * When we are connected, DOING MORE and then go DID + */ + DEBUGASSERT(data->conn); + result = multi_do_more(data, &control); + + if(!result) { + if(control) { + /* if positive, advance to DO_DONE + if negative, go back to DOING */ + multistate(data, control == 1? + MSTATE_DID : MSTATE_DOING); + rc = CURLM_CALL_MULTI_PERFORM; + } + /* else + stay in DO_MORE */ + } + else { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, result, FALSE); + stream_error = TRUE; + } + break; + + case MSTATE_DID: + DEBUGASSERT(data->conn); + if(data->conn->bits.multiplex) + /* Check if we can move pending requests to send pipe */ + process_pending_handles(multi); /* multiplexed */ + + /* Only perform the transfer if there is a good socket to work with. + Having both BAD is a signal to skip immediately to DONE */ + if((data->conn->sockfd != CURL_SOCKET_BAD) || + (data->conn->writesockfd != CURL_SOCKET_BAD)) + multistate(data, MSTATE_PERFORMING); + else { +#ifndef CURL_DISABLE_FTP + if(data->state.wildcardmatch && + ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) { + data->wildcard->state = CURLWC_DONE; + } +#endif + multistate(data, MSTATE_DONE); + } + rc = CURLM_CALL_MULTI_PERFORM; + break; + + case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */ + DEBUGASSERT(data->conn); + /* if both rates are within spec, resume transfer */ + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, *nowp); + + if(result) { + if(!(data->conn->handler->flags & PROTOPT_DUAL) && + result != CURLE_HTTP2_STREAM) + streamclose(data->conn, "Transfer returned error"); + + multi_posttransfer(data); + multi_done(data, result, TRUE); + } + else { + send_timeout_ms = 0; + if(data->set.max_send_speed) + send_timeout_ms = + Curl_pgrsLimitWaitTime(data->progress.uploaded, + data->progress.ul_limit_size, + data->set.max_send_speed, + data->progress.ul_limit_start, + *nowp); + + recv_timeout_ms = 0; + if(data->set.max_recv_speed) + recv_timeout_ms = + Curl_pgrsLimitWaitTime(data->progress.downloaded, + data->progress.dl_limit_size, + data->set.max_recv_speed, + data->progress.dl_limit_start, + *nowp); + + if(!send_timeout_ms && !recv_timeout_ms) { + multistate(data, MSTATE_PERFORMING); + Curl_ratelimit(data, *nowp); + } + else if(send_timeout_ms >= recv_timeout_ms) + Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); + else + Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); + } + break; + + case MSTATE_PERFORMING: + { + char *newurl = NULL; + bool retry = FALSE; + /* check if over send speed */ + send_timeout_ms = 0; + if(data->set.max_send_speed) + send_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded, + data->progress.ul_limit_size, + data->set.max_send_speed, + data->progress.ul_limit_start, + *nowp); + + /* check if over recv speed */ + recv_timeout_ms = 0; + if(data->set.max_recv_speed) + recv_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded, + data->progress.dl_limit_size, + data->set.max_recv_speed, + data->progress.dl_limit_start, + *nowp); + + if(send_timeout_ms || recv_timeout_ms) { + Curl_ratelimit(data, *nowp); + multistate(data, MSTATE_RATELIMITING); + if(send_timeout_ms >= recv_timeout_ms) + Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); + else + Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); + break; + } + + /* read/write data if it is ready to do so */ + result = Curl_readwrite(data); + + if(data->req.done || (result == CURLE_RECV_ERROR)) { + /* If CURLE_RECV_ERROR happens early enough, we assume it was a race + * condition and the server closed the reused connection exactly when + * we wanted to use it, so figure out if that is indeed the case. + */ + CURLcode ret = Curl_retry_request(data, &newurl); + if(!ret) + retry = (newurl)?TRUE:FALSE; + else if(!result) + result = ret; + + if(retry) { + /* if we are to retry, set the result to OK and consider the + request as done */ + result = CURLE_OK; + data->req.done = TRUE; + } + } + else if((CURLE_HTTP2_STREAM == result) && + Curl_h2_http_1_1_error(data)) { + CURLcode ret = Curl_retry_request(data, &newurl); + + if(!ret) { + infof(data, "Downgrades to HTTP/1.1"); + streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1"); + data->state.httpwant = CURL_HTTP_VERSION_1_1; + /* clear the error message bit too as we ignore the one we got */ + data->state.errorbuf = FALSE; + if(!newurl) + /* typically for HTTP_1_1_REQUIRED error on first flight */ + newurl = strdup(data->state.url); + /* if we are to retry, set the result to OK and consider the request + as done */ + retry = TRUE; + result = CURLE_OK; + data->req.done = TRUE; + } + else + result = ret; + } + + if(result) { + /* + * The transfer phase returned error, we mark the connection to get + * closed to prevent being reused. This is because we cannot possibly + * know if the connection is in a good shape or not now. Unless it is + * a protocol which uses two "channels" like FTP, as then the error + * happened in the data connection. + */ + + if(!(data->conn->handler->flags & PROTOPT_DUAL) && + result != CURLE_HTTP2_STREAM) + streamclose(data->conn, "Transfer returned error"); + + multi_posttransfer(data); + multi_done(data, result, TRUE); + } + else if(data->req.done && !Curl_cwriter_is_paused(data)) { + + /* call this even if the readwrite function returned error */ + multi_posttransfer(data); + + /* When we follow redirects or is set to retry the connection, we must + to go back to the CONNECT state */ + if(data->req.newurl || retry) { + followtype follow = FOLLOW_NONE; + if(!retry) { + /* if the URL is a follow-location and not just a retried request + then figure out the URL here */ + free(newurl); + newurl = data->req.newurl; + data->req.newurl = NULL; + follow = FOLLOW_REDIR; + } + else + follow = FOLLOW_RETRY; + (void)multi_done(data, CURLE_OK, FALSE); + /* multi_done() might return CURLE_GOT_NOTHING */ + result = Curl_follow(data, newurl, follow); + if(!result) { + multistate(data, MSTATE_SETUP); + rc = CURLM_CALL_MULTI_PERFORM; + } + } + else { + /* after the transfer is done, go DONE */ + + /* but first check to see if we got a location info even though we + are not following redirects */ + if(data->req.location) { + free(newurl); + newurl = data->req.location; + data->req.location = NULL; + result = Curl_follow(data, newurl, FOLLOW_FAKE); + if(result) { + stream_error = TRUE; + result = multi_done(data, result, TRUE); + } + } + + if(!result) { + multistate(data, MSTATE_DONE); + rc = CURLM_CALL_MULTI_PERFORM; + } + } + } + else if(data->state.select_bits && !Curl_xfer_is_blocked(data)) { + /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer + will not get stuck on this transfer at the expense of other + concurrent transfers */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + free(newurl); + break; + } + + case MSTATE_DONE: + /* this state is highly transient, so run another loop after this */ + rc = CURLM_CALL_MULTI_PERFORM; + + if(data->conn) { + CURLcode res; + + if(data->conn->bits.multiplex) + /* Check if we can move pending requests to connection */ + process_pending_handles(multi); /* multiplexing */ + + /* post-transfer command */ + res = multi_done(data, result, FALSE); + + /* allow a previously set error code take precedence */ + if(!result) + result = res; + } + +#ifndef CURL_DISABLE_FTP + if(data->state.wildcardmatch) { + if(data->wildcard->state != CURLWC_DONE) { + /* if a wildcard is set and we are not ending -> lets start again + with MSTATE_INIT */ + multistate(data, MSTATE_INIT); + break; + } + } +#endif + /* after we have DONE what we are supposed to do, go COMPLETED, and + it does not matter what the multi_done() returned! */ + multistate(data, MSTATE_COMPLETED); + break; + + case MSTATE_COMPLETED: + break; + + case MSTATE_PENDING: + case MSTATE_MSGSENT: + /* handles in these states should NOT be in this list */ + DEBUGASSERT(0); + break; + + default: + return CURLM_INTERNAL_ERROR; + } + + if(data->mstate >= MSTATE_CONNECT && + data->mstate < MSTATE_DO && + rc != CURLM_CALL_MULTI_PERFORM && + !multi_ischanged(multi, false)) { + /* We now handle stream timeouts if and only if this will be the last + * loop iteration. We only check this on the last iteration to ensure + * that if we know we have additional work to do immediately + * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before + * declaring the connection timed out as we may almost have a completed + * connection. */ + multi_handle_timeout(data, nowp, &stream_error, &result, FALSE); + } + +statemachine_end: + + if(data->mstate < MSTATE_COMPLETED) { + if(result) { + /* + * If an error was returned, and we are not in completed state now, + * then we go to completed and consider this transfer aborted. + */ + + /* NOTE: no attempt to disconnect connections must be made + in the case blocks above - cleanup happens only here */ + + /* Check if we can move pending requests to send pipe */ + process_pending_handles(multi); /* connection */ + + if(data->conn) { + if(stream_error) { + /* Do not attempt to send data over a connection that timed out */ + bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; + struct connectdata *conn = data->conn; + + /* This is where we make sure that the conn pointer is reset. + We do not have to do this in every case block above where a + failure is detected */ + Curl_detach_connection(data); + + /* remove connection from cache */ + Curl_conncache_remove_conn(data, conn, TRUE); + + /* disconnect properly */ + Curl_disconnect(data, conn, dead_connection); + } + } + else if(data->mstate == MSTATE_CONNECT) { + /* Curl_connect() failed */ + multi_posttransfer(data); + Curl_pgrsUpdate_nometer(data); + } + + multistate(data, MSTATE_COMPLETED); + rc = CURLM_CALL_MULTI_PERFORM; + } + /* if there is still a connection to use, call the progress function */ + else if(data->conn && Curl_pgrsUpdate(data)) { + /* aborted due to progress callback return code must close the + connection */ + result = CURLE_ABORTED_BY_CALLBACK; + streamclose(data->conn, "Aborted by callback"); + + /* if not yet in DONE state, go there, otherwise COMPLETED */ + multistate(data, (data->mstate < MSTATE_DONE)? + MSTATE_DONE: MSTATE_COMPLETED); + rc = CURLM_CALL_MULTI_PERFORM; + } + } + + if(MSTATE_COMPLETED == data->mstate) { + if(data->set.fmultidone) { + /* signal via callback instead */ + data->set.fmultidone(data, result); + } + else { + /* now fill in the Curl_message with this info */ + msg = &data->msg; + + msg->extmsg.msg = CURLMSG_DONE; + msg->extmsg.easy_handle = data; + msg->extmsg.data.result = result; + + multi_addmsg(multi, msg); + DEBUGASSERT(!data->conn); + } + multistate(data, MSTATE_MSGSENT); + + /* add this handle to the list of msgsent handles */ + Curl_llist_append(&multi->msgsent, data, &data->connect_queue); + /* unlink from the main list */ + unlink_easy(multi, data); + return CURLM_OK; + } + } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); + + data->result = result; + return rc; +} + + +CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) +{ + struct Curl_easy *data; + CURLMcode returncode = CURLM_OK; + struct Curl_tree *t; + struct curltime now = Curl_now(); + SIGPIPE_VARIABLE(pipe_st); + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + sigpipe_init(&pipe_st); + data = multi->easyp; + if(data) { + CURLMcode result; + /* Do the loop and only alter the signal ignore state if the next handle + has a different NO_SIGNAL state than the previous */ + do { + /* the current node might be unlinked in multi_runsingle(), get the next + pointer now */ + struct Curl_easy *datanext = data->next; + + if(data != multi->conn_cache.closure_handle) { + /* connection cache handle is processed below */ + sigpipe_apply(data, &pipe_st); + result = multi_runsingle(multi, &now, data); + if(result) + returncode = result; + } + + data = datanext; /* operate on next handle */ + } while(data); + } + + sigpipe_apply(multi->conn_cache.closure_handle, &pipe_st); + Curl_conncache_multi_perform(multi); + + sigpipe_restore(&pipe_st); + + /* + * Simply remove all expired timers from the splay since handles are dealt + * with unconditionally by this function and curl_multi_timeout() requires + * that already passed/handled expire times are removed from the splay. + * + * It is important that the 'now' value is set at the entry of this function + * and not for the current time as it may have ticked a little while since + * then and then we risk this loop to remove timers that actually have not + * been handled! + */ + do { + multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); + if(t) { + /* the removed may have another timeout in queue */ + data = t->payload; + if(data->mstate == MSTATE_PENDING) { + bool stream_unused; + CURLcode result_unused; + if(multi_handle_timeout(data, &now, &stream_unused, &result_unused, + FALSE)) { + infof(data, "PENDING handle timeout"); + move_pending_to_connect(multi, data); + } + } + (void)add_next_timeout(now, multi, t->payload); + } + } while(t); + + if(running_handles) + *running_handles = (int)multi->num_alive; + + if(CURLM_OK >= returncode) + returncode = Curl_update_timer(multi); + + return returncode; +} + +/* unlink_all_msgsent_handles() detaches all those easy handles from this + multi handle */ +static void unlink_all_msgsent_handles(struct Curl_multi *multi) +{ + struct Curl_llist_element *e = multi->msgsent.head; + if(e) { + struct Curl_easy *data = e->ptr; + DEBUGASSERT(data->mstate == MSTATE_MSGSENT); + data->multi = NULL; + } +} + +CURLMcode curl_multi_cleanup(struct Curl_multi *multi) +{ + struct Curl_easy *data; + struct Curl_easy *nextdata; + + if(GOOD_MULTI_HANDLE(multi)) { + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + multi->magic = 0; /* not good anymore */ + + unlink_all_msgsent_handles(multi); + process_pending_handles(multi); + /* First remove all remaining easy handles */ + data = multi->easyp; + while(data) { + if(!GOOD_EASY_HANDLE(data)) + return CURLM_BAD_HANDLE; + + nextdata = data->next; + if(!data->state.done && data->conn) + /* if DONE was never called for this handle */ + (void)multi_done(data, CURLE_OK, TRUE); + if(data->dns.hostcachetype == HCACHE_MULTI) { + /* clear out the usage of the shared DNS cache */ + Curl_hostcache_clean(data, data->dns.hostcache); + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; + } + + /* Clear the pointer to the connection cache */ + data->state.conn_cache = NULL; + data->multi = NULL; /* clear the association */ + +#ifdef USE_LIBPSL + if(data->psl == &multi->psl) + data->psl = NULL; +#endif + + data = nextdata; + } + + /* Close all the connections in the connection cache */ + Curl_conncache_multi_close_all(multi); + + sockhash_destroy(&multi->sockhash); + Curl_hash_destroy(&multi->proto_hash); + Curl_conncache_destroy(&multi->conn_cache); + Curl_hash_destroy(&multi->hostcache); + Curl_psl_destroy(&multi->psl); + +#ifdef USE_WINSOCK + WSACloseEvent(multi->wsa_event); +#else +#ifdef ENABLE_WAKEUP + wakeup_close(multi->wakeup_pair[0]); +#ifndef USE_EVENTFD + wakeup_close(multi->wakeup_pair[1]); +#endif +#endif +#endif + + multi_xfer_bufs_free(multi); + free(multi); + + return CURLM_OK; + } + return CURLM_BAD_HANDLE; +} + +/* + * curl_multi_info_read() + * + * This function is the primary way for a multi/multi_socket application to + * figure out if a transfer has ended. We MUST make this function as fast as + * possible as it will be polled frequently and we MUST NOT scan any lists in + * here to figure out things. We must scale fine to thousands of handles and + * beyond. The current design is fully O(1). + */ + +CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue) +{ + struct Curl_message *msg; + + *msgs_in_queue = 0; /* default to none */ + + if(GOOD_MULTI_HANDLE(multi) && + !multi->in_callback && + Curl_llist_count(&multi->msglist)) { + /* there is one or more messages in the list */ + struct Curl_llist_element *e; + + /* extract the head of the list to return */ + e = multi->msglist.head; + + msg = e->ptr; + + /* remove the extracted entry */ + Curl_llist_remove(&multi->msglist, e, NULL); + + *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist)); + + return &msg->extmsg; + } + return NULL; +} + +/* + * singlesocket() checks what sockets we deal with and their "action state" + * and if we have a different state in any of those sockets from last time we + * call the callback accordingly. + */ +static CURLMcode singlesocket(struct Curl_multi *multi, + struct Curl_easy *data) +{ + struct easy_pollset cur_poll; + CURLMcode mresult; + + /* Fill in the 'current' struct with the state as it is now: what sockets to + supervise and for what actions */ + multi_getsock(data, &cur_poll); + mresult = Curl_multi_pollset_ev(multi, data, &cur_poll, &data->last_poll); + + if(!mresult) /* Remember for next time */ + memcpy(&data->last_poll, &cur_poll, sizeof(cur_poll)); + return mresult; +} + +CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi, + struct Curl_easy *data, + struct easy_pollset *ps, + struct easy_pollset *last_ps) +{ + unsigned int i; + struct Curl_sh_entry *entry; + curl_socket_t s; + int rc; + + /* We have 0 .. N sockets already and we get to know about the 0 .. M + sockets we should have from now on. Detect the differences, remove no + longer supervised ones and add new ones */ + + /* walk over the sockets we got right now */ + for(i = 0; i < ps->num; i++) { + unsigned char cur_action = ps->actions[i]; + unsigned char last_action = 0; + int comboaction; + + s = ps->sockets[i]; + + /* get it from the hash */ + entry = sh_getentry(&multi->sockhash, s); + if(entry) { + /* check if new for this transfer */ + unsigned int j; + for(j = 0; j< last_ps->num; j++) { + if(s == last_ps->sockets[j]) { + last_action = last_ps->actions[j]; + break; + } + } + } + else { + /* this is a socket we did not have before, add it to the hash! */ + entry = sh_addentry(&multi->sockhash, s); + if(!entry) + /* fatal */ + return CURLM_OUT_OF_MEMORY; + } + if(last_action && (last_action != cur_action)) { + /* Socket was used already, but different action now */ + if(last_action & CURL_POLL_IN) + entry->readers--; + if(last_action & CURL_POLL_OUT) + entry->writers--; + if(cur_action & CURL_POLL_IN) + entry->readers++; + if(cur_action & CURL_POLL_OUT) + entry->writers++; + } + else if(!last_action && + !Curl_hash_pick(&entry->transfers, (char *)&data, /* hash key */ + sizeof(struct Curl_easy *))) { + /* a new transfer using this socket */ + entry->users++; + if(cur_action & CURL_POLL_IN) + entry->readers++; + if(cur_action & CURL_POLL_OUT) + entry->writers++; + /* add 'data' to the transfer hash on this socket! */ + if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */ + sizeof(struct Curl_easy *), data)) { + Curl_hash_destroy(&entry->transfers); + return CURLM_OUT_OF_MEMORY; + } + } + + comboaction = (entry->writers ? CURL_POLL_OUT : 0) | + (entry->readers ? CURL_POLL_IN : 0); + + /* socket existed before and has the same action set as before */ + if(last_action && ((int)entry->action == comboaction)) + /* same, continue */ + continue; + + if(multi->socket_cb) { + set_in_callback(multi, TRUE); + rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, + entry->socketp); + + set_in_callback(multi, FALSE); + if(rc == -1) { + multi->dead = TRUE; + return CURLM_ABORTED_BY_CALLBACK; + } + } + + /* store the current action state */ + entry->action = (unsigned int)comboaction; + } + + /* Check for last_poll.sockets that no longer appear in ps->sockets. + * Need to remove the easy handle from the multi->sockhash->transfers and + * remove multi->sockhash entry when this was the last transfer */ + for(i = 0; i < last_ps->num; i++) { + unsigned int j; + bool stillused = FALSE; + s = last_ps->sockets[i]; + for(j = 0; j < ps->num; j++) { + if(s == ps->sockets[j]) { + /* this is still supervised */ + stillused = TRUE; + break; + } + } + if(stillused) + continue; + + entry = sh_getentry(&multi->sockhash, s); + /* if this is NULL here, the socket has been closed and notified so + already by Curl_multi_closed() */ + if(entry) { + unsigned char oldactions = last_ps->actions[i]; + /* this socket has been removed. Decrease user count */ + entry->users--; + if(oldactions & CURL_POLL_OUT) + entry->writers--; + if(oldactions & CURL_POLL_IN) + entry->readers--; + if(!entry->users) { + if(multi->socket_cb) { + set_in_callback(multi, TRUE); + rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, + multi->socket_userp, entry->socketp); + set_in_callback(multi, FALSE); + if(rc == -1) { + multi->dead = TRUE; + return CURLM_ABORTED_BY_CALLBACK; + } + } + sh_delentry(entry, &multi->sockhash, s); + } + else { + /* still users, but remove this handle as a user of this socket */ + if(Curl_hash_delete(&entry->transfers, (char *)&data, + sizeof(struct Curl_easy *))) { + DEBUGASSERT(NULL); + } + } + } + } /* for loop over num */ + + return CURLM_OK; +} + +CURLcode Curl_updatesocket(struct Curl_easy *data) +{ + if(singlesocket(data->multi, data)) + return CURLE_ABORTED_BY_CALLBACK; + return CURLE_OK; +} + + +/* + * Curl_multi_closed() + * + * Used by the connect code to tell the multi_socket code that one of the + * sockets we were using is about to be closed. This function will then + * remove it from the sockethash for this handle to make the multi_socket API + * behave properly, especially for the case when libcurl will create another + * socket again and it gets the same file descriptor number. + */ + +void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s) +{ + if(data) { + /* if there is still an easy handle associated with this connection */ + struct Curl_multi *multi = data->multi; + if(multi) { + /* this is set if this connection is part of a handle that is added to + a multi handle, and only then this is necessary */ + struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); + + if(entry) { + int rc = 0; + if(multi->socket_cb) { + set_in_callback(multi, TRUE); + rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, + multi->socket_userp, entry->socketp); + set_in_callback(multi, FALSE); + } + + /* now remove it from the socket hash */ + sh_delentry(entry, &multi->sockhash, s); + if(rc == -1) + /* This just marks the multi handle as "dead" without returning an + error code primarily because this function is used from many + places where propagating an error back is tricky. */ + multi->dead = TRUE; + } + } + } +} + +/* + * add_next_timeout() + * + * Each Curl_easy has a list of timeouts. The add_next_timeout() is called + * when it has just been removed from the splay tree because the timeout has + * expired. This function is then to advance in the list to pick the next + * timeout to use (skip the already expired ones) and add this node back to + * the splay tree again. + * + * The splay tree only has each sessionhandle as a single node and the nearest + * timeout is used to sort it on. + */ +static CURLMcode add_next_timeout(struct curltime now, + struct Curl_multi *multi, + struct Curl_easy *d) +{ + struct curltime *tv = &d->state.expiretime; + struct Curl_llist *list = &d->state.timeoutlist; + struct Curl_llist_element *e; + struct time_node *node = NULL; + + /* move over the timeout list for this specific handle and remove all + timeouts that are now passed tense and store the next pending + timeout in *tv */ + for(e = list->head; e;) { + struct Curl_llist_element *n = e->next; + timediff_t diff; + node = (struct time_node *)e->ptr; + diff = Curl_timediff_us(node->time, now); + if(diff <= 0) + /* remove outdated entry */ + Curl_llist_remove(list, e, NULL); + else + /* the list is sorted so get out on the first mismatch */ + break; + e = n; + } + e = list->head; + if(!e) { + /* clear the expire times within the handles that we remove from the + splay tree */ + tv->tv_sec = 0; + tv->tv_usec = 0; + } + else { + /* copy the first entry to 'tv' */ + memcpy(tv, &node->time, sizeof(*tv)); + + /* Insert this node again into the splay. Keep the timer in the list in + case we need to recompute future timers. */ + multi->timetree = Curl_splayinsert(*tv, multi->timetree, + &d->state.timenode); + } + return CURLM_OK; +} + +static CURLMcode multi_socket(struct Curl_multi *multi, + bool checkall, + curl_socket_t s, + int ev_bitmask, + int *running_handles) +{ + CURLMcode result = CURLM_OK; + struct Curl_easy *data = NULL; + struct Curl_tree *t; + struct curltime now = Curl_now(); + bool run_conn_cache = FALSE; + SIGPIPE_VARIABLE(pipe_st); + + if(checkall) { + /* *perform() deals with running_handles on its own */ + result = curl_multi_perform(multi, running_handles); + + /* walk through each easy handle and do the socket state change magic + and callbacks */ + if(result != CURLM_BAD_HANDLE) { + data = multi->easyp; + while(data && !result) { + result = singlesocket(multi, data); + data = data->next; + } + } + + /* or should we fall-through and do the timer-based stuff? */ + return result; + } + if(s != CURL_SOCKET_TIMEOUT) { + struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); + + if(!entry) { + /* Unmatched socket, we cannot act on it but we ignore this fact. In + real-world tests it has been proved that libevent can in fact give + the application actions even though the socket was just previously + asked to get removed, so thus we better survive stray socket actions + and just move on. */ + /* The socket might come from a connection that is being shut down + * by the multi's conncache. */ + Curl_conncache_multi_socket(multi, s, ev_bitmask); + } + else { + struct Curl_hash_iterator iter; + struct Curl_hash_element *he; + + /* the socket can be shared by many transfers, iterate */ + Curl_hash_start_iterate(&entry->transfers, &iter); + for(he = Curl_hash_next_element(&iter); he; + he = Curl_hash_next_element(&iter)) { + data = (struct Curl_easy *)he->ptr; + DEBUGASSERT(data); + DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER); + + if(data == multi->conn_cache.closure_handle) + run_conn_cache = TRUE; + else { + if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) + /* set socket event bitmask if they are not locked */ + data->state.select_bits |= (unsigned char)ev_bitmask; + + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + } + + /* Now we fall-through and do the timer-based stuff, since we do not want + to force the user to have to deal with timeouts as long as at least + one connection in fact has traffic. */ + + data = NULL; /* set data to NULL again to avoid calling + multi_runsingle() in case there is no need to */ + now = Curl_now(); /* get a newer time since the multi_runsingle() loop + may have taken some time */ + } + } + else { + /* Asked to run due to time-out. Clear the 'lastcall' variable to force + Curl_update_timer() to trigger a callback to the app again even if the + same timeout is still the one to run after this call. That handles the + case when the application asks libcurl to run the timeout + prematurely. */ + memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); + } + + /* + * The loop following here will go on as long as there are expire-times left + * to process in the splay and 'data' will be re-assigned for every expired + * handle we deal with. + */ + sigpipe_init(&pipe_st); + do { + if(data == multi->conn_cache.closure_handle) + run_conn_cache = TRUE; + /* the first loop lap 'data' can be NULL */ + else if(data) { + sigpipe_apply(data, &pipe_st); + result = multi_runsingle(multi, &now, data); + + if(CURLM_OK >= result) { + /* get the socket(s) and check if the state has been changed since + last */ + result = singlesocket(multi, data); + if(result) + break; + } + } + + /* Check if there is one (more) expired timer to deal with! This function + extracts a matching node if there is one */ + + multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); + if(t) { + data = t->payload; /* assign this for next loop */ + (void)add_next_timeout(now, multi, t->payload); + } + + } while(t); + + if(run_conn_cache) { + sigpipe_apply(multi->conn_cache.closure_handle, &pipe_st); + Curl_conncache_multi_perform(multi); + } + + sigpipe_restore(&pipe_st); + + if(running_handles) + *running_handles = (int)multi->num_alive; + return result; +} + +#undef curl_multi_setopt +CURLMcode curl_multi_setopt(struct Curl_multi *multi, + CURLMoption option, ...) +{ + CURLMcode res = CURLM_OK; + va_list param; + unsigned long uarg; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + va_start(param, option); + + switch(option) { + case CURLMOPT_SOCKETFUNCTION: + multi->socket_cb = va_arg(param, curl_socket_callback); + break; + case CURLMOPT_SOCKETDATA: + multi->socket_userp = va_arg(param, void *); + break; + case CURLMOPT_PUSHFUNCTION: + multi->push_cb = va_arg(param, curl_push_callback); + break; + case CURLMOPT_PUSHDATA: + multi->push_userp = va_arg(param, void *); + break; + case CURLMOPT_PIPELINING: + multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0; + break; + case CURLMOPT_TIMERFUNCTION: + multi->timer_cb = va_arg(param, curl_multi_timer_callback); + break; + case CURLMOPT_TIMERDATA: + multi->timer_userp = va_arg(param, void *); + break; + case CURLMOPT_MAXCONNECTS: + uarg = va_arg(param, unsigned long); + if(uarg <= UINT_MAX) + multi->maxconnects = (unsigned int)uarg; + break; + case CURLMOPT_MAX_HOST_CONNECTIONS: + multi->max_host_connections = va_arg(param, long); + break; + case CURLMOPT_MAX_TOTAL_CONNECTIONS: + multi->max_total_connections = va_arg(param, long); + /* for now, let this also decide the max number of connections + * in shutdown handling */ + multi->max_shutdown_connections = va_arg(param, long); + break; + /* options formerly used for pipelining */ + case CURLMOPT_MAX_PIPELINE_LENGTH: + break; + case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: + break; + case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: + break; + case CURLMOPT_PIPELINING_SITE_BL: + break; + case CURLMOPT_PIPELINING_SERVER_BL: + break; + case CURLMOPT_MAX_CONCURRENT_STREAMS: + { + long streams = va_arg(param, long); + if((streams < 1) || (streams > INT_MAX)) + streams = 100; + multi->max_concurrent_streams = (unsigned int)streams; + } + break; + default: + res = CURLM_UNKNOWN_OPTION; + break; + } + va_end(param); + return res; +} + +/* we define curl_multi_socket() in the public multi.h header */ +#undef curl_multi_socket + +CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, + int *running_handles) +{ + CURLMcode result; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + result = multi_socket(multi, FALSE, s, 0, running_handles); + if(CURLM_OK >= result) + result = Curl_update_timer(multi); + return result; +} + +CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, + int ev_bitmask, int *running_handles) +{ + CURLMcode result; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles); + if(CURLM_OK >= result) + result = Curl_update_timer(multi); + return result; +} + +CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles) +{ + CURLMcode result; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); + if(CURLM_OK >= result) + result = Curl_update_timer(multi); + return result; +} + +static CURLMcode multi_timeout(struct Curl_multi *multi, + long *timeout_ms) +{ + static const struct curltime tv_zero = {0, 0}; + + if(multi->dead) { + *timeout_ms = 0; + return CURLM_OK; + } + + if(multi->timetree) { + /* we have a tree of expire times */ + struct curltime now = Curl_now(); + + /* splay the lowest to the bottom */ + multi->timetree = Curl_splay(tv_zero, multi->timetree); + + if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) { + /* some time left before expiration */ + timediff_t diff = Curl_timediff_ceil(multi->timetree->key, now); + /* this should be safe even on 32-bit archs, as we do not use that + overly long timeouts */ + *timeout_ms = (long)diff; + } + else + /* 0 means immediately */ + *timeout_ms = 0; + } + else + *timeout_ms = -1; + + return CURLM_OK; +} + +CURLMcode curl_multi_timeout(struct Curl_multi *multi, + long *timeout_ms) +{ + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + return multi_timeout(multi, timeout_ms); +} + +/* + * Tell the application it should update its timers, if it subscribes to the + * update timer callback. + */ +CURLMcode Curl_update_timer(struct Curl_multi *multi) +{ + long timeout_ms; + int rc; + + if(!multi->timer_cb || multi->dead) + return CURLM_OK; + if(multi_timeout(multi, &timeout_ms)) { + return CURLM_OK; + } + if(timeout_ms < 0) { + static const struct curltime none = {0, 0}; + if(Curl_splaycomparekeys(none, multi->timer_lastcall)) { + multi->timer_lastcall = none; + /* there is no timeout now but there was one previously, tell the app to + disable it */ + set_in_callback(multi, TRUE); + rc = multi->timer_cb(multi, -1, multi->timer_userp); + set_in_callback(multi, FALSE); + if(rc == -1) { + multi->dead = TRUE; + return CURLM_ABORTED_BY_CALLBACK; + } + return CURLM_OK; + } + return CURLM_OK; + } + + /* When multi_timeout() is done, multi->timetree points to the node with the + * timeout we got the (relative) time-out time for. We can thus easily check + * if this is the same (fixed) time as we got in a previous call and then + * avoid calling the callback again. */ + if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0) + return CURLM_OK; + + multi->timer_lastcall = multi->timetree->key; + + set_in_callback(multi, TRUE); + rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp); + set_in_callback(multi, FALSE); + if(rc == -1) { + multi->dead = TRUE; + return CURLM_ABORTED_BY_CALLBACK; + } + return CURLM_OK; +} + +/* + * multi_deltimeout() + * + * Remove a given timestamp from the list of timeouts. + */ +static void +multi_deltimeout(struct Curl_easy *data, expire_id eid) +{ + struct Curl_llist_element *e; + struct Curl_llist *timeoutlist = &data->state.timeoutlist; + /* find and remove the specific node from the list */ + for(e = timeoutlist->head; e; e = e->next) { + struct time_node *n = (struct time_node *)e->ptr; + if(n->eid == eid) { + Curl_llist_remove(timeoutlist, e, NULL); + return; + } + } +} + +/* + * multi_addtimeout() + * + * Add a timestamp to the list of timeouts. Keep the list sorted so that head + * of list is always the timeout nearest in time. + * + */ +static CURLMcode +multi_addtimeout(struct Curl_easy *data, + struct curltime *stamp, + expire_id eid) +{ + struct Curl_llist_element *e; + struct time_node *node; + struct Curl_llist_element *prev = NULL; + size_t n; + struct Curl_llist *timeoutlist = &data->state.timeoutlist; + + node = &data->state.expires[eid]; + + /* copy the timestamp and id */ + memcpy(&node->time, stamp, sizeof(*stamp)); + node->eid = eid; /* also marks it as in use */ + + n = Curl_llist_count(timeoutlist); + if(n) { + /* find the correct spot in the list */ + for(e = timeoutlist->head; e; e = e->next) { + struct time_node *check = (struct time_node *)e->ptr; + timediff_t diff = Curl_timediff(check->time, node->time); + if(diff > 0) + break; + prev = e; + } + + } + /* else + this is the first timeout on the list */ + + Curl_llist_insert_next(timeoutlist, prev, node, &node->list); + return CURLM_OK; +} + +/* + * Curl_expire() + * + * given a number of milliseconds from now to use to set the 'act before + * this'-time for the transfer, to be extracted by curl_multi_timeout() + * + * The timeout will be added to a queue of timeouts if it defines a moment in + * time that is later than the current head of queue. + * + * Expire replaces a former timeout using the same id if already set. + */ +void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) +{ + struct Curl_multi *multi = data->multi; + struct curltime *nowp = &data->state.expiretime; + struct curltime set; + + /* this is only interesting while there is still an associated multi struct + remaining! */ + if(!multi) + return; + + DEBUGASSERT(id < EXPIRE_LAST); + + set = Curl_now(); + set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bits conversion */ + set.tv_usec += (int)(milli%1000)*1000; + + if(set.tv_usec >= 1000000) { + set.tv_sec++; + set.tv_usec -= 1000000; + } + + /* Remove any timer with the same id just in case. */ + multi_deltimeout(data, id); + + /* Add it to the timer list. It must stay in the list until it has expired + in case we need to recompute the minimum timer later. */ + multi_addtimeout(data, &set, id); + + if(nowp->tv_sec || nowp->tv_usec) { + /* This means that the struct is added as a node in the splay tree. + Compare if the new time is earlier, and only remove-old/add-new if it + is. */ + timediff_t diff = Curl_timediff(set, *nowp); + int rc; + + if(diff > 0) { + /* The current splay tree entry is sooner than this new expiry time. + We do not need to update our splay tree entry. */ + return; + } + + /* Since this is an updated time, we must remove the previous entry from + the splay tree first and then re-add the new value */ + rc = Curl_splayremove(multi->timetree, &data->state.timenode, + &multi->timetree); + if(rc) + infof(data, "Internal error removing splay node = %d", rc); + } + + /* Indicate that we are in the splay tree and insert the new timer expiry + value since it is our local minimum. */ + *nowp = set; + data->state.timenode.payload = data; + multi->timetree = Curl_splayinsert(*nowp, multi->timetree, + &data->state.timenode); +} + +/* + * Curl_expire_done() + * + * Removes the expire timer. Marks it as done. + * + */ +void Curl_expire_done(struct Curl_easy *data, expire_id id) +{ + /* remove the timer, if there */ + multi_deltimeout(data, id); +} + +/* + * Curl_expire_clear() + * + * Clear ALL timeout values for this handle. + */ +void Curl_expire_clear(struct Curl_easy *data) +{ + struct Curl_multi *multi = data->multi; + struct curltime *nowp = &data->state.expiretime; + + /* this is only interesting while there is still an associated multi struct + remaining! */ + if(!multi) + return; + + if(nowp->tv_sec || nowp->tv_usec) { + /* Since this is an cleared time, we must remove the previous entry from + the splay tree */ + struct Curl_llist *list = &data->state.timeoutlist; + int rc; + + rc = Curl_splayremove(multi->timetree, &data->state.timenode, + &multi->timetree); + if(rc) + infof(data, "Internal error clearing splay node = %d", rc); + + /* flush the timeout list too */ + while(list->size > 0) { + Curl_llist_remove(list, list->tail, NULL); + } + +#ifdef DEBUGBUILD + infof(data, "Expire cleared"); +#endif + nowp->tv_sec = 0; + nowp->tv_usec = 0; + } +} + + + + +CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s, + void *hashp) +{ + struct Curl_sh_entry *there = NULL; + + there = sh_getentry(&multi->sockhash, s); + + if(!there) + return CURLM_BAD_SOCKET; + + there->socketp = hashp; + + return CURLM_OK; +} + +size_t Curl_multi_max_host_connections(struct Curl_multi *multi) +{ + return multi ? (size_t)multi->max_host_connections : 0; +} + +size_t Curl_multi_max_total_connections(struct Curl_multi *multi) +{ + return multi ? (size_t)multi->max_total_connections : 0; +} + +/* + * When information about a connection has appeared, call this! + */ + +void Curl_multiuse_state(struct Curl_easy *data, + int bundlestate) /* use BUNDLE_* defines */ +{ + struct connectdata *conn; + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + conn = data->conn; + DEBUGASSERT(conn); + DEBUGASSERT(conn->bundle); + + conn->bundle->multiuse = bundlestate; + process_pending_handles(data->multi); +} + +static void move_pending_to_connect(struct Curl_multi *multi, + struct Curl_easy *data) +{ + DEBUGASSERT(data->mstate == MSTATE_PENDING); + + /* put it back into the main list */ + link_easy(multi, data); + + multistate(data, MSTATE_CONNECT); + + /* Remove this node from the pending list */ + Curl_llist_remove(&multi->pending, &data->connect_queue, NULL); + + /* Make sure that the handle will be processed soonish. */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); +} + +/* process_pending_handles() moves a handle from PENDING back into the main + list and change state to CONNECT. + + We do not move all transfers because that can be a significant amount. + Since this is tried every now and then doing too many too often becomes a + performance problem. + + When there is a change for connection limits like max host connections etc, + this likely only allows one new transfer. When there is a pipewait change, + it can potentially allow hundreds of new transfers. + + We could consider an improvement where we store the queue reason and allow + more pipewait rechecks than others. +*/ +static void process_pending_handles(struct Curl_multi *multi) +{ + struct Curl_llist_element *e = multi->pending.head; + if(e) { + struct Curl_easy *data = e->ptr; + move_pending_to_connect(multi, data); + } +} + +void Curl_set_in_callback(struct Curl_easy *data, bool value) +{ + if(data && data->multi) + data->multi->in_callback = value; +} + +bool Curl_is_in_callback(struct Curl_easy *data) +{ + return (data && data->multi && data->multi->in_callback); +} + +unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) +{ + DEBUGASSERT(multi); + return multi->max_concurrent_streams; +} + +struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi) +{ + struct Curl_easy **a = malloc(sizeof(struct Curl_easy *) * + (multi->num_easy + 1)); + if(a) { + unsigned int i = 0; + struct Curl_easy *e = multi->easyp; + while(e) { + DEBUGASSERT(i < multi->num_easy); + if(!e->state.internal) + a[i++] = e; + e = e->next; + } + a[i] = NULL; /* last entry is a NULL */ + } + return a; +} + +CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data, + char **pbuf, size_t *pbuflen) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + *pbuf = NULL; + *pbuflen = 0; + if(!data->multi) { + failf(data, "transfer has no multi handle"); + return CURLE_FAILED_INIT; + } + if(!data->set.buffer_size) { + failf(data, "transfer buffer size is 0"); + return CURLE_FAILED_INIT; + } + if(data->multi->xfer_buf_borrowed) { + failf(data, "attempt to borrow xfer_buf when already borrowed"); + return CURLE_AGAIN; + } + + if(data->multi->xfer_buf && + data->set.buffer_size > data->multi->xfer_buf_len) { + /* not large enough, get a new one */ + free(data->multi->xfer_buf); + data->multi->xfer_buf = NULL; + data->multi->xfer_buf_len = 0; + } + + if(!data->multi->xfer_buf) { + data->multi->xfer_buf = malloc((size_t)data->set.buffer_size); + if(!data->multi->xfer_buf) { + failf(data, "could not allocate xfer_buf of %zu bytes", + (size_t)data->set.buffer_size); + return CURLE_OUT_OF_MEMORY; + } + data->multi->xfer_buf_len = data->set.buffer_size; + } + + data->multi->xfer_buf_borrowed = TRUE; + *pbuf = data->multi->xfer_buf; + *pbuflen = data->multi->xfer_buf_len; + return CURLE_OK; +} + +void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf) +{ + (void)buf; + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + DEBUGASSERT(!buf || data->multi->xfer_buf == buf); + data->multi->xfer_buf_borrowed = FALSE; +} + +CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data, + char **pbuf, size_t *pbuflen) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + *pbuf = NULL; + *pbuflen = 0; + if(!data->multi) { + failf(data, "transfer has no multi handle"); + return CURLE_FAILED_INIT; + } + if(!data->set.upload_buffer_size) { + failf(data, "transfer upload buffer size is 0"); + return CURLE_FAILED_INIT; + } + if(data->multi->xfer_ulbuf_borrowed) { + failf(data, "attempt to borrow xfer_ulbuf when already borrowed"); + return CURLE_AGAIN; + } + + if(data->multi->xfer_ulbuf && + data->set.upload_buffer_size > data->multi->xfer_ulbuf_len) { + /* not large enough, get a new one */ + free(data->multi->xfer_ulbuf); + data->multi->xfer_ulbuf = NULL; + data->multi->xfer_ulbuf_len = 0; + } + + if(!data->multi->xfer_ulbuf) { + data->multi->xfer_ulbuf = malloc((size_t)data->set.upload_buffer_size); + if(!data->multi->xfer_ulbuf) { + failf(data, "could not allocate xfer_ulbuf of %zu bytes", + (size_t)data->set.upload_buffer_size); + return CURLE_OUT_OF_MEMORY; + } + data->multi->xfer_ulbuf_len = data->set.upload_buffer_size; + } + + data->multi->xfer_ulbuf_borrowed = TRUE; + *pbuf = data->multi->xfer_ulbuf; + *pbuflen = data->multi->xfer_ulbuf_len; + return CURLE_OK; +} + +void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf) +{ + (void)buf; + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + DEBUGASSERT(!buf || data->multi->xfer_ulbuf == buf); + data->multi->xfer_ulbuf_borrowed = FALSE; +} + +static void multi_xfer_bufs_free(struct Curl_multi *multi) +{ + DEBUGASSERT(multi); + Curl_safefree(multi->xfer_buf); + multi->xfer_buf_len = 0; + multi->xfer_buf_borrowed = FALSE; + Curl_safefree(multi->xfer_ulbuf); + multi->xfer_ulbuf_len = 0; + multi->xfer_ulbuf_borrowed = FALSE; +} diff --git a/third_party/curl/lib/multihandle.h b/third_party/curl/lib/multihandle.h new file mode 100644 index 0000000000..9b80beab9d --- /dev/null +++ b/third_party/curl/lib/multihandle.h @@ -0,0 +1,191 @@ +#ifndef HEADER_CURL_MULTIHANDLE_H +#define HEADER_CURL_MULTIHANDLE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "llist.h" +#include "hash.h" +#include "conncache.h" +#include "psl.h" +#include "socketpair.h" + +struct connectdata; + +struct Curl_message { + struct Curl_llist_element list; + /* the 'CURLMsg' is the part that is visible to the external user */ + struct CURLMsg extmsg; +}; + +/* NOTE: if you add a state here, add the name to the statename[] array as + well! +*/ +typedef enum { + MSTATE_INIT, /* 0 - start in this state */ + MSTATE_PENDING, /* 1 - no connections, waiting for one */ + MSTATE_SETUP, /* 2 - start a new transfer */ + MSTATE_CONNECT, /* 3 - resolve/connect has been sent off */ + MSTATE_RESOLVING, /* 4 - awaiting the resolve to finalize */ + MSTATE_CONNECTING, /* 5 - awaiting the TCP connect to finalize */ + MSTATE_TUNNELING, /* 6 - awaiting HTTPS proxy SSL initialization to + complete and/or proxy CONNECT to finalize */ + MSTATE_PROTOCONNECT, /* 7 - initiate protocol connect procedure */ + MSTATE_PROTOCONNECTING, /* 8 - completing the protocol-specific connect + phase */ + MSTATE_DO, /* 9 - start send off the request (part 1) */ + MSTATE_DOING, /* 10 - sending off the request (part 1) */ + MSTATE_DOING_MORE, /* 11 - send off the request (part 2) */ + MSTATE_DID, /* 12 - done sending off request */ + MSTATE_PERFORMING, /* 13 - transfer data */ + MSTATE_RATELIMITING, /* 14 - wait because limit-rate exceeded */ + MSTATE_DONE, /* 15 - post data transfer operation */ + MSTATE_COMPLETED, /* 16 - operation complete */ + MSTATE_MSGSENT, /* 17 - the operation complete message is sent */ + MSTATE_LAST /* 18 - not a true state, never use this */ +} CURLMstate; + +/* we support N sockets per easy handle. Set the corresponding bit to what + action we should wait for */ +#define MAX_SOCKSPEREASYHANDLE 5 +#define GETSOCK_READABLE (0x00ff) +#define GETSOCK_WRITABLE (0xff00) + +#define CURLPIPE_ANY (CURLPIPE_MULTIPLEX) + +#if !defined(CURL_DISABLE_SOCKETPAIR) +#define ENABLE_WAKEUP +#endif + +/* value for MAXIMUM CONCURRENT STREAMS upper limit */ +#define INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) + +/* This is the struct known as CURLM on the outside */ +struct Curl_multi { + /* First a simple identifier to easier detect if a user mix up + this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */ + unsigned int magic; + + /* We have a doubly-linked list with easy handles */ + struct Curl_easy *easyp; + struct Curl_easy *easylp; /* last node */ + + unsigned int num_easy; /* amount of entries in the linked list above. */ + unsigned int num_alive; /* amount of easy handles that are added but have + not yet reached COMPLETE state */ + + struct Curl_llist msglist; /* a list of messages from completed transfers */ + + struct Curl_llist pending; /* Curl_easys that are in the + MSTATE_PENDING state */ + struct Curl_llist msgsent; /* Curl_easys that are in the + MSTATE_MSGSENT state */ + + /* callback function and user data pointer for the *socket() API */ + curl_socket_callback socket_cb; + void *socket_userp; + + /* callback function and user data pointer for server push */ + curl_push_callback push_cb; + void *push_userp; + + /* Hostname cache */ + struct Curl_hash hostcache; + +#ifdef USE_LIBPSL + /* PSL cache. */ + struct PslCache psl; +#endif + + /* timetree points to the splay-tree of time nodes to figure out expire + times of all currently set timers */ + struct Curl_tree *timetree; + + /* buffer used for transfer data, lazy initialized */ + char *xfer_buf; /* the actual buffer */ + size_t xfer_buf_len; /* the allocated length */ + /* buffer used for upload data, lazy initialized */ + char *xfer_ulbuf; /* the actual buffer */ + size_t xfer_ulbuf_len; /* the allocated length */ + + /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note + the pluralis form, there can be more than one easy handle waiting on the + same actual socket) */ + struct Curl_hash sockhash; + /* `proto_hash` is a general key-value store for protocol implementations + * with the lifetime of the multi handle. The number of elements kept here + * should be in the order of supported protocols (and sub-protocols like + * TLS), *not* in the order of connections or current transfers! + * Elements need to be added with their own destructor to be invoked when + * the multi handle is cleaned up (see Curl_hash_add2()).*/ + struct Curl_hash proto_hash; + + /* Shared connection cache (bundles)*/ + struct conncache conn_cache; + + long max_host_connections; /* if >0, a fixed limit of the maximum number + of connections per host */ + + long max_total_connections; /* if >0, a fixed limit of the maximum number + of connections in total */ + long max_shutdown_connections; /* if >0, a fixed limit of the maximum number + of connections in shutdown handling */ + + /* timer callback and user data pointer for the *socket() API */ + curl_multi_timer_callback timer_cb; + void *timer_userp; + struct curltime timer_lastcall; /* the fixed time for the timeout for the + previous callback */ +#ifdef USE_WINSOCK + WSAEVENT wsa_event; /* winsock event used for waits */ +#else +#ifdef ENABLE_WAKEUP + curl_socket_t wakeup_pair[2]; /* eventfd()/pipe()/socketpair() used for + wakeup 0 is used for read, 1 is used + for write */ +#endif +#endif + unsigned int max_concurrent_streams; + unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of + entries we are allowed to grow the connection + cache to */ +#define IPV6_UNKNOWN 0 +#define IPV6_DEAD 1 +#define IPV6_WORKS 2 + unsigned char ipv6_up; /* IPV6_* defined */ + BIT(multiplexing); /* multiplexing wanted */ + BIT(recheckstate); /* see Curl_multi_connchanged */ + BIT(in_callback); /* true while executing a callback */ +#ifdef USE_OPENSSL + BIT(ssl_seeded); +#endif + BIT(dead); /* a callback returned error, everything needs to crash and + burn */ + BIT(xfer_buf_borrowed); /* xfer_buf is currently being borrowed */ + BIT(xfer_ulbuf_borrowed); /* xfer_ulbuf is currently being borrowed */ +#ifdef DEBUGBUILD + BIT(warned); /* true after user warned of DEBUGBUILD */ +#endif +}; + +#endif /* HEADER_CURL_MULTIHANDLE_H */ diff --git a/third_party/curl/lib/multiif.h b/third_party/curl/lib/multiif.h new file mode 100644 index 0000000000..d3c12baee5 --- /dev/null +++ b/third_party/curl/lib/multiif.h @@ -0,0 +1,156 @@ +#ifndef HEADER_CURL_MULTIIF_H +#define HEADER_CURL_MULTIIF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Prototypes for library-wide functions provided by multi.c + */ + +CURLcode Curl_updatesocket(struct Curl_easy *data); +void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id); +void Curl_expire_clear(struct Curl_easy *data); +void Curl_expire_done(struct Curl_easy *data, expire_id id); +CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; +void Curl_attach_connection(struct Curl_easy *data, + struct connectdata *conn); +void Curl_detach_connection(struct Curl_easy *data); +bool Curl_multiplex_wanted(const struct Curl_multi *multi); +void Curl_set_in_callback(struct Curl_easy *data, bool value); +bool Curl_is_in_callback(struct Curl_easy *data); +CURLcode Curl_preconnect(struct Curl_easy *data); + +void Curl_multi_connchanged(struct Curl_multi *multi); + +/* Internal version of curl_multi_init() accepts size parameters for the + socket, connection and dns hashes */ +struct Curl_multi *Curl_multi_handle(size_t hashsize, + size_t chashsize, + size_t dnssize); + +/* the write bits start at bit 16 for the *getsock() bitmap */ +#define GETSOCK_WRITEBITSTART 16 + +#define GETSOCK_BLANK 0 /* no bits set */ + +/* set the bit for the given sock number to make the bitmap for writable */ +#define GETSOCK_WRITESOCK(x) (1 << (GETSOCK_WRITEBITSTART + (x))) + +/* set the bit for the given sock number to make the bitmap for readable */ +#define GETSOCK_READSOCK(x) (1 << (x)) + +/* mask for checking if read and/or write is set for index x */ +#define GETSOCK_MASK_RW(x) (GETSOCK_READSOCK(x)|GETSOCK_WRITESOCK(x)) + +/* Return the value of the CURLMOPT_MAX_HOST_CONNECTIONS option */ +size_t Curl_multi_max_host_connections(struct Curl_multi *multi); + +/* Return the value of the CURLMOPT_MAX_TOTAL_CONNECTIONS option */ +size_t Curl_multi_max_total_connections(struct Curl_multi *multi); + +void Curl_multiuse_state(struct Curl_easy *data, + int bundlestate); /* use BUNDLE_* defines */ + +/* + * Curl_multi_closed() + * + * Used by the connect code to tell the multi_socket code that one of the + * sockets we were using is about to be closed. This function will then + * remove it from the sockethash for this handle to make the multi_socket API + * behave properly, especially for the case when libcurl will create another + * socket again and it gets the same file descriptor number. + */ + +void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s); + +/* Compare the two pollsets to notify the multi_socket API of changes + * in socket polling, e.g calling multi->socket_cb() with the changes if + * differences are seen. + */ +CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi, + struct Curl_easy *data, + struct easy_pollset *ps, + struct easy_pollset *last_ps); + +/* + * Add a handle and move it into PERFORM state at once. For pushed streams. + */ +CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, + struct Curl_easy *data, + struct connectdata *conn); + + +/* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */ +unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi); + +/** + * Borrow the transfer buffer from the multi, suitable + * for the given transfer `data`. The buffer may only be used in one + * multi processing of the easy handle. It MUST be returned to the + * multi before it can be borrowed again. + * Pointers into the buffer remain only valid as long as it is borrowed. + * + * @param data the easy handle + * @param pbuf on return, the buffer to use or NULL on error + * @param pbuflen on return, the size of *pbuf or 0 on error + * @return CURLE_OK when buffer is available and is returned. + * CURLE_OUT_OF_MEMORy on failure to allocate the buffer, + * CURLE_FAILED_INIT if the easy handle is without multi. + * CURLE_AGAIN if the buffer is borrowed already. + */ +CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data, + char **pbuf, size_t *pbuflen); +/** + * Release the borrowed buffer. All references into the buffer become + * invalid after this. + * @param buf the buffer pointer borrowed for coding error checks. + */ +void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf); + +/** + * Borrow the upload buffer from the multi, suitable + * for the given transfer `data`. The buffer may only be used in one + * multi processing of the easy handle. It MUST be returned to the + * multi before it can be borrowed again. + * Pointers into the buffer remain only valid as long as it is borrowed. + * + * @param data the easy handle + * @param pbuf on return, the buffer to use or NULL on error + * @param pbuflen on return, the size of *pbuf or 0 on error + * @return CURLE_OK when buffer is available and is returned. + * CURLE_OUT_OF_MEMORy on failure to allocate the buffer, + * CURLE_FAILED_INIT if the easy handle is without multi. + * CURLE_AGAIN if the buffer is borrowed already. + */ +CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data, + char **pbuf, size_t *pbuflen); + +/** + * Release the borrowed upload buffer. All references into the buffer become + * invalid after this. + * @param buf the upload buffer pointer borrowed for coding error checks. + */ +void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf); + +#endif /* HEADER_CURL_MULTIIF_H */ diff --git a/third_party/curl/lib/netrc.c b/third_party/curl/lib/netrc.c new file mode 100644 index 0000000000..571fc73599 --- /dev/null +++ b/third_party/curl/lib/netrc.c @@ -0,0 +1,353 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#ifndef CURL_DISABLE_NETRC + +#ifdef HAVE_PWD_H +#include +#endif + +#include +#include "netrc.h" +#include "strtok.h" +#include "strcase.h" +#include "curl_get_line.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* Get user and password from .netrc when given a machine name */ + +enum host_lookup_state { + NOTHING, + HOSTFOUND, /* the 'machine' keyword was found */ + HOSTVALID, /* this is "our" machine! */ + MACDEF +}; + +#define NETRC_FILE_MISSING 1 +#define NETRC_FAILED -1 +#define NETRC_SUCCESS 0 + +#define MAX_NETRC_LINE 4096 + +/* + * Returns zero on success. + */ +static int parsenetrc(const char *host, + char **loginp, + char **passwordp, + char *netrcfile) +{ + FILE *file; + int retcode = NETRC_FILE_MISSING; + char *login = *loginp; + char *password = *passwordp; + bool specific_login = (login && *login != 0); + bool login_alloc = FALSE; + bool password_alloc = FALSE; + enum host_lookup_state state = NOTHING; + + char state_login = 0; /* Found a login keyword */ + char state_password = 0; /* Found a password keyword */ + int state_our_login = TRUE; /* With specific_login, found *our* login + name (or login-less line) */ + + DEBUGASSERT(netrcfile); + + file = fopen(netrcfile, FOPEN_READTEXT); + if(file) { + bool done = FALSE; + struct dynbuf buf; + Curl_dyn_init(&buf, MAX_NETRC_LINE); + + while(!done && Curl_get_line(&buf, file)) { + char *tok; + char *tok_end; + bool quoted; + char *netrcbuffer = Curl_dyn_ptr(&buf); + if(state == MACDEF) { + if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r')) + state = NOTHING; + else + continue; + } + tok = netrcbuffer; + while(tok) { + while(ISBLANK(*tok)) + tok++; + /* tok is first non-space letter */ + if(!*tok || (*tok == '#')) + /* end of line or the rest is a comment */ + break; + + /* leading double-quote means quoted string */ + quoted = (*tok == '\"'); + + tok_end = tok; + if(!quoted) { + while(!ISSPACE(*tok_end)) + tok_end++; + *tok_end = 0; + } + else { + bool escape = FALSE; + bool endquote = FALSE; + char *store = tok; + tok_end++; /* pass the leading quote */ + while(*tok_end) { + char s = *tok_end; + if(escape) { + escape = FALSE; + switch(s) { + case 'n': + s = '\n'; + break; + case 'r': + s = '\r'; + break; + case 't': + s = '\t'; + break; + } + } + else if(s == '\\') { + escape = TRUE; + tok_end++; + continue; + } + else if(s == '\"') { + tok_end++; /* pass the ending quote */ + endquote = TRUE; + break; + } + *store++ = s; + tok_end++; + } + *store = 0; + if(escape || !endquote) { + /* bad syntax, get out */ + retcode = NETRC_FAILED; + goto out; + } + } + + if((login && *login) && (password && *password)) { + done = TRUE; + break; + } + + switch(state) { + case NOTHING: + if(strcasecompare("macdef", tok)) { + /* Define a macro. A macro is defined with the specified name; its + contents begin with the next .netrc line and continue until a + null line (consecutive new-line characters) is encountered. */ + state = MACDEF; + } + else if(strcasecompare("machine", tok)) { + /* the next tok is the machine name, this is in itself the + delimiter that starts the stuff entered for this machine, + after this we need to search for 'login' and + 'password'. */ + state = HOSTFOUND; + } + else if(strcasecompare("default", tok)) { + state = HOSTVALID; + retcode = NETRC_SUCCESS; /* we did find our host */ + } + break; + case MACDEF: + if(!strlen(tok)) { + state = NOTHING; + } + break; + case HOSTFOUND: + if(strcasecompare(host, tok)) { + /* and yes, this is our host! */ + state = HOSTVALID; + retcode = NETRC_SUCCESS; /* we did find our host */ + } + else + /* not our host */ + state = NOTHING; + break; + case HOSTVALID: + /* we are now parsing sub-keywords concerning "our" host */ + if(state_login) { + if(specific_login) { + state_our_login = !Curl_timestrcmp(login, tok); + } + else if(!login || Curl_timestrcmp(login, tok)) { + if(login_alloc) { + free(login); + login_alloc = FALSE; + } + login = strdup(tok); + if(!login) { + retcode = NETRC_FAILED; /* allocation failed */ + goto out; + } + login_alloc = TRUE; + } + state_login = 0; + } + else if(state_password) { + if((state_our_login || !specific_login) + && (!password || Curl_timestrcmp(password, tok))) { + if(password_alloc) { + free(password); + password_alloc = FALSE; + } + password = strdup(tok); + if(!password) { + retcode = NETRC_FAILED; /* allocation failed */ + goto out; + } + password_alloc = TRUE; + } + state_password = 0; + } + else if(strcasecompare("login", tok)) + state_login = 1; + else if(strcasecompare("password", tok)) + state_password = 1; + else if(strcasecompare("machine", tok)) { + /* ok, there is machine here go => */ + state = HOSTFOUND; + state_our_login = FALSE; + } + break; + } /* switch (state) */ + tok = ++tok_end; + } + } /* while Curl_get_line() */ + +out: + Curl_dyn_free(&buf); + if(!retcode) { + /* success */ + if(login_alloc) { + if(*loginp) + free(*loginp); + *loginp = login; + } + if(password_alloc) { + if(*passwordp) + free(*passwordp); + *passwordp = password; + } + } + else { + if(login_alloc) + free(login); + if(password_alloc) + free(password); + } + fclose(file); + } + + return retcode; +} + +/* + * @unittest: 1304 + * + * *loginp and *passwordp MUST be allocated if they are not NULL when passed + * in. + */ +int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, + char *netrcfile) +{ + int retcode = 1; + char *filealloc = NULL; + + if(!netrcfile) { +#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) + char pwbuf[1024]; +#endif + char *home = NULL; + char *homea = curl_getenv("HOME"); /* portable environment reader */ + if(homea) { + home = homea; +#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) + } + else { + struct passwd pw, *pw_res; + if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) + && pw_res) { + home = pw.pw_dir; + } +#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID) + } + else { + struct passwd *pw; + pw = getpwuid(geteuid()); + if(pw) { + home = pw->pw_dir; + } +#elif defined(_WIN32) + } + else { + homea = curl_getenv("USERPROFILE"); + if(homea) { + home = homea; + } +#endif + } + + if(!home) + return retcode; /* no home directory found (or possibly out of + memory) */ + + filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR); + if(!filealloc) { + free(homea); + return -1; + } + retcode = parsenetrc(host, loginp, passwordp, filealloc); + free(filealloc); +#ifdef _WIN32 + if(retcode == NETRC_FILE_MISSING) { + /* fallback to the old-style "_netrc" file */ + filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR); + if(!filealloc) { + free(homea); + return -1; + } + retcode = parsenetrc(host, loginp, passwordp, filealloc); + free(filealloc); + } +#endif + free(homea); + } + else + retcode = parsenetrc(host, loginp, passwordp, netrcfile); + return retcode; +} + +#endif diff --git a/third_party/curl/lib/netrc.h b/third_party/curl/lib/netrc.h new file mode 100644 index 0000000000..37c95db5e4 --- /dev/null +++ b/third_party/curl/lib/netrc.h @@ -0,0 +1,43 @@ +#ifndef HEADER_CURL_NETRC_H +#define HEADER_CURL_NETRC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#ifndef CURL_DISABLE_NETRC + +/* returns -1 on failure, 0 if the host is found, 1 is the host is not found */ +int Curl_parsenetrc(const char *host, char **loginp, + char **passwordp, char *filename); + /* Assume: (*passwordp)[0]=0, host[0] != 0. + * If (*loginp)[0] = 0, search for login and password within a machine + * section in the netrc. + * If (*loginp)[0] != 0, search for password within machine and login. + */ +#else +/* disabled */ +#define Curl_parsenetrc(a,b,c,d,e,f) 1 +#endif + +#endif /* HEADER_CURL_NETRC_H */ diff --git a/third_party/curl/lib/nonblock.c b/third_party/curl/lib/nonblock.c new file mode 100644 index 0000000000..9ee93db2ee --- /dev/null +++ b/third_party/curl/lib/nonblock.c @@ -0,0 +1,93 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef __VMS +#include +#include +#endif + +#include "nonblock.h" + +/* + * curlx_nonblock() set the given socket to either blocking or non-blocking + * mode based on the 'nonblock' boolean argument. This function is highly + * portable. + */ +int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ + int nonblock /* TRUE or FALSE */) +{ +#if defined(HAVE_FCNTL_O_NONBLOCK) + /* most recent unix versions */ + int flags; + flags = sfcntl(sockfd, F_GETFL, 0); + if(flags < 0) + return -1; + /* Check if the current file status flags have already satisfied + * the request, if so, it is no need to call fcntl() to replicate it. + */ + if(!!(flags & O_NONBLOCK) == !!nonblock) + return 0; + if(nonblock) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + return sfcntl(sockfd, F_SETFL, flags); + +#elif defined(HAVE_IOCTL_FIONBIO) + + /* older unix versions */ + int flags = nonblock ? 1 : 0; + return ioctl(sockfd, FIONBIO, &flags); + +#elif defined(HAVE_IOCTLSOCKET_FIONBIO) + + /* Windows */ + unsigned long flags = nonblock ? 1UL : 0UL; + return ioctlsocket(sockfd, (long)FIONBIO, &flags); + +#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO) + + /* Amiga */ + long flags = nonblock ? 1L : 0L; + return IoctlSocket(sockfd, FIONBIO, (char *)&flags); + +#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK) + + /* Orbis OS */ + long b = nonblock ? 1L : 0L; + return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); + +#else +# error "no non-blocking method was found/used/set" +#endif +} diff --git a/third_party/curl/lib/nonblock.h b/third_party/curl/lib/nonblock.h new file mode 100644 index 0000000000..4a1a6151f2 --- /dev/null +++ b/third_party/curl/lib/nonblock.h @@ -0,0 +1,32 @@ +#ifndef HEADER_CURL_NONBLOCK_H +#define HEADER_CURL_NONBLOCK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include /* for curl_socket_t */ + +int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ + int nonblock /* TRUE or FALSE */); + +#endif /* HEADER_CURL_NONBLOCK_H */ diff --git a/third_party/curl/lib/noproxy.c b/third_party/curl/lib/noproxy.c new file mode 100644 index 0000000000..dbfafc93eb --- /dev/null +++ b/third_party/curl/lib/noproxy.c @@ -0,0 +1,264 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_PROXY + +#include "inet_pton.h" +#include "strcase.h" +#include "noproxy.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +/* + * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the + * specified CIDR address range. + */ +UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits) +{ + unsigned int address = 0; + unsigned int check = 0; + + if(bits > 32) + /* strange input */ + return FALSE; + + if(1 != Curl_inet_pton(AF_INET, ipv4, &address)) + return FALSE; + if(1 != Curl_inet_pton(AF_INET, network, &check)) + return FALSE; + + if(bits && (bits != 32)) { + unsigned int mask = 0xffffffff << (32 - bits); + unsigned int haddr = htonl(address); + unsigned int hcheck = htonl(check); +#if 0 + fprintf(stderr, "Host %s (%x) network %s (%x) bits %u mask %x => %x\n", + ipv4, haddr, network, hcheck, bits, mask, + (haddr ^ hcheck) & mask); +#endif + if((haddr ^ hcheck) & mask) + return FALSE; + return TRUE; + } + return (address == check); +} + +UNITTEST bool Curl_cidr6_match(const char *ipv6, + const char *network, + unsigned int bits) +{ +#ifdef USE_IPV6 + unsigned int bytes; + unsigned int rest; + unsigned char address[16]; + unsigned char check[16]; + + if(!bits) + bits = 128; + + bytes = bits / 8; + rest = bits & 0x07; + if((bytes > 16) || ((bytes == 16) && rest)) + return FALSE; + if(1 != Curl_inet_pton(AF_INET6, ipv6, address)) + return FALSE; + if(1 != Curl_inet_pton(AF_INET6, network, check)) + return FALSE; + if(bytes && memcmp(address, check, bytes)) + return FALSE; + if(rest && !((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) + return FALSE; + + return TRUE; +#else + (void)ipv6; + (void)network; + (void)bits; + return FALSE; +#endif +} + +enum nametype { + TYPE_HOST, + TYPE_IPV4, + TYPE_IPV6 +}; + +/**************************************************************** +* Checks if the host is in the noproxy list. returns TRUE if it matches and +* therefore the proxy should NOT be used. +****************************************************************/ +bool Curl_check_noproxy(const char *name, const char *no_proxy) +{ + char hostip[128]; + + /* + * If we do not have a hostname at all, like for example with a FILE + * transfer, we have nothing to interrogate the noproxy list with. + */ + if(!name || name[0] == '\0') + return FALSE; + + /* no_proxy=domain1.dom,host.domain2.dom + * (a comma-separated list of hosts which should + * not be proxied, or an asterisk to override + * all proxy variables) + */ + if(no_proxy && no_proxy[0]) { + const char *p = no_proxy; + size_t namelen; + enum nametype type = TYPE_HOST; + if(!strcmp("*", no_proxy)) + return TRUE; + + /* NO_PROXY was specified and it was not just an asterisk */ + + if(name[0] == '[') { + char *endptr; + /* IPv6 numerical address */ + endptr = strchr(name, ']'); + if(!endptr) + return FALSE; + name++; + namelen = endptr - name; + if(namelen >= sizeof(hostip)) + return FALSE; + memcpy(hostip, name, namelen); + hostip[namelen] = 0; + name = hostip; + type = TYPE_IPV6; + } + else { + unsigned int address; + namelen = strlen(name); + if(1 == Curl_inet_pton(AF_INET, name, &address)) + type = TYPE_IPV4; + else { + /* ignore trailing dots in the hostname */ + if(name[namelen - 1] == '.') + namelen--; + } + } + + while(*p) { + const char *token; + size_t tokenlen = 0; + bool match = FALSE; + + /* pass blanks */ + while(*p && ISBLANK(*p)) + p++; + + token = p; + /* pass over the pattern */ + while(*p && !ISBLANK(*p) && (*p != ',')) { + p++; + tokenlen++; + } + + if(tokenlen) { + switch(type) { + case TYPE_HOST: + /* ignore trailing dots in the token to check */ + if(token[tokenlen - 1] == '.') + tokenlen--; + + if(tokenlen && (*token == '.')) { + /* ignore leading token dot as well */ + token++; + tokenlen--; + } + /* A: example.com matches 'example.com' + B: www.example.com matches 'example.com' + C: nonexample.com DOES NOT match 'example.com' + */ + if(tokenlen == namelen) + /* case A, exact match */ + match = strncasecompare(token, name, namelen); + else if(tokenlen < namelen) { + /* case B, tailmatch domain */ + match = (name[namelen - tokenlen - 1] == '.') && + strncasecompare(token, name + (namelen - tokenlen), + tokenlen); + } + /* case C passes through, not a match */ + break; + case TYPE_IPV4: + case TYPE_IPV6: { + const char *check = token; + char *slash; + unsigned int bits = 0; + char checkip[128]; + if(tokenlen >= sizeof(checkip)) + /* this cannot match */ + break; + /* copy the check name to a temp buffer */ + memcpy(checkip, check, tokenlen); + checkip[tokenlen] = 0; + check = checkip; + + slash = strchr(check, '/'); + /* if the slash is part of this token, use it */ + if(slash) { + /* if the bits variable gets a crazy value here, that is fine as + the value will then be rejected in the cidr function */ + bits = (unsigned int)atoi(slash + 1); + *slash = 0; /* null terminate there */ + } + if(type == TYPE_IPV6) + match = Curl_cidr6_match(name, check, bits); + else + match = Curl_cidr4_match(name, check, bits); + break; + } + } + if(match) + return TRUE; + } /* if(tokenlen) */ + /* pass blanks after pattern */ + while(ISBLANK(*p)) + p++; + /* if not a comma, this ends the loop */ + if(*p != ',') + break; + /* pass any number of commas */ + while(*p == ',') + p++; + } /* while(*p) */ + } /* NO_PROXY was specified and it was not just an asterisk */ + + return FALSE; +} + +#endif /* CURL_DISABLE_PROXY */ diff --git a/third_party/curl/lib/noproxy.h b/third_party/curl/lib/noproxy.h new file mode 100644 index 0000000000..71ae7eaafa --- /dev/null +++ b/third_party/curl/lib/noproxy.h @@ -0,0 +1,43 @@ +#ifndef HEADER_CURL_NOPROXY_H +#define HEADER_CURL_NOPROXY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifndef CURL_DISABLE_PROXY + +#ifdef UNITTESTS + +UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits); +UNITTEST bool Curl_cidr6_match(const char *ipv6, + const char *network, + unsigned int bits); +#endif + +bool Curl_check_noproxy(const char *name, const char *no_proxy); +#endif + +#endif /* HEADER_CURL_NOPROXY_H */ diff --git a/third_party/curl/lib/openldap.c b/third_party/curl/lib/openldap.c new file mode 100644 index 0000000000..1b35ba0d78 --- /dev/null +++ b/third_party/curl/lib/openldap.c @@ -0,0 +1,1224 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Howard Chu, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP) + +/* + * Notice that USE_OPENLDAP is only a source code selection switch. When + * libcurl is built with USE_OPENLDAP defined the libcurl source code that + * gets compiled is the code from openldap.c, otherwise the code that gets + * compiled is the code from ldap.c. + * + * When USE_OPENLDAP is defined a recent version of the OpenLDAP library + * might be required for compilation and runtime. In order to use ancient + * OpenLDAP library versions, USE_OPENLDAP shall not be defined. + */ + +#include + +#include "urldata.h" +#include +#include "sendf.h" +#include "vtls/vtls.h" +#include "transfer.h" +#include "curl_ldap.h" +#include "curl_base64.h" +#include "cfilters.h" +#include "connect.h" +#include "curl_sasl.h" +#include "strcase.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Uncommenting this will enable the built-in debug logging of the openldap + * library. The debug log level can be set using the CURL_OPENLDAP_TRACE + * environment variable. The debug output is written to stderr. + * + * The library supports the following debug flags: + * LDAP_DEBUG_NONE 0x0000 + * LDAP_DEBUG_TRACE 0x0001 + * LDAP_DEBUG_CONSTRUCT 0x0002 + * LDAP_DEBUG_DESTROY 0x0004 + * LDAP_DEBUG_PARAMETER 0x0008 + * LDAP_DEBUG_ANY 0xffff + * + * For example, use CURL_OPENLDAP_TRACE=0 for no debug, + * CURL_OPENLDAP_TRACE=2 for LDAP_DEBUG_CONSTRUCT messages only, + * CURL_OPENLDAP_TRACE=65535 for all debug message levels. + */ +/* #define CURL_OPENLDAP_DEBUG */ + +/* Machine states. */ +typedef enum { + OLDAP_STOP, /* Do nothing state, stops the state machine */ + OLDAP_SSL, /* Performing SSL handshake. */ + OLDAP_STARTTLS, /* STARTTLS request sent. */ + OLDAP_TLS, /* Performing TLS handshake. */ + OLDAP_MECHS, /* Get SASL authentication mechanisms. */ + OLDAP_SASL, /* SASL binding reply. */ + OLDAP_BIND, /* Simple bind reply. */ + OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */ + OLDAP_LAST /* Never used */ +} ldapstate; + +#ifndef _LDAP_PVT_H +extern int ldap_pvt_url_scheme2proto(const char *); +extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, + LDAP **ld); +#endif + +static CURLcode oldap_setup_connection(struct Curl_easy *data, + struct connectdata *conn); +static CURLcode oldap_do(struct Curl_easy *data, bool *done); +static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool); +static CURLcode oldap_connect(struct Curl_easy *data, bool *done); +static CURLcode oldap_connecting(struct Curl_easy *data, bool *done); +static CURLcode oldap_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead); + +static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, + const struct bufref *initresp); +static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, + const struct bufref *resp); +static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech); +static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out); + +static Curl_recv oldap_recv; + +/* + * LDAP protocol handler. + */ + +const struct Curl_handler Curl_handler_ldap = { + "ldap", /* scheme */ + oldap_setup_connection, /* setup_connection */ + oldap_do, /* do_it */ + oldap_done, /* done */ + ZERO_NULL, /* do_more */ + oldap_connect, /* connect_it */ + oldap_connecting, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + oldap_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_LDAP, /* defport */ + CURLPROTO_LDAP, /* protocol */ + CURLPROTO_LDAP, /* family */ + PROTOPT_NONE /* flags */ +}; + +#ifdef USE_SSL +/* + * LDAPS protocol handler. + */ + +const struct Curl_handler Curl_handler_ldaps = { + "ldaps", /* scheme */ + oldap_setup_connection, /* setup_connection */ + oldap_do, /* do_it */ + oldap_done, /* done */ + ZERO_NULL, /* do_more */ + oldap_connect, /* connect_it */ + oldap_connecting, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + oldap_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_LDAPS, /* defport */ + CURLPROTO_LDAPS, /* protocol */ + CURLPROTO_LDAP, /* family */ + PROTOPT_SSL /* flags */ +}; +#endif + +/* SASL parameters for the ldap protocol */ +static const struct SASLproto saslldap = { + "ldap", /* The service name */ + oldap_perform_auth, /* Send authentication command */ + oldap_continue_auth, /* Send authentication continuation */ + oldap_cancel_auth, /* Send authentication cancellation */ + oldap_get_message, /* Get SASL response message */ + 0, /* Maximum initial response length (no max) */ + LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */ + LDAP_SUCCESS, /* Code to receive upon authentication success */ + SASL_AUTH_NONE, /* Default mechanisms */ + 0 /* Configuration flags */ +}; + +struct ldapconninfo { + struct SASL sasl; /* SASL-related parameters */ + LDAP *ld; /* Openldap connection handle. */ + Curl_recv *recv; /* For stacking SSL handler */ + Curl_send *send; + struct berval *servercred; /* SASL data from server. */ + ldapstate state; /* Current machine state. */ + int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */ + int msgid; /* Current message id. */ +}; + +struct ldapreqinfo { + int msgid; + int nument; +}; + +/* + * oldap_state() + * + * This is the ONLY way to change LDAP state! + */ +static void oldap_state(struct Curl_easy *data, ldapstate newstate) +{ + struct ldapconninfo *ldapc = data->conn->proto.ldapc; + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char * const names[] = { + "STOP", + "SSL", + "STARTTLS", + "TLS", + "MECHS", + "SASL", + "BIND", + "BINDV2", + /* LAST */ + }; + + if(ldapc->state != newstate) + infof(data, "LDAP %p state change from %s to %s", + (void *)ldapc, names[ldapc->state], names[newstate]); +#endif + + ldapc->state = newstate; +} + +/* Map some particular LDAP error codes to CURLcode values. */ +static CURLcode oldap_map_error(int rc, CURLcode result) +{ + switch(rc) { + case LDAP_NO_MEMORY: + result = CURLE_OUT_OF_MEMORY; + break; + case LDAP_INVALID_CREDENTIALS: + result = CURLE_LOGIN_DENIED; + break; + case LDAP_PROTOCOL_ERROR: + result = CURLE_UNSUPPORTED_PROTOCOL; + break; + case LDAP_INSUFFICIENT_ACCESS: + result = CURLE_REMOTE_ACCESS_DENIED; + break; + } + return result; +} + +static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp) +{ + CURLcode result = CURLE_OK; + int rc = LDAP_URL_ERR_BADURL; + static const char * const url_errs[] = { + "success", + "out of memory", + "bad parameter", + "unrecognized scheme", + "unbalanced delimiter", + "bad URL", + "bad host or port", + "bad or missing attributes", + "bad or missing scope", + "bad or missing filter", + "bad or missing extensions" + }; + + *ludp = NULL; + if(!data->state.up.user && !data->state.up.password && + !data->state.up.options) + rc = ldap_url_parse(data->state.url, ludp); + if(rc != LDAP_URL_SUCCESS) { + const char *msg = "url parsing problem"; + + result = rc == LDAP_URL_ERR_MEM? CURLE_OUT_OF_MEMORY: CURLE_URL_MALFORMAT; + rc -= LDAP_URL_SUCCESS; + if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0])) + msg = url_errs[rc]; + failf(data, "LDAP local: %s", msg); + } + return result; +} + +/* Parse the login options. */ +static CURLcode oldap_parse_login_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct ldapconninfo *li = conn->proto.ldapc; + const char *ptr = conn->options; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(checkprefix("AUTH=", key)) + result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value); + else + result = CURLE_SETOPT_OPTION_SYNTAX; + + if(*ptr == ';') + ptr++; + } + + return result == CURLE_URL_MALFORMAT? CURLE_SETOPT_OPTION_SYNTAX: result; +} + +static CURLcode oldap_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result; + LDAPURLDesc *lud; + (void)conn; + + /* Early URL syntax check. */ + result = oldap_url_parse(data, &lud); + ldap_free_urldesc(lud); + + return result; +} + +/* + * Get the SASL authentication challenge from the server credential buffer. + */ +static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out) +{ + struct berval *servercred = data->conn->proto.ldapc->servercred; + + if(!servercred || !servercred->bv_val) + return CURLE_WEIRD_SERVER_REPLY; + Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL); + return CURLE_OK; +} + +/* + * Sends an initial SASL bind request to the server. + */ +static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, + const struct bufref *initresp) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode result = CURLE_OK; + struct berval cred; + struct berval *pcred = &cred; + int rc; + + cred.bv_val = (char *) Curl_bufref_ptr(initresp); + cred.bv_len = Curl_bufref_len(initresp); + if(!cred.bv_val) + pcred = NULL; + rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid); + if(rc != LDAP_SUCCESS) + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); + return result; +} + +/* + * Sends SASL continuation. + */ +static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, + const struct bufref *resp) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode result = CURLE_OK; + struct berval cred; + struct berval *pcred = &cred; + int rc; + + cred.bv_val = (char *) Curl_bufref_ptr(resp); + cred.bv_len = Curl_bufref_len(resp); + if(!cred.bv_val) + pcred = NULL; + rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid); + if(rc != LDAP_SUCCESS) + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); + return result; +} + +/* + * Sends SASL bind cancellation. + */ +static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech) +{ + struct ldapconninfo *li = data->conn->proto.ldapc; + CURLcode result = CURLE_OK; + int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL, + &li->msgid); + + (void)mech; + if(rc != LDAP_SUCCESS) + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); + return result; +} + +/* Starts LDAP simple bind. */ +static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + char *binddn = NULL; + struct berval passwd; + int rc; + + passwd.bv_val = NULL; + passwd.bv_len = 0; + + if(data->state.aptr.user) { + binddn = conn->user; + passwd.bv_val = conn->passwd; + passwd.bv_len = strlen(passwd.bv_val); + } + + rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd, + NULL, NULL, &li->msgid); + if(rc == LDAP_SUCCESS) + oldap_state(data, newstate); + else + result = oldap_map_error(rc, + data->state.aptr.user? + CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND); + return result; +} + +/* Query the supported SASL authentication mechanisms. */ +static CURLcode oldap_perform_mechs(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct ldapconninfo *li = data->conn->proto.ldapc; + int rc; + static const char * const supportedSASLMechanisms[] = { + "supportedSASLMechanisms", + NULL + }; + + rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", + (char **) supportedSASLMechanisms, 0, + NULL, NULL, NULL, 0, &li->msgid); + if(rc == LDAP_SUCCESS) + oldap_state(data, OLDAP_MECHS); + else + result = oldap_map_error(rc, CURLE_LOGIN_DENIED); + return result; +} + +/* Starts SASL bind. */ +static CURLcode oldap_perform_sasl(struct Curl_easy *data) +{ + saslprogress progress = SASL_IDLE; + struct ldapconninfo *li = data->conn->proto.ldapc; + CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress); + + oldap_state(data, OLDAP_SASL); + if(!result && progress != SASL_INPROGRESS) + result = CURLE_LOGIN_DENIED; + return result; +} + +#ifdef USE_SSL +static Sockbuf_IO ldapsb_tls; + +static bool ssl_installed(struct connectdata *conn) +{ + return conn->proto.ldapc->recv != NULL; +} + +static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + bool ssldone = 0; + + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + if(!result) { + oldap_state(data, newstate); + + if(ssldone) { + Sockbuf *sb; + + /* Install the libcurl SSL handlers into the sockbuf. */ + ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); + ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); + li->recv = conn->recv[FIRSTSOCKET]; + li->send = conn->send[FIRSTSOCKET]; + } + } + + return result; +} + +/* Send the STARTTLS request */ +static CURLcode oldap_perform_starttls(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct ldapconninfo *li = data->conn->proto.ldapc; + int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid); + + if(rc == LDAP_SUCCESS) + oldap_state(data, OLDAP_STARTTLS); + else + result = oldap_map_error(rc, CURLE_USE_SSL_FAILED); + return result; +} +#endif + +static CURLcode oldap_connect(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li; + static const int version = LDAP_VERSION3; + int rc; + char *hosturl; +#ifdef CURL_OPENLDAP_DEBUG + static int do_trace = -1; +#endif + + (void)done; + + DEBUGASSERT(!conn->proto.ldapc); + li = calloc(1, sizeof(struct ldapconninfo)); + if(!li) + return CURLE_OUT_OF_MEMORY; + else { + CURLcode result; + li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme); + conn->proto.ldapc = li; + + /* Initialize the SASL storage */ + Curl_sasl_init(&li->sasl, data, &saslldap); + + /* Clear the TLS upgraded flag */ + conn->bits.tls_upgraded = FALSE; + + result = oldap_parse_login_options(conn); + if(result) + return result; + } + + hosturl = aprintf("%s://%s%s%s:%d", + conn->handler->scheme, + conn->bits.ipv6_ip? "[": "", + conn->host.name, + conn->bits.ipv6_ip? "]": "", + conn->remote_port); + if(!hosturl) + return CURLE_OUT_OF_MEMORY; + + rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld); + if(rc) { + failf(data, "LDAP local: Cannot connect to %s, %s", + hosturl, ldap_err2string(rc)); + free(hosturl); + return CURLE_COULDNT_CONNECT; + } + + free(hosturl); + +#ifdef CURL_OPENLDAP_DEBUG + if(do_trace < 0) { + const char *env = getenv("CURL_OPENLDAP_TRACE"); + do_trace = (env && strtol(env, NULL, 10) > 0); + } + if(do_trace) + ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace); +#endif + + /* Try version 3 first. */ + ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version); + + /* Do not chase referrals. */ + ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); + +#ifdef USE_SSL + if(conn->handler->flags & PROTOPT_SSL) + return oldap_ssl_connect(data, OLDAP_SSL); + + if(data->set.use_ssl) { + CURLcode result = oldap_perform_starttls(data); + + if(!result || data->set.use_ssl != CURLUSESSL_TRY) + return result; + } +#endif + + if(li->sasl.prefmech != SASL_AUTH_NONE) + return oldap_perform_mechs(data); + + /* Force bind even if anonymous bind is not needed in protocol version 3 + to detect missing version 3 support. */ + return oldap_perform_bind(data, OLDAP_BIND); +} + +/* Handle the supported SASL mechanisms query response */ +static CURLcode oldap_state_mechs_resp(struct Curl_easy *data, + LDAPMessage *msg, int code) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + int rc; + BerElement *ber = NULL; + CURLcode result = CURLE_OK; + struct berval bv, *bvals; + + switch(ldap_msgtype(msg)) { + case LDAP_RES_SEARCH_ENTRY: + /* Got a list of supported SASL mechanisms. */ + if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED) + return CURLE_LOGIN_DENIED; + + rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv); + if(rc < 0) + return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING); + for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals); + rc == LDAP_SUCCESS; + rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) { + int i; + + if(!bv.bv_val) + break; + + if(bvals) { + for(i = 0; bvals[i].bv_val; i++) { + size_t llen; + unsigned short mech = Curl_sasl_decode_mech((char *) bvals[i].bv_val, + bvals[i].bv_len, &llen); + if(bvals[i].bv_len == llen) + li->sasl.authmechs |= mech; + } + ber_memfree(bvals); + } + } + ber_free(ber, 0); + break; + + case LDAP_RES_SEARCH_RESULT: + switch(code) { + case LDAP_SIZELIMIT_EXCEEDED: + infof(data, "Too many authentication mechanisms\n"); + FALLTHROUGH(); + case LDAP_SUCCESS: + case LDAP_NO_RESULTS_RETURNED: + if(Curl_sasl_can_authenticate(&li->sasl, data)) + result = oldap_perform_sasl(data); + else + result = CURLE_LOGIN_DENIED; + break; + default: + result = oldap_map_error(code, CURLE_LOGIN_DENIED); + break; + } + break; + default: + break; + } + return result; +} + +/* Handle a SASL bind response. */ +static CURLcode oldap_state_sasl_resp(struct Curl_easy *data, + LDAPMessage *msg, int code) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode result = CURLE_OK; + saslprogress progress; + int rc; + + li->servercred = NULL; + rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: sasl ldap_parse_result %s", ldap_err2string(rc)); + result = oldap_map_error(rc, CURLE_LOGIN_DENIED); + } + else { + result = Curl_sasl_continue(&li->sasl, data, code, &progress); + if(!result && progress != SASL_INPROGRESS) + oldap_state(data, OLDAP_STOP); + } + + if(li->servercred) + ber_bvfree(li->servercred); + return result; +} + +/* Handle a simple bind response. */ +static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg, + int code) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode result = CURLE_OK; + struct berval *bv = NULL; + int rc; + + if(code != LDAP_SUCCESS) + return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND); + + rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s", + ldap_err2string(rc)); + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); + } + else + oldap_state(data, OLDAP_STOP); + + if(bv) + ber_bvfree(bv); + return result; +} + +static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + LDAPMessage *msg = NULL; + struct timeval tv = {0, 0}; + int code = LDAP_SUCCESS; + int rc; + + if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) { + /* Get response to last command. */ + rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg); + switch(rc) { + case 0: /* Timed out. */ + return CURLE_OK; + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + break; + default: + li->msgid = 0; /* Nothing to abandon upon error. */ + if(rc < 0) { + failf(data, "LDAP local: connecting ldap_result %s", + ldap_err2string(rc)); + return oldap_map_error(rc, CURLE_COULDNT_CONNECT); + } + break; + } + + /* Get error code from message. */ + rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0); + if(rc) + code = rc; + else { + /* store the latest code for later retrieval */ + data->info.httpcode = code; + } + + /* If protocol version 3 is not supported, fallback to version 2. */ + if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 && +#ifdef USE_SSL + (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) && +#endif + li->sasl.prefmech == SASL_AUTH_NONE) { + static const int version = LDAP_VERSION2; + + ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version); + ldap_msgfree(msg); + return oldap_perform_bind(data, OLDAP_BINDV2); + } + } + + /* Handle response message according to current state. */ + switch(li->state) { + +#ifdef USE_SSL + case OLDAP_SSL: + result = oldap_ssl_connect(data, OLDAP_SSL); + if(!result && ssl_installed(conn)) { + if(li->sasl.prefmech != SASL_AUTH_NONE) + result = oldap_perform_mechs(data); + else + result = oldap_perform_bind(data, OLDAP_BIND); + } + break; + case OLDAP_STARTTLS: + if(code != LDAP_SUCCESS) { + if(data->set.use_ssl != CURLUSESSL_TRY) + result = oldap_map_error(code, CURLE_USE_SSL_FAILED); + else if(li->sasl.prefmech != SASL_AUTH_NONE) + result = oldap_perform_mechs(data); + else + result = oldap_perform_bind(data, OLDAP_BIND); + break; + } + result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + if(result) + break; + FALLTHROUGH(); + case OLDAP_TLS: + result = oldap_ssl_connect(data, OLDAP_TLS); + if(result) + result = oldap_map_error(code, CURLE_USE_SSL_FAILED); + else if(ssl_installed(conn)) { + conn->bits.tls_upgraded = TRUE; + if(li->sasl.prefmech != SASL_AUTH_NONE) + result = oldap_perform_mechs(data); + else if(data->state.aptr.user) + result = oldap_perform_bind(data, OLDAP_BIND); + else { + /* Version 3 supported: no bind required */ + oldap_state(data, OLDAP_STOP); + result = CURLE_OK; + } + } + break; +#endif + + case OLDAP_MECHS: + result = oldap_state_mechs_resp(data, msg, code); + break; + case OLDAP_SASL: + result = oldap_state_sasl_resp(data, msg, code); + break; + case OLDAP_BIND: + case OLDAP_BINDV2: + result = oldap_state_bind_resp(data, msg, code); + break; + default: + /* internal error */ + result = CURLE_COULDNT_CONNECT; + break; + } + + ldap_msgfree(msg); + + *done = li->state == OLDAP_STOP; + if(*done) + conn->recv[FIRSTSOCKET] = oldap_recv; + + if(result && li->msgid) { + ldap_abandon_ext(li->ld, li->msgid, NULL, NULL); + li->msgid = 0; + } + return result; +} + +static CURLcode oldap_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + struct ldapconninfo *li = conn->proto.ldapc; + (void) dead_connection; +#ifndef USE_SSL + (void)data; +#endif + + if(li) { + if(li->ld) { +#ifdef USE_SSL + if(ssl_installed(conn)) { + Sockbuf *sb; + ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); + ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); + } +#endif + ldap_unbind_ext(li->ld, NULL, NULL); + li->ld = NULL; + } + Curl_sasl_cleanup(conn, li->sasl.authused); + conn->proto.ldapc = NULL; + free(li); + } + return CURLE_OK; +} + +static CURLcode oldap_do(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + struct ldapreqinfo *lr; + CURLcode result; + int rc; + LDAPURLDesc *lud; + int msgid; + + connkeep(conn, "OpenLDAP do"); + + infof(data, "LDAP local: %s", data->state.url); + + result = oldap_url_parse(data, &lud); + if(!result) { +#ifdef USE_SSL + if(ssl_installed(conn)) { + Sockbuf *sb; + /* re-install the libcurl SSL handlers into the sockbuf. */ + ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); + ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); + } +#endif + + rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope, + lud->lud_filter, lud->lud_attrs, 0, + NULL, NULL, NULL, 0, &msgid); + ldap_free_urldesc(lud); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc)); + result = CURLE_LDAP_SEARCH_FAILED; + } + else { + lr = calloc(1, sizeof(struct ldapreqinfo)); + if(!lr) { + ldap_abandon_ext(li->ld, msgid, NULL, NULL); + result = CURLE_OUT_OF_MEMORY; + } + else { + lr->msgid = msgid; + data->req.p.ldap = lr; + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); + *done = TRUE; + } + } + } + return result; +} + +static CURLcode oldap_done(struct Curl_easy *data, CURLcode res, + bool premature) +{ + struct connectdata *conn = data->conn; + struct ldapreqinfo *lr = data->req.p.ldap; + + (void)res; + (void)premature; + + if(lr) { + /* if there was a search in progress, abandon it */ + if(lr->msgid) { + struct ldapconninfo *li = conn->proto.ldapc; + ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL); + lr->msgid = 0; + } + data->req.p.ldap = NULL; + free(lr); + } + + return CURLE_OK; +} + +static CURLcode client_write(struct Curl_easy *data, + const char *prefix, size_t plen, + const char *value, size_t len, + const char *suffix, size_t slen) +{ + CURLcode result = CURLE_OK; + + if(prefix) { + /* If we have a zero-length value and the prefix ends with a space + separator, drop the latter. */ + if(!len && plen && prefix[plen - 1] == ' ') + plen--; + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen); + } + if(!result && value) { + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len); + } + if(!result && suffix) { + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen); + } + return result; +} + +static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, + size_t len, CURLcode *err) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + struct ldapreqinfo *lr = data->req.p.ldap; + int rc; + LDAPMessage *msg = NULL; + BerElement *ber = NULL; + struct timeval tv = {0, 0}; + struct berval bv, *bvals; + int binary = 0; + CURLcode result = CURLE_AGAIN; + int code; + char *info = NULL; + + (void)len; + (void)buf; + (void)sockindex; + + rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg); + if(rc < 0) { + failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc)); + result = CURLE_RECV_ERROR; + } + + *err = result; + + /* error or timed out */ + if(!msg) + return -1; + + result = CURLE_OK; + + switch(ldap_msgtype(msg)) { + case LDAP_RES_SEARCH_RESULT: + lr->msgid = 0; + rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0); + if(rc) { + failf(data, "LDAP local: search ldap_parse_result %s", + ldap_err2string(rc)); + result = CURLE_LDAP_SEARCH_FAILED; + break; + } + + /* store the latest code for later retrieval */ + data->info.httpcode = code; + + switch(code) { + case LDAP_SIZELIMIT_EXCEEDED: + infof(data, "There are more than %d entries", lr->nument); + FALLTHROUGH(); + case LDAP_SUCCESS: + data->req.size = data->req.bytecount; + break; + default: + failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code), + info ? info : ""); + result = CURLE_LDAP_SEARCH_FAILED; + break; + } + if(info) + ldap_memfree(info); + break; + case LDAP_RES_SEARCH_ENTRY: + lr->nument++; + rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv); + if(rc < 0) { + result = CURLE_RECV_ERROR; + break; + } + + result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len, + STRCONST("\n")); + if(result) + break; + + for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals); + rc == LDAP_SUCCESS; + rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) { + int i; + + if(!bv.bv_val) + break; + + if(!bvals) { + result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, + STRCONST(":\n")); + if(result) + break; + continue; + } + + binary = bv.bv_len > 7 && + !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7); + + for(i = 0; bvals[i].bv_val != NULL; i++) { + int binval = 0; + + result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, + STRCONST(":")); + if(result) + break; + + if(!binary) { + /* check for leading or trailing whitespace */ + if(ISBLANK(bvals[i].bv_val[0]) || + ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1])) + binval = 1; + else { + /* check for unprintable characters */ + unsigned int j; + for(j = 0; j < bvals[i].bv_len; j++) + if(!ISPRINT(bvals[i].bv_val[j])) { + binval = 1; + break; + } + } + } + if(binary || binval) { + char *val_b64 = NULL; + size_t val_b64_sz = 0; + + /* Binary value, encode to base64. */ + if(bvals[i].bv_len) + result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len, + &val_b64, &val_b64_sz); + if(!result) + result = client_write(data, STRCONST(": "), val_b64, val_b64_sz, + STRCONST("\n")); + free(val_b64); + } + else + result = client_write(data, STRCONST(" "), + bvals[i].bv_val, bvals[i].bv_len, + STRCONST("\n")); + if(result) + break; + } + + ber_memfree(bvals); + bvals = NULL; + if(!result) + result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); + if(result) + break; + } + + ber_free(ber, 0); + + if(!result) + result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); + if(!result) + result = CURLE_AGAIN; + break; + } + + ldap_msgfree(msg); + *err = result; + return result? -1: 0; +} + +#ifdef USE_SSL +static int +ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg) +{ + sbiod->sbiod_pvt = arg; + return 0; +} + +static int +ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod) +{ + sbiod->sbiod_pvt = NULL; + return 0; +} + +/* We do not need to do anything because libcurl does it already */ +static int +ldapsb_tls_close(Sockbuf_IO_Desc *sbiod) +{ + (void)sbiod; + return 0; +} + +static int +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_conn_data_pending(data, FIRSTSOCKET); + } + return 0; +} + +static ber_slen_t +ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct Curl_easy *data = sbiod->sbiod_pvt; + ber_slen_t ret = 0; + if(data) { + struct connectdata *conn = data->conn; + if(conn) { + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode err = CURLE_RECV_ERROR; + + ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err); + if(ret < 0 && err == CURLE_AGAIN) { + SET_SOCKERRNO(EWOULDBLOCK); + } + } + } + return ret; +} + +static ber_slen_t +ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct Curl_easy *data = sbiod->sbiod_pvt; + ber_slen_t ret = 0; + if(data) { + struct connectdata *conn = data->conn; + if(conn) { + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode err = CURLE_SEND_ERROR; + ret = (li->send)(data, FIRSTSOCKET, buf, len, &err); + if(ret < 0 && err == CURLE_AGAIN) { + SET_SOCKERRNO(EWOULDBLOCK); + } + } + } + return ret; +} + +static Sockbuf_IO ldapsb_tls = +{ + ldapsb_tls_setup, + ldapsb_tls_remove, + ldapsb_tls_ctrl, + ldapsb_tls_read, + ldapsb_tls_write, + ldapsb_tls_close +}; +#endif /* USE_SSL */ + +#endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */ diff --git a/third_party/curl/lib/parsedate.c b/third_party/curl/lib/parsedate.c new file mode 100644 index 0000000000..d35b58b0d4 --- /dev/null +++ b/third_party/curl/lib/parsedate.c @@ -0,0 +1,644 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* + A brief summary of the date string formats this parser groks: + + RFC 2616 3.3.1 + + Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + + we support dates without week day name: + + 06 Nov 1994 08:49:37 GMT + 06-Nov-94 08:49:37 GMT + Nov 6 08:49:37 1994 + + without the time zone: + + 06 Nov 1994 08:49:37 + 06-Nov-94 08:49:37 + + weird order: + + 1994 Nov 6 08:49:37 (GNU date fails) + GMT 08:49:37 06-Nov-94 Sunday + 94 6 Nov 08:49:37 (GNU date fails) + + time left out: + + 1994 Nov 6 + 06-Nov-94 + Sun Nov 6 94 + + unusual separators: + + 1994.Nov.6 + Sun/Nov/6/94/GMT + + commonly used time zone names: + + Sun, 06 Nov 1994 08:49:37 CET + 06 Nov 1994 08:49:37 EST + + time zones specified using RFC822 style: + + Sun, 12 Sep 2004 15:05:58 -0700 + Sat, 11 Sep 2004 21:32:11 +0200 + + compact numerical date strings: + + 20040912 15:05:58 -0700 + 20040911 +0200 + +*/ + +#include "curl_setup.h" + +#include + +#include +#include "strcase.h" +#include "warnless.h" +#include "parsedate.h" + +/* + * parsedate() + * + * Returns: + * + * PARSEDATE_OK - a fine conversion + * PARSEDATE_FAIL - failed to convert + * PARSEDATE_LATER - time overflow at the far end of time_t + * PARSEDATE_SOONER - time underflow at the low end of time_t + */ + +static int parsedate(const char *date, time_t *output); + +#define PARSEDATE_OK 0 +#define PARSEDATE_FAIL -1 +#define PARSEDATE_LATER 1 +#define PARSEDATE_SOONER 2 + +#if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \ + !defined(CURL_DISABLE_FILE) +/* These names are also used by FTP and FILE code */ +const char * const Curl_wkday[] = +{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; +const char * const Curl_month[]= +{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +#endif + +#ifndef CURL_DISABLE_PARSEDATE +static const char * const weekday[] = +{ "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday", "Sunday" }; + +struct tzinfo { + char name[5]; + int offset; /* +/- in minutes */ +}; + +/* Here's a bunch of frequently used time zone names. These were supported + by the old getdate parser. */ +#define tDAYZONE -60 /* offset for daylight savings time */ +static const struct tzinfo tz[]= { + {"GMT", 0}, /* Greenwich Mean */ + {"UT", 0}, /* Universal Time */ + {"UTC", 0}, /* Universal (Coordinated) */ + {"WET", 0}, /* Western European */ + {"BST", 0 tDAYZONE}, /* British Summer */ + {"WAT", 60}, /* West Africa */ + {"AST", 240}, /* Atlantic Standard */ + {"ADT", 240 tDAYZONE}, /* Atlantic Daylight */ + {"EST", 300}, /* Eastern Standard */ + {"EDT", 300 tDAYZONE}, /* Eastern Daylight */ + {"CST", 360}, /* Central Standard */ + {"CDT", 360 tDAYZONE}, /* Central Daylight */ + {"MST", 420}, /* Mountain Standard */ + {"MDT", 420 tDAYZONE}, /* Mountain Daylight */ + {"PST", 480}, /* Pacific Standard */ + {"PDT", 480 tDAYZONE}, /* Pacific Daylight */ + {"YST", 540}, /* Yukon Standard */ + {"YDT", 540 tDAYZONE}, /* Yukon Daylight */ + {"HST", 600}, /* Hawaii Standard */ + {"HDT", 600 tDAYZONE}, /* Hawaii Daylight */ + {"CAT", 600}, /* Central Alaska */ + {"AHST", 600}, /* Alaska-Hawaii Standard */ + {"NT", 660}, /* Nome */ + {"IDLW", 720}, /* International Date Line West */ + {"CET", -60}, /* Central European */ + {"MET", -60}, /* Middle European */ + {"MEWT", -60}, /* Middle European Winter */ + {"MEST", -60 tDAYZONE}, /* Middle European Summer */ + {"CEST", -60 tDAYZONE}, /* Central European Summer */ + {"MESZ", -60 tDAYZONE}, /* Middle European Summer */ + {"FWT", -60}, /* French Winter */ + {"FST", -60 tDAYZONE}, /* French Summer */ + {"EET", -120}, /* Eastern Europe, USSR Zone 1 */ + {"WAST", -420}, /* West Australian Standard */ + {"WADT", -420 tDAYZONE}, /* West Australian Daylight */ + {"CCT", -480}, /* China Coast, USSR Zone 7 */ + {"JST", -540}, /* Japan Standard, USSR Zone 8 */ + {"EAST", -600}, /* Eastern Australian Standard */ + {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */ + {"GST", -600}, /* Guam Standard, USSR Zone 9 */ + {"NZT", -720}, /* New Zealand */ + {"NZST", -720}, /* New Zealand Standard */ + {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */ + {"IDLE", -720}, /* International Date Line East */ + /* Next up: Military timezone names. RFC822 allowed these, but (as noted in + RFC 1123) had their signs wrong. Here we use the correct signs to match + actual military usage. + */ + {"A", 1 * 60}, /* Alpha */ + {"B", 2 * 60}, /* Bravo */ + {"C", 3 * 60}, /* Charlie */ + {"D", 4 * 60}, /* Delta */ + {"E", 5 * 60}, /* Echo */ + {"F", 6 * 60}, /* Foxtrot */ + {"G", 7 * 60}, /* Golf */ + {"H", 8 * 60}, /* Hotel */ + {"I", 9 * 60}, /* India */ + /* "J", Juliet is not used as a timezone, to indicate the observer's local + time */ + {"K", 10 * 60}, /* Kilo */ + {"L", 11 * 60}, /* Lima */ + {"M", 12 * 60}, /* Mike */ + {"N", -1 * 60}, /* November */ + {"O", -2 * 60}, /* Oscar */ + {"P", -3 * 60}, /* Papa */ + {"Q", -4 * 60}, /* Quebec */ + {"R", -5 * 60}, /* Romeo */ + {"S", -6 * 60}, /* Sierra */ + {"T", -7 * 60}, /* Tango */ + {"U", -8 * 60}, /* Uniform */ + {"V", -9 * 60}, /* Victor */ + {"W", -10 * 60}, /* Whiskey */ + {"X", -11 * 60}, /* X-ray */ + {"Y", -12 * 60}, /* Yankee */ + {"Z", 0}, /* Zulu, zero meridian, a.k.a. UTC */ +}; + +/* returns: + -1 no day + 0 monday - 6 sunday +*/ + +static int checkday(const char *check, size_t len) +{ + int i; + const char * const *what; + if(len > 3) + what = &weekday[0]; + else if(len == 3) + what = &Curl_wkday[0]; + else + return -1; /* too short */ + for(i = 0; i<7; i++) { + size_t ilen = strlen(what[0]); + if((ilen == len) && + strncasecompare(check, what[0], len)) + return i; + what++; + } + return -1; +} + +static int checkmonth(const char *check, size_t len) +{ + int i; + const char * const *what = &Curl_month[0]; + if(len != 3) + return -1; /* not a month */ + + for(i = 0; i<12; i++) { + if(strncasecompare(check, what[0], 3)) + return i; + what++; + } + return -1; /* return the offset or -1, no real offset is -1 */ +} + +/* return the time zone offset between GMT and the input one, in number + of seconds or -1 if the timezone was not found/legal */ + +static int checktz(const char *check, size_t len) +{ + unsigned int i; + const struct tzinfo *what = tz; + if(len > 4) /* longer than any valid timezone */ + return -1; + + for(i = 0; i< sizeof(tz)/sizeof(tz[0]); i++) { + size_t ilen = strlen(what->name); + if((ilen == len) && + strncasecompare(check, what->name, len)) + return what->offset*60; + what++; + } + return -1; +} + +static void skip(const char **date) +{ + /* skip everything that are not letters or digits */ + while(**date && !ISALNUM(**date)) + (*date)++; +} + +enum assume { + DATE_MDAY, + DATE_YEAR, + DATE_TIME +}; + +/* + * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to + * mktime but for GMT only. + */ +static time_t time2epoch(int sec, int min, int hour, + int mday, int mon, int year) +{ + static const int month_days_cumulative [12] = + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int leap_days = year - (mon <= 1); + leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400) + - (1969 / 4) + (1969 / 100) - (1969 / 400)); + return ((((time_t) (year - 1970) * 365 + + leap_days + month_days_cumulative[mon] + mday - 1) * 24 + + hour) * 60 + min) * 60 + sec; +} + +/* Returns the value of a single-digit or two-digit decimal number, return + then pointer to after the number. The 'date' pointer is known to point to a + digit. */ +static int oneortwodigit(const char *date, const char **endp) +{ + int num = date[0] - '0'; + if(ISDIGIT(date[1])) { + *endp = &date[2]; + return num*10 + (date[1] - '0'); + } + *endp = &date[1]; + return num; +} + + +/* HH:MM:SS or HH:MM and accept single-digits too */ +static bool match_time(const char *date, + int *h, int *m, int *s, char **endp) +{ + const char *p; + int hh, mm, ss = 0; + hh = oneortwodigit(date, &p); + if((hh < 24) && (*p == ':') && ISDIGIT(p[1])) { + mm = oneortwodigit(&p[1], &p); + if(mm < 60) { + if((*p == ':') && ISDIGIT(p[1])) { + ss = oneortwodigit(&p[1], &p); + if(ss <= 60) { + /* valid HH:MM:SS */ + goto match; + } + } + else { + /* valid HH:MM */ + goto match; + } + } + } + return FALSE; /* not a time string */ +match: + *h = hh; + *m = mm; + *s = ss; + *endp = (char *)p; + return TRUE; +} + +/* + * parsedate() + * + * Returns: + * + * PARSEDATE_OK - a fine conversion + * PARSEDATE_FAIL - failed to convert + * PARSEDATE_LATER - time overflow at the far end of time_t + * PARSEDATE_SOONER - time underflow at the low end of time_t + */ + +/* Wednesday is the longest name this parser knows about */ +#define NAME_LEN 12 + +static int parsedate(const char *date, time_t *output) +{ + time_t t = 0; + int wdaynum = -1; /* day of the week number, 0-6 (mon-sun) */ + int monnum = -1; /* month of the year number, 0-11 */ + int mdaynum = -1; /* day of month, 1 - 31 */ + int hournum = -1; + int minnum = -1; + int secnum = -1; + int yearnum = -1; + int tzoff = -1; + enum assume dignext = DATE_MDAY; + const char *indate = date; /* save the original pointer */ + int part = 0; /* max 6 parts */ + + while(*date && (part < 6)) { + bool found = FALSE; + + skip(&date); + + if(ISALPHA(*date)) { + /* a name coming up */ + size_t len = 0; + const char *p = date; + while(ISALPHA(*p) && (len < NAME_LEN)) { + p++; + len++; + } + + if(len != NAME_LEN) { + if(wdaynum == -1) { + wdaynum = checkday(date, len); + if(wdaynum != -1) + found = TRUE; + } + if(!found && (monnum == -1)) { + monnum = checkmonth(date, len); + if(monnum != -1) + found = TRUE; + } + + if(!found && (tzoff == -1)) { + /* this just must be a time zone string */ + tzoff = checktz(date, len); + if(tzoff != -1) + found = TRUE; + } + } + if(!found) + return PARSEDATE_FAIL; /* bad string */ + + date += len; + } + else if(ISDIGIT(*date)) { + /* a digit */ + int val; + char *end; + if((secnum == -1) && + match_time(date, &hournum, &minnum, &secnum, &end)) { + /* time stamp */ + date = end; + } + else { + long lval; + int error; + int old_errno; + + old_errno = errno; + errno = 0; + lval = strtol(date, &end, 10); + error = errno; + if(errno != old_errno) + errno = old_errno; + + if(error) + return PARSEDATE_FAIL; + +#if LONG_MAX != INT_MAX + if((lval > (long)INT_MAX) || (lval < (long)INT_MIN)) + return PARSEDATE_FAIL; +#endif + + val = curlx_sltosi(lval); + + if((tzoff == -1) && + ((end - date) == 4) && + (val <= 1400) && + (indate< date) && + ((date[-1] == '+' || date[-1] == '-'))) { + /* four digits and a value less than or equal to 1400 (to take into + account all sorts of funny time zone diffs) and it is preceded + with a plus or minus. This is a time zone indication. 1400 is + picked since +1300 is frequently used and +1400 is mentioned as + an edge number in the document "ISO C 200X Proposal: Timezone + Functions" at http://david.tribble.com/text/c0xtimezone.html If + anyone has a more authoritative source for the exact maximum time + zone offsets, please speak up! */ + found = TRUE; + tzoff = (val/100 * 60 + val%100)*60; + + /* the + and - prefix indicates the local time compared to GMT, + this we need their reversed math to get what we want */ + tzoff = date[-1]=='+'?-tzoff:tzoff; + } + + if(((end - date) == 8) && + (yearnum == -1) && + (monnum == -1) && + (mdaynum == -1)) { + /* 8 digits, no year, month or day yet. This is YYYYMMDD */ + found = TRUE; + yearnum = val/10000; + monnum = (val%10000)/100-1; /* month is 0 - 11 */ + mdaynum = val%100; + } + + if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) { + if((val > 0) && (val<32)) { + mdaynum = val; + found = TRUE; + } + dignext = DATE_YEAR; + } + + if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) { + yearnum = val; + found = TRUE; + if(yearnum < 100) { + if(yearnum > 70) + yearnum += 1900; + else + yearnum += 2000; + } + if(mdaynum == -1) + dignext = DATE_MDAY; + } + + if(!found) + return PARSEDATE_FAIL; + + date = end; + } + } + + part++; + } + + if(-1 == secnum) + secnum = minnum = hournum = 0; /* no time, make it zero */ + + if((-1 == mdaynum) || + (-1 == monnum) || + (-1 == yearnum)) + /* lacks vital info, fail */ + return PARSEDATE_FAIL; + +#ifdef HAVE_TIME_T_UNSIGNED + if(yearnum < 1970) { + /* only positive numbers cannot return earlier */ + *output = TIME_T_MIN; + return PARSEDATE_SOONER; + } +#endif + +#if (SIZEOF_TIME_T < 5) + +#ifdef HAVE_TIME_T_UNSIGNED + /* an unsigned 32-bit time_t can only hold dates to 2106 */ + if(yearnum > 2105) { + *output = TIME_T_MAX; + return PARSEDATE_LATER; + } +#else + /* a signed 32-bit time_t can only hold dates to the beginning of 2038 */ + if(yearnum > 2037) { + *output = TIME_T_MAX; + return PARSEDATE_LATER; + } + if(yearnum < 1903) { + *output = TIME_T_MIN; + return PARSEDATE_SOONER; + } +#endif + +#else + /* The Gregorian calendar was introduced 1582 */ + if(yearnum < 1583) + return PARSEDATE_FAIL; +#endif + + if((mdaynum > 31) || (monnum > 11) || + (hournum > 23) || (minnum > 59) || (secnum > 60)) + return PARSEDATE_FAIL; /* clearly an illegal date */ + + /* time2epoch() returns a time_t. time_t is often 32 bits, sometimes even on + architectures that feature a 64 bits 'long' but ultimately time_t is the + correct data type to use. + */ + t = time2epoch(secnum, minnum, hournum, mdaynum, monnum, yearnum); + + /* Add the time zone diff between local time zone and GMT. */ + if(tzoff == -1) + tzoff = 0; + + if((tzoff > 0) && (t > TIME_T_MAX - tzoff)) { + *output = TIME_T_MAX; + return PARSEDATE_LATER; /* time_t overflow */ + } + + t += tzoff; + + *output = t; + + return PARSEDATE_OK; +} +#else +/* disabled */ +static int parsedate(const char *date, time_t *output) +{ + (void)date; + *output = 0; + return PARSEDATE_OK; /* a lie */ +} +#endif + +time_t curl_getdate(const char *p, const time_t *now) +{ + time_t parsed = -1; + int rc = parsedate(p, &parsed); + (void)now; /* legacy argument from the past that we ignore */ + + if(rc == PARSEDATE_OK) { + if(parsed == -1) + /* avoid returning -1 for a working scenario */ + parsed++; + return parsed; + } + /* everything else is fail */ + return -1; +} + +/* Curl_getdate_capped() differs from curl_getdate() in that this will return + TIME_T_MAX in case the parsed time value was too big, instead of an + error. */ + +time_t Curl_getdate_capped(const char *p) +{ + time_t parsed = -1; + int rc = parsedate(p, &parsed); + + switch(rc) { + case PARSEDATE_OK: + if(parsed == -1) + /* avoid returning -1 for a working scenario */ + parsed++; + return parsed; + case PARSEDATE_LATER: + /* this returns the maximum time value */ + return parsed; + default: + return -1; /* everything else is fail */ + } + /* UNREACHABLE */ +} + +/* + * Curl_gmtime() is a gmtime() replacement for portability. Do not use the + * gmtime_r() or gmtime() functions anywhere else but here. + * + */ + +CURLcode Curl_gmtime(time_t intime, struct tm *store) +{ + const struct tm *tm; +#ifdef HAVE_GMTIME_R + /* thread-safe version */ + tm = (struct tm *)gmtime_r(&intime, store); +#else + /* !checksrc! disable BANNEDFUNC 1 */ + tm = gmtime(&intime); + if(tm) + *store = *tm; /* copy the pointed struct to the local copy */ +#endif + + if(!tm) + return CURLE_BAD_FUNCTION_ARGUMENT; + return CURLE_OK; +} diff --git a/third_party/curl/lib/parsedate.h b/third_party/curl/lib/parsedate.h new file mode 100644 index 0000000000..84c37f1675 --- /dev/null +++ b/third_party/curl/lib/parsedate.h @@ -0,0 +1,38 @@ +#ifndef HEADER_CURL_PARSEDATE_H +#define HEADER_CURL_PARSEDATE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +extern const char * const Curl_wkday[7]; +extern const char * const Curl_month[12]; + +CURLcode Curl_gmtime(time_t intime, struct tm *store); + +/* Curl_getdate_capped() differs from curl_getdate() in that this will return + TIME_T_MAX in case the parsed time value was too big, instead of an + error. */ + +time_t Curl_getdate_capped(const char *p); + +#endif /* HEADER_CURL_PARSEDATE_H */ diff --git a/third_party/curl/lib/pingpong.c b/third_party/curl/lib/pingpong.c new file mode 100644 index 0000000000..7ce81e8af7 --- /dev/null +++ b/third_party/curl/lib/pingpong.c @@ -0,0 +1,438 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * 'pingpong' is for generic back-and-forth support functions used by FTP, + * IMAP, POP3, SMTP and whatever more that likes them. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "urldata.h" +#include "cfilters.h" +#include "sendf.h" +#include "select.h" +#include "progress.h" +#include "speedcheck.h" +#include "pingpong.h" +#include "multiif.h" +#include "vtls/vtls.h" +#include "strdup.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#ifdef USE_PINGPONG + +/* Returns timeout in ms. 0 or negative number means the timeout has already + triggered */ +timediff_t Curl_pp_state_timeout(struct Curl_easy *data, + struct pingpong *pp, bool disconnecting) +{ + struct connectdata *conn = data->conn; + timediff_t timeout_ms; /* in milliseconds */ + timediff_t response_time = (data->set.server_response_timeout)? + data->set.server_response_timeout: pp->response_time; + + /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine + remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is + supposed to govern the response for any given server response, not for + the time from connect to the given server response. */ + + /* Without a requested timeout, we only wait 'response_time' seconds for the + full response to arrive before we bail out */ + timeout_ms = response_time - + Curl_timediff(Curl_now(), pp->response); /* spent time */ + + if(data->set.timeout && !disconnecting) { + /* if timeout is requested, find out how much remaining time we have */ + timediff_t timeout2_ms = data->set.timeout - /* timeout time */ + Curl_timediff(Curl_now(), conn->now); /* spent time */ + + /* pick the lowest number */ + timeout_ms = CURLMIN(timeout_ms, timeout2_ms); + } + + return timeout_ms; +} + +/* + * Curl_pp_statemach() + */ +CURLcode Curl_pp_statemach(struct Curl_easy *data, + struct pingpong *pp, bool block, + bool disconnecting) +{ + struct connectdata *conn = data->conn; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int rc; + timediff_t interval_ms; + timediff_t timeout_ms = Curl_pp_state_timeout(data, pp, disconnecting); + CURLcode result = CURLE_OK; + + if(timeout_ms <= 0) { + failf(data, "server response timeout"); + return CURLE_OPERATION_TIMEDOUT; /* already too little time */ + } + + if(block) { + interval_ms = 1000; /* use 1 second timeout intervals */ + if(timeout_ms < interval_ms) + interval_ms = timeout_ms; + } + else + interval_ms = 0; /* immediate */ + + if(Curl_conn_data_pending(data, FIRSTSOCKET)) + rc = 1; + else if(pp->overflow) + /* We are receiving and there is data in the cache so just read it */ + rc = 1; + else if(!pp->sendleft && Curl_conn_data_pending(data, FIRSTSOCKET)) + /* We are receiving and there is data ready in the SSL library */ + rc = 1; + else + rc = Curl_socket_check(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */ + CURL_SOCKET_BAD, + pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */ + interval_ms); + + if(block) { + /* if we did not wait, we do not have to spend time on this now */ + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, Curl_now()); + + if(result) + return result; + } + + if(rc == -1) { + failf(data, "select/poll error"); + result = CURLE_OUT_OF_MEMORY; + } + else if(rc) + result = pp->statemachine(data, data->conn); + + return result; +} + +/* initialize stuff to prepare for reading a fresh new response */ +void Curl_pp_init(struct pingpong *pp) +{ + pp->nread_resp = 0; + pp->response = Curl_now(); /* start response time-out now! */ + pp->pending_resp = TRUE; + Curl_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD); + Curl_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD); +} + +/*********************************************************************** + * + * Curl_pp_vsendf() + * + * Send the formatted string as a command to a pingpong server. Note that + * the string should not have any CRLF appended, as this function will + * append the necessary things itself. + * + * made to never block + */ +CURLcode Curl_pp_vsendf(struct Curl_easy *data, + struct pingpong *pp, + const char *fmt, + va_list args) +{ + size_t bytes_written = 0; + size_t write_len; + char *s; + CURLcode result; + struct connectdata *conn = data->conn; + +#ifdef HAVE_GSSAPI + enum protection_level data_sec; +#endif + + DEBUGASSERT(pp->sendleft == 0); + DEBUGASSERT(pp->sendsize == 0); + DEBUGASSERT(pp->sendthis == NULL); + + if(!conn) + /* cannot send without a connection! */ + return CURLE_SEND_ERROR; + + Curl_dyn_reset(&pp->sendbuf); + result = Curl_dyn_vaddf(&pp->sendbuf, fmt, args); + if(result) + return result; + + /* append CRLF */ + result = Curl_dyn_addn(&pp->sendbuf, "\r\n", 2); + if(result) + return result; + + pp->pending_resp = TRUE; + write_len = Curl_dyn_len(&pp->sendbuf); + s = Curl_dyn_ptr(&pp->sendbuf); + +#ifdef HAVE_GSSAPI + conn->data_prot = PROT_CMD; +#endif + result = Curl_conn_send(data, FIRSTSOCKET, s, write_len, &bytes_written); + if(result == CURLE_AGAIN) { + bytes_written = 0; + } + else if(result) + return result; +#ifdef HAVE_GSSAPI + data_sec = conn->data_prot; + DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); + conn->data_prot = (unsigned char)data_sec; +#endif + + Curl_debug(data, CURLINFO_HEADER_OUT, s, bytes_written); + + if(bytes_written != write_len) { + /* the whole chunk was not sent, keep it around and adjust sizes */ + pp->sendthis = s; + pp->sendsize = write_len; + pp->sendleft = write_len - bytes_written; + } + else { + pp->sendthis = NULL; + pp->sendleft = pp->sendsize = 0; + pp->response = Curl_now(); + } + + return CURLE_OK; +} + + +/*********************************************************************** + * + * Curl_pp_sendf() + * + * Send the formatted string as a command to a pingpong server. Note that + * the string should not have any CRLF appended, as this function will + * append the necessary things itself. + * + * made to never block + */ +CURLcode Curl_pp_sendf(struct Curl_easy *data, struct pingpong *pp, + const char *fmt, ...) +{ + CURLcode result; + va_list ap; + va_start(ap, fmt); + + result = Curl_pp_vsendf(data, pp, fmt, ap); + + va_end(ap); + + return result; +} + +static CURLcode pingpong_read(struct Curl_easy *data, + int sockindex, + char *buffer, + size_t buflen, + ssize_t *nread) +{ + CURLcode result; +#ifdef HAVE_GSSAPI + enum protection_level prot = data->conn->data_prot; + data->conn->data_prot = PROT_CLEAR; +#endif + result = Curl_conn_recv(data, sockindex, buffer, buflen, nread); +#ifdef HAVE_GSSAPI + DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST); + data->conn->data_prot = (unsigned char)prot; +#endif + return result; +} + +/* + * Curl_pp_readresp() + * + * Reads a piece of a server response. + */ +CURLcode Curl_pp_readresp(struct Curl_easy *data, + int sockindex, + struct pingpong *pp, + int *code, /* return the server code if done */ + size_t *size) /* size of the response */ +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + + *code = 0; /* 0 for errors or not done */ + *size = 0; + + if(pp->nfinal) { + /* a previous call left this many bytes in the beginning of the buffer as + that was the final line; now ditch that */ + size_t full = Curl_dyn_len(&pp->recvbuf); + + /* trim off the "final" leading part */ + Curl_dyn_tail(&pp->recvbuf, full - pp->nfinal); + + pp->nfinal = 0; /* now gone */ + } + if(!pp->overflow) { + ssize_t gotbytes = 0; + char buffer[900]; + + result = pingpong_read(data, sockindex, buffer, sizeof(buffer), &gotbytes); + if(result == CURLE_AGAIN) + return CURLE_OK; + + if(result) + return result; + + if(gotbytes <= 0) { + failf(data, "response reading failed (errno: %d)", SOCKERRNO); + return CURLE_RECV_ERROR; + } + + result = Curl_dyn_addn(&pp->recvbuf, buffer, gotbytes); + if(result) + return result; + + data->req.headerbytecount += (unsigned int)gotbytes; + + pp->nread_resp += gotbytes; + } + + do { + char *line = Curl_dyn_ptr(&pp->recvbuf); + char *nl = memchr(line, '\n', Curl_dyn_len(&pp->recvbuf)); + if(nl) { + /* a newline is CRLF in pp-talk, so the CR is ignored as + the line is not really terminated until the LF comes */ + size_t length = nl - line + 1; + + /* output debug output if that is requested */ +#ifdef HAVE_GSSAPI + if(!conn->sec_complete) +#endif + Curl_debug(data, CURLINFO_HEADER_IN, line, length); + + /* + * Pass all response-lines to the callback function registered for + * "headers". The response lines can be seen as a kind of headers. + */ + result = Curl_client_write(data, CLIENTWRITE_INFO, line, length); + if(result) + return result; + + if(pp->endofresp(data, conn, line, length, code)) { + /* When at "end of response", keep the endofresp line first in the + buffer since it will be accessed outside (by pingpong + parsers). Store the overflow counter to inform about additional + data in this buffer after the endofresp line. */ + pp->nfinal = length; + if(Curl_dyn_len(&pp->recvbuf) > length) + pp->overflow = Curl_dyn_len(&pp->recvbuf) - length; + else + pp->overflow = 0; + *size = pp->nread_resp; /* size of the response */ + pp->nread_resp = 0; /* restart */ + break; + } + if(Curl_dyn_len(&pp->recvbuf) > length) + /* keep the remaining piece */ + Curl_dyn_tail((&pp->recvbuf), Curl_dyn_len(&pp->recvbuf) - length); + else + Curl_dyn_reset(&pp->recvbuf); + } + else { + /* without a newline, there is no overflow */ + pp->overflow = 0; + break; + } + + } while(1); /* while there is buffer left to scan */ + + pp->pending_resp = FALSE; + + return result; +} + +int Curl_pp_getsock(struct Curl_easy *data, + struct pingpong *pp, curl_socket_t *socks) +{ + struct connectdata *conn = data->conn; + socks[0] = conn->sock[FIRSTSOCKET]; + + if(pp->sendleft) { + /* write mode */ + return GETSOCK_WRITESOCK(0); + } + + /* read mode */ + return GETSOCK_READSOCK(0); +} + +CURLcode Curl_pp_flushsend(struct Curl_easy *data, + struct pingpong *pp) +{ + /* we have a piece of a command still left to send */ + size_t written; + CURLcode result; + + result = Curl_conn_send(data, FIRSTSOCKET, + pp->sendthis + pp->sendsize - pp->sendleft, + pp->sendleft, &written); + if(result == CURLE_AGAIN) { + result = CURLE_OK; + written = 0; + } + if(result) + return result; + + if(written != pp->sendleft) { + /* only a fraction was sent */ + pp->sendleft -= written; + } + else { + pp->sendthis = NULL; + pp->sendleft = pp->sendsize = 0; + pp->response = Curl_now(); + } + return CURLE_OK; +} + +CURLcode Curl_pp_disconnect(struct pingpong *pp) +{ + Curl_dyn_free(&pp->sendbuf); + Curl_dyn_free(&pp->recvbuf); + return CURLE_OK; +} + +bool Curl_pp_moredata(struct pingpong *pp) +{ + return (!pp->sendleft && Curl_dyn_len(&pp->recvbuf) > pp->nfinal); +} + +#endif diff --git a/third_party/curl/lib/pingpong.h b/third_party/curl/lib/pingpong.h new file mode 100644 index 0000000000..62f2467fc8 --- /dev/null +++ b/third_party/curl/lib/pingpong.h @@ -0,0 +1,160 @@ +#ifndef HEADER_CURL_PINGPONG_H +#define HEADER_CURL_PINGPONG_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_FTP) || \ + !defined(CURL_DISABLE_POP3) || !defined(CURL_DISABLE_SMTP) +#define USE_PINGPONG +#endif + +/* forward-declaration, this is defined in urldata.h */ +struct connectdata; + +typedef enum { + PPTRANSFER_BODY, /* yes do transfer a body */ + PPTRANSFER_INFO, /* do still go through to get info/headers */ + PPTRANSFER_NONE /* do not get anything and do not get info */ +} curl_pp_transfer; + +/* + * 'pingpong' is the generic struct used for protocols doing server<->client + * conversations in a back-and-forth style such as FTP, IMAP, POP3, SMTP etc. + * + * It holds response cache and non-blocking sending data. + */ +struct pingpong { + size_t nread_resp; /* number of bytes currently read of a server response */ + bool pending_resp; /* set TRUE when a server response is pending or in + progress, and is cleared once the last response is + read */ + char *sendthis; /* pointer to a buffer that is to be sent to the server */ + size_t sendleft; /* number of bytes left to send from the sendthis buffer */ + size_t sendsize; /* total size of the sendthis buffer */ + struct curltime response; /* set to Curl_now() when a command has been sent + off, used to time-out response reading */ + timediff_t response_time; /* When no timeout is given, this is the amount of + milliseconds we await for a server response. */ + struct dynbuf sendbuf; + struct dynbuf recvbuf; + size_t overflow; /* number of bytes left after a final response line */ + size_t nfinal; /* number of bytes in the final response line, which + after a match is first in the receice buffer */ + + /* Function pointers the protocols MUST implement and provide for the + pingpong layer to function */ + + CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn); + bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn, + char *ptr, size_t len, int *code); +}; + +#define PINGPONG_SETUP(pp,s,e) \ + do { \ + pp->response_time = RESP_TIMEOUT; \ + pp->statemachine = s; \ + pp->endofresp = e; \ + } while(0) + +/* + * Curl_pp_statemach() + * + * called repeatedly until done. Set 'wait' to make it wait a while on the + * socket if there is no traffic. + */ +CURLcode Curl_pp_statemach(struct Curl_easy *data, struct pingpong *pp, + bool block, bool disconnecting); + +/* initialize stuff to prepare for reading a fresh new response */ +void Curl_pp_init(struct pingpong *pp); + +/* Returns timeout in ms. 0 or negative number means the timeout has already + triggered */ +timediff_t Curl_pp_state_timeout(struct Curl_easy *data, + struct pingpong *pp, bool disconnecting); + + +/*********************************************************************** + * + * Curl_pp_sendf() + * + * Send the formatted string as a command to a pingpong server. Note that + * the string should not have any CRLF appended, as this function will + * append the necessary things itself. + * + * made to never block + */ +CURLcode Curl_pp_sendf(struct Curl_easy *data, + struct pingpong *pp, + const char *fmt, ...) CURL_PRINTF(3, 4); + +/*********************************************************************** + * + * Curl_pp_vsendf() + * + * Send the formatted string as a command to a pingpong server. Note that + * the string should not have any CRLF appended, as this function will + * append the necessary things itself. + * + * made to never block + */ +CURLcode Curl_pp_vsendf(struct Curl_easy *data, + struct pingpong *pp, + const char *fmt, + va_list args) CURL_PRINTF(3, 0); + +/* + * Curl_pp_readresp() + * + * Reads a piece of a server response. + */ +CURLcode Curl_pp_readresp(struct Curl_easy *data, + int sockindex, + struct pingpong *pp, + int *code, /* return the server code if done */ + size_t *size); /* size of the response */ + + +CURLcode Curl_pp_flushsend(struct Curl_easy *data, + struct pingpong *pp); + +/* call this when a pingpong connection is disconnected */ +CURLcode Curl_pp_disconnect(struct pingpong *pp); + +int Curl_pp_getsock(struct Curl_easy *data, struct pingpong *pp, + curl_socket_t *socks); + + +/*********************************************************************** + * + * Curl_pp_moredata() + * + * Returns whether there are still more data in the cache and so a call + * to Curl_pp_readresp() will not block. + */ +bool Curl_pp_moredata(struct pingpong *pp); + +#endif /* HEADER_CURL_PINGPONG_H */ diff --git a/third_party/curl/lib/pop3.c b/third_party/curl/lib/pop3.c new file mode 100644 index 0000000000..4cf1782ac1 --- /dev/null +++ b/third_party/curl/lib/pop3.c @@ -0,0 +1,1584 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC1734 POP3 Authentication + * RFC1939 POP3 protocol + * RFC2195 CRAM-MD5 authentication + * RFC2384 POP URL Scheme + * RFC2449 POP3 Extension Mechanism + * RFC2595 Using TLS with IMAP, POP3 and ACAP + * RFC2831 DIGEST-MD5 authentication + * RFC4422 Simple Authentication and Security Layer (SASL) + * RFC4616 PLAIN authentication + * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism + * RFC5034 POP3 SASL Authentication Mechanism + * RFC6749 OAuth 2.0 Authorization Framework + * RFC8314 Use of TLS for Email Submission and Access + * Draft LOGIN SASL Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_POP3 + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "escape.h" +#include "http.h" /* for HTTP proxy tunnel stuff */ +#include "socks.h" +#include "pop3.h" +#include "strtoofft.h" +#include "strcase.h" +#include "vtls/vtls.h" +#include "cfilters.h" +#include "connect.h" +#include "select.h" +#include "multiif.h" +#include "url.h" +#include "bufref.h" +#include "curl_sasl.h" +#include "curl_md5.h" +#include "warnless.h" +#include "strdup.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* Local API functions */ +static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done); +static CURLcode pop3_do(struct Curl_easy *data, bool *done); +static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, + bool premature); +static CURLcode pop3_connect(struct Curl_easy *data, bool *done); +static CURLcode pop3_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead); +static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done); +static int pop3_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); +static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done); +static CURLcode pop3_setup_connection(struct Curl_easy *data, + struct connectdata *conn); +static CURLcode pop3_parse_url_options(struct connectdata *conn); +static CURLcode pop3_parse_url_path(struct Curl_easy *data); +static CURLcode pop3_parse_custom_request(struct Curl_easy *data); +static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech, + const struct bufref *initresp); +static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech, + const struct bufref *resp); +static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech); +static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out); + +/* + * POP3 protocol handler. + */ + +const struct Curl_handler Curl_handler_pop3 = { + "pop3", /* scheme */ + pop3_setup_connection, /* setup_connection */ + pop3_do, /* do_it */ + pop3_done, /* done */ + ZERO_NULL, /* do_more */ + pop3_connect, /* connect_it */ + pop3_multi_statemach, /* connecting */ + pop3_doing, /* doing */ + pop3_getsock, /* proto_getsock */ + pop3_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + pop3_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_POP3, /* defport */ + CURLPROTO_POP3, /* protocol */ + CURLPROTO_POP3, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ + PROTOPT_URLOPTIONS +}; + +#ifdef USE_SSL +/* + * POP3S protocol handler. + */ + +const struct Curl_handler Curl_handler_pop3s = { + "pop3s", /* scheme */ + pop3_setup_connection, /* setup_connection */ + pop3_do, /* do_it */ + pop3_done, /* done */ + ZERO_NULL, /* do_more */ + pop3_connect, /* connect_it */ + pop3_multi_statemach, /* connecting */ + pop3_doing, /* doing */ + pop3_getsock, /* proto_getsock */ + pop3_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + pop3_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_POP3S, /* defport */ + CURLPROTO_POP3S, /* protocol */ + CURLPROTO_POP3, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL + | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */ +}; +#endif + +/* SASL parameters for the pop3 protocol */ +static const struct SASLproto saslpop3 = { + "pop", /* The service name */ + pop3_perform_auth, /* Send authentication command */ + pop3_continue_auth, /* Send authentication continuation */ + pop3_cancel_auth, /* Send authentication cancellation */ + pop3_get_message, /* Get SASL response message */ + 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ + '*', /* Code received when continuation is expected */ + '+', /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ +}; + +#ifdef USE_SSL +static void pop3_to_pop3s(struct connectdata *conn) +{ + /* Change the connection handler */ + conn->handler = &Curl_handler_pop3s; + + /* Set the connection's upgraded to TLS flag */ + conn->bits.tls_upgraded = TRUE; +} +#else +#define pop3_to_pop3s(x) Curl_nop_stmt +#endif + +/*********************************************************************** + * + * pop3_endofresp() + * + * Checks for an ending POP3 status code at the start of the given string, but + * also detects the APOP timestamp from the server greeting and various + * capabilities from the CAPA response including the supported authentication + * types and allowed SASL mechanisms. + */ +static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn, + char *line, size_t len, int *resp) +{ + struct pop3_conn *pop3c = &conn->proto.pop3c; + (void)data; + + /* Do we have an error response? */ + if(len >= 4 && !memcmp("-ERR", line, 4)) { + *resp = '-'; + + return TRUE; + } + + /* Are we processing CAPA command responses? */ + if(pop3c->state == POP3_CAPA) { + /* Do we have the terminating line? */ + if(len >= 1 && line[0] == '.') + /* Treat the response as a success */ + *resp = '+'; + else + /* Treat the response as an untagged continuation */ + *resp = '*'; + + return TRUE; + } + + /* Do we have a success response? */ + if(len >= 3 && !memcmp("+OK", line, 3)) { + *resp = '+'; + + return TRUE; + } + + /* Do we have a continuation response? */ + if(len >= 1 && line[0] == '+') { + *resp = '*'; + + return TRUE; + } + + return FALSE; /* Nothing for us */ +} + +/*********************************************************************** + * + * pop3_get_message() + * + * Gets the authentication message from the response buffer. + */ +static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out) +{ + char *message = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf); + size_t len = data->conn->proto.pop3c.pp.nfinal; + + if(len > 2) { + /* Find the start of the message */ + len -= 2; + for(message += 2; *message == ' ' || *message == '\t'; message++, len--) + ; + + /* Find the end of the message */ + while(len--) + if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && + message[len] != '\t') + break; + + /* Terminate the message */ + message[++len] = '\0'; + Curl_bufref_set(out, message, len, NULL); + } + else + /* junk input => zero length output */ + Curl_bufref_set(out, "", 0, NULL); + + return CURLE_OK; +} + +/*********************************************************************** + * + * pop3_state() + * + * This is the ONLY way to change POP3 state! + */ +static void pop3_state(struct Curl_easy *data, pop3state newstate) +{ + struct pop3_conn *pop3c = &data->conn->proto.pop3c; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char * const names[] = { + "STOP", + "SERVERGREET", + "CAPA", + "STARTTLS", + "UPGRADETLS", + "AUTH", + "APOP", + "USER", + "PASS", + "COMMAND", + "QUIT", + /* LAST */ + }; + + if(pop3c->state != newstate) + infof(data, "POP3 %p state change from %s to %s", + (void *)pop3c, names[pop3c->state], names[newstate]); +#endif + + pop3c->state = newstate; +} + +/*********************************************************************** + * + * pop3_perform_capa() + * + * Sends the CAPA command in order to obtain a list of server side supported + * capabilities. + */ +static CURLcode pop3_perform_capa(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; + + pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */ + pop3c->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */ + pop3c->tls_supported = FALSE; /* Clear the TLS capability */ + + /* Send the CAPA command */ + result = Curl_pp_sendf(data, &pop3c->pp, "%s", "CAPA"); + + if(!result) + pop3_state(data, POP3_CAPA); + + return result; +} + +/*********************************************************************** + * + * pop3_perform_starttls() + * + * Sends the STLS command to start the upgrade to TLS. + */ +static CURLcode pop3_perform_starttls(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Send the STLS command */ + CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "STLS"); + + if(!result) + pop3_state(data, POP3_STARTTLS); + + return result; +} + +/*********************************************************************** + * + * pop3_perform_upgrade_tls() + * + * Performs the upgrade to TLS. + */ +static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Start the SSL connection */ + struct pop3_conn *pop3c = &conn->proto.pop3c; + CURLcode result; + bool ssldone = FALSE; + + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + if(result) + goto out; + } + + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + + if(!result) { + pop3c->ssldone = ssldone; + if(pop3c->state != POP3_UPGRADETLS) + pop3_state(data, POP3_UPGRADETLS); + + if(pop3c->ssldone) { + pop3_to_pop3s(conn); + result = pop3_perform_capa(data, conn); + } + } +out: + return result; +} + +/*********************************************************************** + * + * pop3_perform_user() + * + * Sends a clear text USER command to authenticate with. + */ +static CURLcode pop3_perform_user(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + + /* Check we have a username and password to authenticate with and end the + connect phase if we do not */ + if(!data->state.aptr.user) { + pop3_state(data, POP3_STOP); + + return result; + } + + /* Send the USER command */ + result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "USER %s", + conn->user ? conn->user : ""); + if(!result) + pop3_state(data, POP3_USER); + + return result; +} + +#ifndef CURL_DISABLE_DIGEST_AUTH +/*********************************************************************** + * + * pop3_perform_apop() + * + * Sends an APOP command to authenticate with. + */ +static CURLcode pop3_perform_apop(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; + size_t i; + struct MD5_context *ctxt; + unsigned char digest[MD5_DIGEST_LEN]; + char secret[2 * MD5_DIGEST_LEN + 1]; + + /* Check we have a username and password to authenticate with and end the + connect phase if we do not */ + if(!data->state.aptr.user) { + pop3_state(data, POP3_STOP); + + return result; + } + + /* Create the digest */ + ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + if(!ctxt) + return CURLE_OUT_OF_MEMORY; + + Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp, + curlx_uztoui(strlen(pop3c->apoptimestamp))); + + Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd, + curlx_uztoui(strlen(conn->passwd))); + + /* Finalise the digest */ + Curl_MD5_final(ctxt, digest); + + /* Convert the calculated 16 octet digest into a 32 byte hex string */ + for(i = 0; i < MD5_DIGEST_LEN; i++) + msnprintf(&secret[2 * i], 3, "%02x", digest[i]); + + result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret); + + if(!result) + pop3_state(data, POP3_APOP); + + return result; +} +#endif + +/*********************************************************************** + * + * pop3_perform_auth() + * + * Sends an AUTH command allowing the client to login with the given SASL + * authentication mechanism. + */ +static CURLcode pop3_perform_auth(struct Curl_easy *data, + const char *mech, + const struct bufref *initresp) +{ + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &data->conn->proto.pop3c; + const char *ir = (const char *) Curl_bufref_ptr(initresp); + + if(ir) { /* AUTH ... */ + /* Send the AUTH command with the initial response */ + result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, ir); + } + else { + /* Send the AUTH command */ + result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s", mech); + } + + return result; +} + +/*********************************************************************** + * + * pop3_continue_auth() + * + * Sends SASL continuation data. + */ +static CURLcode pop3_continue_auth(struct Curl_easy *data, + const char *mech, + const struct bufref *resp) +{ + struct pop3_conn *pop3c = &data->conn->proto.pop3c; + + (void)mech; + + return Curl_pp_sendf(data, &pop3c->pp, + "%s", (const char *) Curl_bufref_ptr(resp)); +} + +/*********************************************************************** + * + * pop3_cancel_auth() + * + * Sends SASL cancellation. + */ +static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech) +{ + struct pop3_conn *pop3c = &data->conn->proto.pop3c; + + (void)mech; + + return Curl_pp_sendf(data, &pop3c->pp, "*"); +} + +/*********************************************************************** + * + * pop3_perform_authentication() + * + * Initiates the authentication sequence, with the appropriate SASL + * authentication mechanism, falling back to APOP and clear text should a + * common mechanism not be available between the client and server. + */ +static CURLcode pop3_perform_authentication(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; + saslprogress progress = SASL_IDLE; + + /* Check we have enough data to authenticate with and end the + connect phase if we do not */ + if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) { + pop3_state(data, POP3_STOP); + return result; + } + + if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) { + /* Calculate the SASL login details */ + result = Curl_sasl_start(&pop3c->sasl, data, FALSE, &progress); + + if(!result) + if(progress == SASL_INPROGRESS) + pop3_state(data, POP3_AUTH); + } + + if(!result && progress == SASL_IDLE) { +#ifndef CURL_DISABLE_DIGEST_AUTH + if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) + /* Perform APOP authentication */ + result = pop3_perform_apop(data, conn); + else +#endif + if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT) + /* Perform clear text authentication */ + result = pop3_perform_user(data, conn); + else { + /* Other mechanisms not supported */ + infof(data, "No known authentication mechanisms supported"); + result = CURLE_LOGIN_DENIED; + } + } + + return result; +} + +/*********************************************************************** + * + * pop3_perform_command() + * + * Sends a POP3 based command. + */ +static CURLcode pop3_perform_command(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct POP3 *pop3 = data->req.p.pop3; + const char *command = NULL; + + /* Calculate the default command */ + if(pop3->id[0] == '\0' || data->set.list_only) { + command = "LIST"; + + if(pop3->id[0] != '\0') + /* Message specific LIST so skip the BODY transfer */ + pop3->transfer = PPTRANSFER_INFO; + } + else + command = "RETR"; + + /* Send the command */ + if(pop3->id[0] != '\0') + result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s %s", + (pop3->custom && pop3->custom[0] != '\0' ? + pop3->custom : command), pop3->id); + else + result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", + (pop3->custom && pop3->custom[0] != '\0' ? + pop3->custom : command)); + + if(!result) + pop3_state(data, POP3_COMMAND); + + return result; +} + +/*********************************************************************** + * + * pop3_perform_quit() + * + * Performs the quit action prior to sclose() be called. + */ +static CURLcode pop3_perform_quit(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Send the QUIT command */ + CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "QUIT"); + + if(!result) + pop3_state(data, POP3_QUIT); + + return result; +} + +/* For the initial server greeting */ +static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data, + int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct pop3_conn *pop3c = &conn->proto.pop3c; + const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf); + size_t len = data->conn->proto.pop3c.pp.nfinal; + + (void)instate; /* no use for this yet */ + + if(pop3code != '+') { + failf(data, "Got unexpected pop3-server response"); + result = CURLE_WEIRD_SERVER_REPLY; + } + else if(len > 3) { + /* Does the server support APOP authentication? */ + char *lt; + char *gt = NULL; + + /* Look for the APOP timestamp */ + lt = memchr(line, '<', len); + if(lt) + /* search the remainder for '>' */ + gt = memchr(lt, '>', len - (lt - line)); + if(gt) { + /* the length of the timestamp, including the brackets */ + size_t timestamplen = gt - lt + 1; + char *at = memchr(lt, '@', timestamplen); + /* If the timestamp does not contain '@' it is not (as required by + RFC-1939) conformant to the RFC-822 message id syntax, and we + therefore do not use APOP authentication. */ + if(at) { + /* dupe the timestamp */ + pop3c->apoptimestamp = Curl_memdup0(lt, timestamplen); + if(!pop3c->apoptimestamp) + return CURLE_OUT_OF_MEMORY; + /* Store the APOP capability */ + pop3c->authtypes |= POP3_TYPE_APOP; + } + } + + if(!result) + result = pop3_perform_capa(data, conn); + } + + return result; +} + +/* For CAPA responses */ +static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct pop3_conn *pop3c = &conn->proto.pop3c; + const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf); + size_t len = data->conn->proto.pop3c.pp.nfinal; + + (void)instate; /* no use for this yet */ + + /* Do we have a untagged continuation response? */ + if(pop3code == '*') { + /* Does the server support the STLS capability? */ + if(len >= 4 && !memcmp(line, "STLS", 4)) + pop3c->tls_supported = TRUE; + + /* Does the server support clear text authentication? */ + else if(len >= 4 && !memcmp(line, "USER", 4)) + pop3c->authtypes |= POP3_TYPE_CLEARTEXT; + + /* Does the server support SASL based authentication? */ + else if(len >= 5 && !memcmp(line, "SASL ", 5)) { + pop3c->authtypes |= POP3_TYPE_SASL; + + /* Advance past the SASL keyword */ + line += 5; + len -= 5; + + /* Loop through the data line */ + for(;;) { + size_t llen; + size_t wordlen; + unsigned short mechbit; + + while(len && + (*line == ' ' || *line == '\t' || + *line == '\r' || *line == '\n')) { + + line++; + len--; + } + + if(!len) + break; + + /* Extract the word */ + for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && + line[wordlen] != '\t' && line[wordlen] != '\r' && + line[wordlen] != '\n';) + wordlen++; + + /* Test the word for a matching authentication mechanism */ + mechbit = Curl_sasl_decode_mech(line, wordlen, &llen); + if(mechbit && llen == wordlen) + pop3c->sasl.authmechs |= mechbit; + + line += wordlen; + len -= wordlen; + } + } + } + else { + /* Clear text is supported when CAPA is not recognised */ + if(pop3code != '+') + pop3c->authtypes |= POP3_TYPE_CLEARTEXT; + + if(!data->set.use_ssl || Curl_conn_is_ssl(conn, FIRSTSOCKET)) + result = pop3_perform_authentication(data, conn); + else if(pop3code == '+' && pop3c->tls_supported) + /* Switch to TLS connection now */ + result = pop3_perform_starttls(data, conn); + else if(data->set.use_ssl <= CURLUSESSL_TRY) + /* Fallback and carry on with authentication */ + result = pop3_perform_authentication(data, conn); + else { + failf(data, "STLS not supported."); + result = CURLE_USE_SSL_FAILED; + } + } + + return result; +} + +/* For STARTTLS responses */ +static CURLcode pop3_state_starttls_resp(struct Curl_easy *data, + struct connectdata *conn, + int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + (void)instate; /* no use for this yet */ + + /* Pipelining in response is forbidden. */ + if(data->conn->proto.pop3c.pp.overflow) + return CURLE_WEIRD_SERVER_REPLY; + + if(pop3code != '+') { + if(data->set.use_ssl != CURLUSESSL_TRY) { + failf(data, "STARTTLS denied"); + result = CURLE_USE_SSL_FAILED; + } + else + result = pop3_perform_authentication(data, conn); + } + else + result = pop3_perform_upgrade_tls(data, conn); + + return result; +} + +/* For SASL authentication responses */ +static CURLcode pop3_state_auth_resp(struct Curl_easy *data, + int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct pop3_conn *pop3c = &conn->proto.pop3c; + saslprogress progress; + + (void)instate; /* no use for this yet */ + + result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress); + if(!result) + switch(progress) { + case SASL_DONE: + pop3_state(data, POP3_STOP); /* Authenticated */ + break; + case SASL_IDLE: /* No mechanism left after cancellation */ +#ifndef CURL_DISABLE_DIGEST_AUTH + if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) + /* Perform APOP authentication */ + result = pop3_perform_apop(data, conn); + else +#endif + if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT) + /* Perform clear text authentication */ + result = pop3_perform_user(data, conn); + else { + failf(data, "Authentication cancelled"); + result = CURLE_LOGIN_DENIED; + } + break; + default: + break; + } + + return result; +} + +#ifndef CURL_DISABLE_DIGEST_AUTH +/* For APOP responses */ +static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + (void)instate; /* no use for this yet */ + + if(pop3code != '+') { + failf(data, "Authentication failed: %d", pop3code); + result = CURLE_LOGIN_DENIED; + } + else + /* End of connect phase */ + pop3_state(data, POP3_STOP); + + return result; +} +#endif + +/* For USER responses */ +static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + (void)instate; /* no use for this yet */ + + if(pop3code != '+') { + failf(data, "Access denied. %c", pop3code); + result = CURLE_LOGIN_DENIED; + } + else + /* Send the PASS command */ + result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s", + conn->passwd ? conn->passwd : ""); + if(!result) + pop3_state(data, POP3_PASS); + + return result; +} + +/* For PASS responses */ +static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + (void)instate; /* no use for this yet */ + + if(pop3code != '+') { + failf(data, "Access denied. %c", pop3code); + result = CURLE_LOGIN_DENIED; + } + else + /* End of connect phase */ + pop3_state(data, POP3_STOP); + + return result; +} + +/* For command responses */ +static CURLcode pop3_state_command_resp(struct Curl_easy *data, + int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct POP3 *pop3 = data->req.p.pop3; + struct pop3_conn *pop3c = &conn->proto.pop3c; + struct pingpong *pp = &pop3c->pp; + + (void)instate; /* no use for this yet */ + + if(pop3code != '+') { + pop3_state(data, POP3_STOP); + return CURLE_WEIRD_SERVER_REPLY; + } + + /* This 'OK' line ends with a CR LF pair which is the two first bytes of the + EOB string so count this is two matching bytes. This is necessary to make + the code detect the EOB if the only data than comes now is %2e CR LF like + when there is no body to return. */ + pop3c->eob = 2; + + /* But since this initial CR LF pair is not part of the actual body, we set + the strip counter here so that these bytes will not be delivered. */ + pop3c->strip = 2; + + if(pop3->transfer == PPTRANSFER_BODY) { + /* POP3 download */ + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); + + if(pp->overflow) { + /* The recv buffer contains data that is actually body content so send + it as such. Note that there may even be additional "headers" after + the body */ + + /* keep only the overflow */ + Curl_dyn_tail(&pp->recvbuf, pp->overflow); + pp->nfinal = 0; /* done */ + + if(!data->req.no_body) { + result = Curl_pop3_write(data, Curl_dyn_ptr(&pp->recvbuf), + Curl_dyn_len(&pp->recvbuf)); + if(result) + return result; + } + + /* reset the buffer */ + Curl_dyn_reset(&pp->recvbuf); + pp->overflow = 0; + } + } + else + pp->overflow = 0; + + /* End of DO phase */ + pop3_state(data, POP3_STOP); + + return result; +} + +static CURLcode pop3_statemachine(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + int pop3code; + struct pop3_conn *pop3c = &conn->proto.pop3c; + struct pingpong *pp = &pop3c->pp; + size_t nread = 0; + (void)data; + + /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */ + if(pop3c->state == POP3_UPGRADETLS) + return pop3_perform_upgrade_tls(data, conn); + + /* Flush any data that needs to be sent */ + if(pp->sendleft) + return Curl_pp_flushsend(data, pp); + + do { + /* Read the response from the server */ + result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread); + if(result) + return result; + + if(!pop3code) + break; + + /* We have now received a full POP3 server response */ + switch(pop3c->state) { + case POP3_SERVERGREET: + result = pop3_state_servergreet_resp(data, pop3code, pop3c->state); + break; + + case POP3_CAPA: + result = pop3_state_capa_resp(data, pop3code, pop3c->state); + break; + + case POP3_STARTTLS: + result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state); + break; + + case POP3_AUTH: + result = pop3_state_auth_resp(data, pop3code, pop3c->state); + break; + +#ifndef CURL_DISABLE_DIGEST_AUTH + case POP3_APOP: + result = pop3_state_apop_resp(data, pop3code, pop3c->state); + break; +#endif + + case POP3_USER: + result = pop3_state_user_resp(data, pop3code, pop3c->state); + break; + + case POP3_PASS: + result = pop3_state_pass_resp(data, pop3code, pop3c->state); + break; + + case POP3_COMMAND: + result = pop3_state_command_resp(data, pop3code, pop3c->state); + break; + + case POP3_QUIT: + pop3_state(data, POP3_STOP); + break; + + default: + /* internal error */ + pop3_state(data, POP3_STOP); + break; + } + } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp)); + + return result; +} + +/* Called repeatedly until done from multi.c */ +static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct pop3_conn *pop3c = &conn->proto.pop3c; + + if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) { + bool ssldone = FALSE; + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + pop3c->ssldone = ssldone; + if(result || !pop3c->ssldone) + return result; + } + + result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE); + *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE; + + return result; +} + +static CURLcode pop3_block_statemach(struct Curl_easy *data, + struct connectdata *conn, + bool disconnecting) +{ + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; + + while(pop3c->state != POP3_STOP && !result) + result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting); + + return result; +} + +/* Allocate and initialize the POP3 struct for the current Curl_easy if + required */ +static CURLcode pop3_init(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct POP3 *pop3; + + pop3 = data->req.p.pop3 = calloc(1, sizeof(struct POP3)); + if(!pop3) + result = CURLE_OUT_OF_MEMORY; + + return result; +} + +/* For the POP3 "protocol connect" and "doing" phases only */ +static int pop3_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks) +{ + return Curl_pp_getsock(data, &conn->proto.pop3c.pp, socks); +} + +/*********************************************************************** + * + * pop3_connect() + * + * This function should do everything that is to be considered a part of the + * connection phase. + * + * The variable 'done' points to will be TRUE if the protocol-layer connect + * phase is done when this function returns, or FALSE if not. + */ +static CURLcode pop3_connect(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct pop3_conn *pop3c = &conn->proto.pop3c; + struct pingpong *pp = &pop3c->pp; + + *done = FALSE; /* default to not done yet */ + + /* We always support persistent connections in POP3 */ + connkeep(conn, "POP3 default"); + + PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp); + + /* Set the default preferred authentication type and mechanism */ + pop3c->preftype = POP3_TYPE_ANY; + Curl_sasl_init(&pop3c->sasl, data, &saslpop3); + + /* Initialise the pingpong layer */ + Curl_pp_init(pp); + + /* Parse the URL options */ + result = pop3_parse_url_options(conn); + if(result) + return result; + + /* Start off waiting for the server greeting response */ + pop3_state(data, POP3_SERVERGREET); + + result = pop3_multi_statemach(data, done); + + return result; +} + +/*********************************************************************** + * + * pop3_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + CURLcode result = CURLE_OK; + struct POP3 *pop3 = data->req.p.pop3; + + (void)premature; + + if(!pop3) + return CURLE_OK; + + if(status) { + connclose(data->conn, "POP3 done with bad status"); + result = status; /* use the already set error code */ + } + + /* Cleanup our per-request based variables */ + Curl_safefree(pop3->id); + Curl_safefree(pop3->custom); + + /* Clear the transfer mode for the next request */ + pop3->transfer = PPTRANSFER_BODY; + + return result; +} + +/*********************************************************************** + * + * pop3_perform() + * + * This is the actual DO function for POP3. Get a message/listing according to + * the options previously setup. + */ +static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, + bool *dophase_done) +{ + /* This is POP3 and no proxy */ + CURLcode result = CURLE_OK; + struct POP3 *pop3 = data->req.p.pop3; + + DEBUGF(infof(data, "DO phase starts")); + + if(data->req.no_body) { + /* Requested no body means no transfer */ + pop3->transfer = PPTRANSFER_INFO; + } + + *dophase_done = FALSE; /* not done yet */ + + /* Start the first command in the DO phase */ + result = pop3_perform_command(data); + if(result) + return result; + + /* Run the state-machine */ + result = pop3_multi_statemach(data, dophase_done); + *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + + if(*dophase_done) + DEBUGF(infof(data, "DO phase is complete")); + + return result; +} + +/*********************************************************************** + * + * pop3_do() + * + * This function is registered as 'curl_do' function. It decodes the path + * parts etc as a wrapper to the actual DO function (pop3_perform). + * + * The input argument is already checked for validity. + */ +static CURLcode pop3_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + *done = FALSE; /* default to false */ + + /* Parse the URL path */ + result = pop3_parse_url_path(data); + if(result) + return result; + + /* Parse the custom request */ + result = pop3_parse_custom_request(data); + if(result) + return result; + + result = pop3_regular_transfer(data, done); + + return result; +} + +/*********************************************************************** + * + * pop3_disconnect() + * + * Disconnect from an POP3 server. Cleanup protocol-specific per-connection + * resources. BLOCKING. + */ +static CURLcode pop3_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) +{ + struct pop3_conn *pop3c = &conn->proto.pop3c; + (void)data; + + /* We cannot send quit unconditionally. If this connection is stale or + bad in any way, sending quit and waiting around here will make the + disconnect wait in vain and cause more problems than we need to. */ + + if(!dead_connection && conn->bits.protoconnstart) { + if(!pop3_perform_quit(data, conn)) + (void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */ + } + + /* Disconnect from the server */ + Curl_pp_disconnect(&pop3c->pp); + + /* Cleanup the SASL module */ + Curl_sasl_cleanup(conn, pop3c->sasl.authused); + + /* Cleanup our connection based variables */ + Curl_safefree(pop3c->apoptimestamp); + + return CURLE_OK; +} + +/* Call this when the DO phase has completed */ +static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected) +{ + (void)data; + (void)connected; + + return CURLE_OK; +} + +/* Called from multi.c while DOing */ +static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done) +{ + CURLcode result = pop3_multi_statemach(data, dophase_done); + + if(result) + DEBUGF(infof(data, "DO phase failed")); + else if(*dophase_done) { + result = pop3_dophase_done(data, FALSE /* not connected */); + + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +/*********************************************************************** + * + * pop3_regular_transfer() + * + * The input argument is already checked for validity. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. + */ +static CURLcode pop3_regular_transfer(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + + /* Make sure size is unknown at this point */ + data->req.size = -1; + + /* Set the progress data */ + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); + + /* Carry out the perform */ + result = pop3_perform(data, &connected, dophase_done); + + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) + result = pop3_dophase_done(data, connected); + + return result; +} + +static CURLcode pop3_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Initialise the POP3 layer */ + CURLcode result = pop3_init(data); + if(result) + return result; + + /* Clear the TLS upgraded flag */ + conn->bits.tls_upgraded = FALSE; + + return CURLE_OK; +} + +/*********************************************************************** + * + * pop3_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode pop3_parse_url_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; + const char *ptr = conn->options; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(strncasecompare(key, "AUTH=", 5)) { + result = Curl_sasl_parse_url_auth_option(&pop3c->sasl, + value, ptr - value); + + if(result && strncasecompare(value, "+APOP", ptr - value)) { + pop3c->preftype = POP3_TYPE_APOP; + pop3c->sasl.prefmech = SASL_AUTH_NONE; + result = CURLE_OK; + } + } + else + result = CURLE_URL_MALFORMAT; + + if(*ptr == ';') + ptr++; + } + + if(pop3c->preftype != POP3_TYPE_APOP) + switch(pop3c->sasl.prefmech) { + case SASL_AUTH_NONE: + pop3c->preftype = POP3_TYPE_NONE; + break; + case SASL_AUTH_DEFAULT: + pop3c->preftype = POP3_TYPE_ANY; + break; + default: + pop3c->preftype = POP3_TYPE_SASL; + break; + } + + return result; +} + +/*********************************************************************** + * + * pop3_parse_url_path() + * + * Parse the URL path into separate path components. + */ +static CURLcode pop3_parse_url_path(struct Curl_easy *data) +{ + /* The POP3 struct is already initialised in pop3_connect() */ + struct POP3 *pop3 = data->req.p.pop3; + const char *path = &data->state.up.path[1]; /* skip leading path */ + + /* URL decode the path for the message ID */ + return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL); +} + +/*********************************************************************** + * + * pop3_parse_custom_request() + * + * Parse the custom request. + */ +static CURLcode pop3_parse_custom_request(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct POP3 *pop3 = data->req.p.pop3; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + + /* URL decode the custom request */ + if(custom) + result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL); + + return result; +} + +/*********************************************************************** + * + * Curl_pop3_write() + * + * This function scans the body after the end-of-body and writes everything + * until the end is found. + */ +CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread) +{ + /* This code could be made into a special function in the handler struct */ + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + struct connectdata *conn = data->conn; + struct pop3_conn *pop3c = &conn->proto.pop3c; + bool strip_dot = FALSE; + size_t last = 0; + size_t i; + + /* Search through the buffer looking for the end-of-body marker which is + 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches + the eob so the server will have prefixed it with an extra dot which we + need to strip out. Additionally the marker could of course be spread out + over 5 different data chunks. */ + for(i = 0; i < nread; i++) { + size_t prev = pop3c->eob; + + switch(str[i]) { + case 0x0d: + if(pop3c->eob == 0) { + pop3c->eob++; + + if(i) { + /* Write out the body part that did not match */ + result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], + i - last); + + if(result) + return result; + + last = i; + } + } + else if(pop3c->eob == 3) + pop3c->eob++; + else + /* If the character match was not at position 0 or 3 then restart the + pattern matching */ + pop3c->eob = 1; + break; + + case 0x0a: + if(pop3c->eob == 1 || pop3c->eob == 4) + pop3c->eob++; + else + /* If the character match was not at position 1 or 4 then start the + search again */ + pop3c->eob = 0; + break; + + case 0x2e: + if(pop3c->eob == 2) + pop3c->eob++; + else if(pop3c->eob == 3) { + /* We have an extra dot after the CRLF which we need to strip off */ + strip_dot = TRUE; + pop3c->eob = 0; + } + else + /* If the character match was not at position 2 then start the search + again */ + pop3c->eob = 0; + break; + + default: + pop3c->eob = 0; + break; + } + + /* Did we have a partial match which has subsequently failed? */ + if(prev && prev >= pop3c->eob) { + /* Strip can only be non-zero for the very first mismatch after CRLF + and then both prev and strip are equal and nothing will be output + below */ + while(prev && pop3c->strip) { + prev--; + pop3c->strip--; + } + + if(prev) { + /* If the partial match was the CRLF and dot then only write the CRLF + as the server would have inserted the dot */ + if(strip_dot && prev - 1 > 0) { + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, + prev - 1); + } + else if(!strip_dot) { + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, + prev); + } + else { + result = CURLE_OK; + } + + if(result) + return result; + + last = i; + strip_dot = FALSE; + } + } + } + + if(pop3c->eob == POP3_EOB_LEN) { + /* We have a full match so the transfer is done, however we must transfer + the CRLF at the start of the EOB as this is considered to be part of the + message as per RFC-1939, sect. 3 */ + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, 2); + + k->keepon &= ~KEEP_RECV; + pop3c->eob = 0; + + return result; + } + + if(pop3c->eob) + /* While EOB is matching nothing should be output */ + return CURLE_OK; + + if(nread - last) { + result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], + nread - last); + } + + return result; +} + +#endif /* CURL_DISABLE_POP3 */ diff --git a/third_party/curl/lib/pop3.h b/third_party/curl/lib/pop3.h new file mode 100644 index 0000000000..e8e98db04b --- /dev/null +++ b/third_party/curl/lib/pop3.h @@ -0,0 +1,98 @@ +#ifndef HEADER_CURL_POP3_H +#define HEADER_CURL_POP3_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "pingpong.h" +#include "curl_sasl.h" + +/**************************************************************************** + * POP3 unique setup + ***************************************************************************/ +typedef enum { + POP3_STOP, /* do nothing state, stops the state machine */ + POP3_SERVERGREET, /* waiting for the initial greeting immediately after + a connect */ + POP3_CAPA, + POP3_STARTTLS, + POP3_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS + (multi mode only) */ + POP3_AUTH, + POP3_APOP, + POP3_USER, + POP3_PASS, + POP3_COMMAND, + POP3_QUIT, + POP3_LAST /* never used */ +} pop3state; + +/* This POP3 struct is used in the Curl_easy. All POP3 data that is + connection-oriented must be in pop3_conn to properly deal with the fact that + perhaps the Curl_easy is changed between the times the connection is + used. */ +struct POP3 { + curl_pp_transfer transfer; + char *id; /* Message ID */ + char *custom; /* Custom Request */ +}; + +/* pop3_conn is used for struct connection-oriented data in the connectdata + struct */ +struct pop3_conn { + struct pingpong pp; + pop3state state; /* Always use pop3.c:state() to change state! */ + size_t eob; /* Number of bytes of the EOB (End Of Body) that + have been received so far */ + size_t strip; /* Number of bytes from the start to ignore as + non-body */ + struct SASL sasl; /* SASL-related storage */ + char *apoptimestamp; /* APOP timestamp from the server greeting */ + unsigned char authtypes; /* Accepted authentication types */ + unsigned char preftype; /* Preferred authentication type */ + BIT(ssldone); /* Is connect() over SSL done? */ + BIT(tls_supported); /* StartTLS capability supported by server */ +}; + +extern const struct Curl_handler Curl_handler_pop3; +extern const struct Curl_handler Curl_handler_pop3s; + +/* Authentication type flags */ +#define POP3_TYPE_CLEARTEXT (1 << 0) +#define POP3_TYPE_APOP (1 << 1) +#define POP3_TYPE_SASL (1 << 2) + +/* Authentication type values */ +#define POP3_TYPE_NONE 0 +#define POP3_TYPE_ANY (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL) + +/* This is the 5-bytes End-Of-Body marker for POP3 */ +#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a" +#define POP3_EOB_LEN 5 + +/* This function scans the body after the end-of-body and writes everything + * until the end is found */ +CURLcode Curl_pop3_write(struct Curl_easy *data, + const char *str, size_t nread); + +#endif /* HEADER_CURL_POP3_H */ diff --git a/third_party/curl/lib/progress.c b/third_party/curl/lib/progress.c new file mode 100644 index 0000000000..34c29a2701 --- /dev/null +++ b/third_party/curl/lib/progress.c @@ -0,0 +1,647 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "urldata.h" +#include "sendf.h" +#include "multiif.h" +#include "progress.h" +#include "timeval.h" +#include "curl_printf.h" + +/* check rate limits within this many recent milliseconds, at minimum. */ +#define MIN_RATE_LIMIT_PERIOD 3000 + +#ifndef CURL_DISABLE_PROGRESS_METER +/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero + byte) */ +static void time2str(char *r, curl_off_t seconds) +{ + curl_off_t h; + if(seconds <= 0) { + strcpy(r, "--:--:--"); + return; + } + h = seconds / CURL_OFF_T_C(3600); + if(h <= CURL_OFF_T_C(99)) { + curl_off_t m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60); + curl_off_t s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60)); + msnprintf(r, 9, "%2" CURL_FORMAT_CURL_OFF_T ":%02" CURL_FORMAT_CURL_OFF_T + ":%02" CURL_FORMAT_CURL_OFF_T, h, m, s); + } + else { + /* this equals to more than 99 hours, switch to a more suitable output + format to fit within the limits. */ + curl_off_t d = seconds / CURL_OFF_T_C(86400); + h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600); + if(d <= CURL_OFF_T_C(999)) + msnprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T + "d %02" CURL_FORMAT_CURL_OFF_T "h", d, h); + else + msnprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d); + } +} + +/* The point of this function would be to return a string of the input data, + but never longer than 5 columns (+ one zero byte). + Add suffix k, M, G when suitable... */ +static char *max5data(curl_off_t bytes, char *max5) +{ +#define ONE_KILOBYTE CURL_OFF_T_C(1024) +#define ONE_MEGABYTE (CURL_OFF_T_C(1024) * ONE_KILOBYTE) +#define ONE_GIGABYTE (CURL_OFF_T_C(1024) * ONE_MEGABYTE) +#define ONE_TERABYTE (CURL_OFF_T_C(1024) * ONE_GIGABYTE) +#define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE) + + if(bytes < CURL_OFF_T_C(100000)) + msnprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes); + + else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE) + msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "k", bytes/ONE_KILOBYTE); + + else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE) + /* 'XX.XM' is good as long as we are less than 100 megs */ + msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0" + CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE, + (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) ); + + else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE) + /* 'XXXXM' is good until we are at 10000MB or above */ + msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); + + else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE) + /* 10000 MB - 100 GB, we show it as XX.XG */ + msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0" + CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE, + (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) ); + + else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE) + /* up to 10000GB, display without decimal: XXXXG */ + msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE); + + else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE) + /* up to 10000TB, display without decimal: XXXXT */ + msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "T", bytes/ONE_TERABYTE); + + else + /* up to 10000PB, display without decimal: XXXXP */ + msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE); + + /* 16384 petabytes (16 exabytes) is the maximum a 64-bit unsigned number can + hold, but our data type is signed so 8192PB will be the maximum. */ + + return max5; +} +#endif + +/* + + New proposed interface, 9th of February 2000: + + pgrsStartNow() - sets start time + pgrsSetDownloadSize(x) - known expected download size + pgrsSetUploadSize(x) - known expected upload size + pgrsSetDownloadCounter() - amount of data currently downloaded + pgrsSetUploadCounter() - amount of data currently uploaded + pgrsUpdate() - show progress + pgrsDone() - transfer complete + +*/ + +int Curl_pgrsDone(struct Curl_easy *data) +{ + int rc; + data->progress.lastshow = 0; + rc = Curl_pgrsUpdate(data); /* the final (forced) update */ + if(rc) + return rc; + + if(!(data->progress.flags & PGRS_HIDE) && + !data->progress.callback) + /* only output if we do not use a progress callback and we are not + * hidden */ + fprintf(data->set.err, "\n"); + + data->progress.speeder_c = 0; /* reset the progress meter display */ + return 0; +} + +/* reset the known transfer sizes */ +void Curl_pgrsResetTransferSizes(struct Curl_easy *data) +{ + Curl_pgrsSetDownloadSize(data, -1); + Curl_pgrsSetUploadSize(data, -1); +} + +/* + * + * Curl_pgrsTimeWas(). Store the timestamp time at the given label. + */ +void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, + struct curltime timestamp) +{ + timediff_t *delta = NULL; + + switch(timer) { + default: + case TIMER_NONE: + /* mistake filter */ + break; + case TIMER_STARTOP: + /* This is set at the start of a transfer */ + data->progress.t_startop = timestamp; + break; + case TIMER_STARTSINGLE: + /* This is set at the start of each single transfer */ + data->progress.t_startsingle = timestamp; + data->progress.is_t_startransfer_set = false; + break; + case TIMER_POSTQUEUE: + /* Set when the transfer starts (after potentially having been brought + back from the waiting queue). It needs to count from t_startop and not + t_startsingle since the latter is reset when a connection is brought + back from the pending queue. */ + data->progress.t_postqueue = + Curl_timediff_us(timestamp, data->progress.t_startop); + break; + case TIMER_STARTACCEPT: + data->progress.t_acceptdata = timestamp; + break; + case TIMER_NAMELOOKUP: + delta = &data->progress.t_nslookup; + break; + case TIMER_CONNECT: + delta = &data->progress.t_connect; + break; + case TIMER_APPCONNECT: + delta = &data->progress.t_appconnect; + break; + case TIMER_PRETRANSFER: + delta = &data->progress.t_pretransfer; + break; + case TIMER_STARTTRANSFER: + delta = &data->progress.t_starttransfer; + /* prevent updating t_starttransfer unless: + * 1) this is the first time we are setting t_starttransfer + * 2) a redirect has occurred since the last time t_starttransfer was set + * This prevents repeated invocations of the function from incorrectly + * changing the t_starttransfer time. + */ + if(data->progress.is_t_startransfer_set) { + return; + } + else { + data->progress.is_t_startransfer_set = true; + break; + } + case TIMER_POSTRANSFER: + /* this is the normal end-of-transfer thing */ + break; + case TIMER_REDIRECT: + data->progress.t_redirect = Curl_timediff_us(timestamp, + data->progress.start); + break; + } + if(delta) { + timediff_t us = Curl_timediff_us(timestamp, data->progress.t_startsingle); + if(us < 1) + us = 1; /* make sure at least one microsecond passed */ + *delta += us; + } +} + +/* + * + * Curl_pgrsTime(). Store the current time at the given label. This fetches a + * fresh "now" and returns it. + * + * @unittest: 1399 + */ +struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) +{ + struct curltime now = Curl_now(); + + Curl_pgrsTimeWas(data, timer, now); + return now; +} + +void Curl_pgrsStartNow(struct Curl_easy *data) +{ + data->progress.speeder_c = 0; /* reset the progress meter display */ + data->progress.start = Curl_now(); + data->progress.is_t_startransfer_set = false; + data->progress.ul_limit_start = data->progress.start; + data->progress.dl_limit_start = data->progress.start; + data->progress.ul_limit_size = 0; + data->progress.dl_limit_size = 0; + data->progress.downloaded = 0; + data->progress.uploaded = 0; + /* clear all bits except HIDE and HEADERS_OUT */ + data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT; + Curl_ratelimit(data, data->progress.start); +} + +/* + * This is used to handle speed limits, calculating how many milliseconds to + * wait until we are back under the speed limit, if needed. + * + * The way it works is by having a "starting point" (time & amount of data + * transferred by then) used in the speed computation, to be used instead of + * the start of the transfer. This starting point is regularly moved as + * transfer goes on, to keep getting accurate values (instead of average over + * the entire transfer). + * + * This function takes the current amount of data transferred, the amount at + * the starting point, the limit (in bytes/s), the time of the starting point + * and the current time. + * + * Returns 0 if no waiting is needed or when no waiting is needed but the + * starting point should be reset (to current); or the number of milliseconds + * to wait to get back under the speed limit. + */ +timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, + curl_off_t startsize, + curl_off_t limit, + struct curltime start, + struct curltime now) +{ + curl_off_t size = cursize - startsize; + timediff_t minimum; + timediff_t actual; + + if(!limit || !size) + return 0; + + /* + * 'minimum' is the number of milliseconds 'size' should take to download to + * stay below 'limit'. + */ + if(size < CURL_OFF_T_MAX/1000) + minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / limit); + else { + minimum = (timediff_t) (size / limit); + if(minimum < TIMEDIFF_T_MAX/1000) + minimum *= 1000; + else + minimum = TIMEDIFF_T_MAX; + } + + /* + * 'actual' is the time in milliseconds it took to actually download the + * last 'size' bytes. + */ + actual = Curl_timediff_ceil(now, start); + if(actual < minimum) { + /* if it downloaded the data faster than the limit, make it wait the + difference */ + return (minimum - actual); + } + + return 0; +} + +/* + * Set the number of downloaded bytes so far. + */ +CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) +{ + data->progress.downloaded = size; + return CURLE_OK; +} + +/* + * Update the timestamp and sizestamp to use for rate limit calculations. + */ +void Curl_ratelimit(struct Curl_easy *data, struct curltime now) +{ + /* do not set a new stamp unless the time since last update is long enough */ + if(data->set.max_recv_speed) { + if(Curl_timediff(now, data->progress.dl_limit_start) >= + MIN_RATE_LIMIT_PERIOD) { + data->progress.dl_limit_start = now; + data->progress.dl_limit_size = data->progress.downloaded; + } + } + if(data->set.max_send_speed) { + if(Curl_timediff(now, data->progress.ul_limit_start) >= + MIN_RATE_LIMIT_PERIOD) { + data->progress.ul_limit_start = now; + data->progress.ul_limit_size = data->progress.uploaded; + } + } +} + +/* + * Set the number of uploaded bytes so far. + */ +void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size) +{ + data->progress.uploaded = size; +} + +void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size) +{ + if(size >= 0) { + data->progress.size_dl = size; + data->progress.flags |= PGRS_DL_SIZE_KNOWN; + } + else { + data->progress.size_dl = 0; + data->progress.flags &= ~PGRS_DL_SIZE_KNOWN; + } +} + +void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size) +{ + if(size >= 0) { + data->progress.size_ul = size; + data->progress.flags |= PGRS_UL_SIZE_KNOWN; + } + else { + data->progress.size_ul = 0; + data->progress.flags &= ~PGRS_UL_SIZE_KNOWN; + } +} + +/* returns the average speed in bytes / second */ +static curl_off_t trspeed(curl_off_t size, /* number of bytes */ + curl_off_t us) /* microseconds */ +{ + if(us < 1) + return size * 1000000; + else if(size < CURL_OFF_T_MAX/1000000) + return (size * 1000000) / us; + else if(us >= 1000000) + return size / (us / 1000000); + else + return CURL_OFF_T_MAX; +} + +/* returns TRUE if it is time to show the progress meter */ +static bool progress_calc(struct Curl_easy *data, struct curltime now) +{ + bool timetoshow = FALSE; + struct Progress * const p = &data->progress; + + /* The time spent so far (from the start) in microseconds */ + p->timespent = Curl_timediff_us(now, p->start); + p->dlspeed = trspeed(p->downloaded, p->timespent); + p->ulspeed = trspeed(p->uploaded, p->timespent); + + /* Calculations done at most once a second, unless end is reached */ + if(p->lastshow != now.tv_sec) { + int countindex; /* amount of seconds stored in the speeder array */ + int nowindex = p->speeder_c% CURR_TIME; + p->lastshow = now.tv_sec; + timetoshow = TRUE; + + /* Let's do the "current speed" thing, with the dl + ul speeds + combined. Store the speed at entry 'nowindex'. */ + p->speeder[ nowindex ] = p->downloaded + p->uploaded; + + /* remember the exact time for this moment */ + p->speeder_time [ nowindex ] = now; + + /* advance our speeder_c counter, which is increased every time we get + here and we expect it to never wrap as 2^32 is a lot of seconds! */ + p->speeder_c++; + + /* figure out how many index entries of data we have stored in our speeder + array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of + transfer. Imagine, after one second we have filled in two entries, + after two seconds we have filled in three entries etc. */ + countindex = ((p->speeder_c >= CURR_TIME)? CURR_TIME:p->speeder_c) - 1; + + /* first of all, we do not do this if there is no counted seconds yet */ + if(countindex) { + int checkindex; + timediff_t span_ms; + curl_off_t amount; + + /* Get the index position to compare with the 'nowindex' position. + Get the oldest entry possible. While we have less than CURR_TIME + entries, the first entry will remain the oldest. */ + checkindex = (p->speeder_c >= CURR_TIME)? p->speeder_c%CURR_TIME:0; + + /* Figure out the exact time for the time span */ + span_ms = Curl_timediff(now, p->speeder_time[checkindex]); + if(0 == span_ms) + span_ms = 1; /* at least one millisecond MUST have passed */ + + /* Calculate the average speed the last 'span_ms' milliseconds */ + amount = p->speeder[nowindex]- p->speeder[checkindex]; + + if(amount > CURL_OFF_T_C(4294967) /* 0xffffffff/1000 */) + /* the 'amount' value is bigger than would fit in 32 bits if + multiplied with 1000, so we use the double math for this */ + p->current_speed = (curl_off_t) + ((double)amount/((double)span_ms/1000.0)); + else + /* the 'amount' value is small enough to fit within 32 bits even + when multiplied with 1000 */ + p->current_speed = amount*CURL_OFF_T_C(1000)/span_ms; + } + else + /* the first second we use the average */ + p->current_speed = p->ulspeed + p->dlspeed; + + } /* Calculations end */ + return timetoshow; +} + +#ifndef CURL_DISABLE_PROGRESS_METER +static void progress_meter(struct Curl_easy *data) +{ + char max5[6][10]; + curl_off_t dlpercen = 0; + curl_off_t ulpercen = 0; + curl_off_t total_percen = 0; + curl_off_t total_transfer; + curl_off_t total_expected_transfer; + char time_left[10]; + char time_total[10]; + char time_spent[10]; + curl_off_t ulestimate = 0; + curl_off_t dlestimate = 0; + curl_off_t total_estimate; + curl_off_t timespent = + (curl_off_t)data->progress.timespent/1000000; /* seconds */ + + if(!(data->progress.flags & PGRS_HEADERS_OUT)) { + if(data->state.resume_from) { + fprintf(data->set.err, + "** Resuming transfer from byte position %" + CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from); + } + fprintf(data->set.err, + " %% Total %% Received %% Xferd Average Speed " + "Time Time Time Current\n" + " Dload Upload " + "Total Spent Left Speed\n"); + data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */ + } + + /* Figure out the estimated time of arrival for the upload */ + if((data->progress.flags & PGRS_UL_SIZE_KNOWN) && + (data->progress.ulspeed > CURL_OFF_T_C(0))) { + ulestimate = data->progress.size_ul / data->progress.ulspeed; + + if(data->progress.size_ul > CURL_OFF_T_C(10000)) + ulpercen = data->progress.uploaded / + (data->progress.size_ul/CURL_OFF_T_C(100)); + else if(data->progress.size_ul > CURL_OFF_T_C(0)) + ulpercen = (data->progress.uploaded*100) / + data->progress.size_ul; + } + + /* ... and the download */ + if((data->progress.flags & PGRS_DL_SIZE_KNOWN) && + (data->progress.dlspeed > CURL_OFF_T_C(0))) { + dlestimate = data->progress.size_dl / data->progress.dlspeed; + + if(data->progress.size_dl > CURL_OFF_T_C(10000)) + dlpercen = data->progress.downloaded / + (data->progress.size_dl/CURL_OFF_T_C(100)); + else if(data->progress.size_dl > CURL_OFF_T_C(0)) + dlpercen = (data->progress.downloaded*100) / + data->progress.size_dl; + } + + /* Now figure out which of them is slower and use that one for the + total estimate! */ + total_estimate = ulestimate>dlestimate?ulestimate:dlestimate; + + /* create the three time strings */ + time2str(time_left, total_estimate > 0?(total_estimate - timespent):0); + time2str(time_total, total_estimate); + time2str(time_spent, timespent); + + /* Get the total amount of data expected to get transferred */ + total_expected_transfer = + ((data->progress.flags & PGRS_UL_SIZE_KNOWN)? + data->progress.size_ul:data->progress.uploaded)+ + ((data->progress.flags & PGRS_DL_SIZE_KNOWN)? + data->progress.size_dl:data->progress.downloaded); + + /* We have transferred this much so far */ + total_transfer = data->progress.downloaded + data->progress.uploaded; + + /* Get the percentage of data transferred so far */ + if(total_expected_transfer > CURL_OFF_T_C(10000)) + total_percen = total_transfer / + (total_expected_transfer/CURL_OFF_T_C(100)); + else if(total_expected_transfer > CURL_OFF_T_C(0)) + total_percen = (total_transfer*100) / total_expected_transfer; + + fprintf(data->set.err, + "\r" + "%3" CURL_FORMAT_CURL_OFF_T " %s " + "%3" CURL_FORMAT_CURL_OFF_T " %s " + "%3" CURL_FORMAT_CURL_OFF_T " %s %s %s %s %s %s %s", + total_percen, /* 3 letters */ /* total % */ + max5data(total_expected_transfer, max5[2]), /* total size */ + dlpercen, /* 3 letters */ /* rcvd % */ + max5data(data->progress.downloaded, max5[0]), /* rcvd size */ + ulpercen, /* 3 letters */ /* xfer % */ + max5data(data->progress.uploaded, max5[1]), /* xfer size */ + max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */ + max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */ + time_total, /* 8 letters */ /* total time */ + time_spent, /* 8 letters */ /* time spent */ + time_left, /* 8 letters */ /* time left */ + max5data(data->progress.current_speed, max5[5]) + ); + + /* we flush the output stream to make it appear as soon as possible */ + fflush(data->set.err); +} +#else + /* progress bar disabled */ +#define progress_meter(x) Curl_nop_stmt +#endif + + +/* + * Curl_pgrsUpdate() returns 0 for success or the value returned by the + * progress callback! + */ +static int pgrsupdate(struct Curl_easy *data, bool showprogress) +{ + if(!(data->progress.flags & PGRS_HIDE)) { + if(data->set.fxferinfo) { + int result; + /* There is a callback set, call that */ + Curl_set_in_callback(data, true); + result = data->set.fxferinfo(data->set.progress_client, + data->progress.size_dl, + data->progress.downloaded, + data->progress.size_ul, + data->progress.uploaded); + Curl_set_in_callback(data, false); + if(result != CURL_PROGRESSFUNC_CONTINUE) { + if(result) + failf(data, "Callback aborted"); + return result; + } + } + else if(data->set.fprogress) { + int result; + /* The older deprecated callback is set, call that */ + Curl_set_in_callback(data, true); + result = data->set.fprogress(data->set.progress_client, + (double)data->progress.size_dl, + (double)data->progress.downloaded, + (double)data->progress.size_ul, + (double)data->progress.uploaded); + Curl_set_in_callback(data, false); + if(result != CURL_PROGRESSFUNC_CONTINUE) { + if(result) + failf(data, "Callback aborted"); + return result; + } + } + + if(showprogress) + progress_meter(data); + } + + return 0; +} + +int Curl_pgrsUpdate(struct Curl_easy *data) +{ + struct curltime now = Curl_now(); /* what time is it */ + bool showprogress = progress_calc(data, now); + return pgrsupdate(data, showprogress); +} + +/* + * Update all progress, do not do progress meter/callbacks. + */ +void Curl_pgrsUpdate_nometer(struct Curl_easy *data) +{ + struct curltime now = Curl_now(); /* what time is it */ + (void)progress_calc(data, now); +} diff --git a/third_party/curl/lib/progress.h b/third_party/curl/lib/progress.h new file mode 100644 index 0000000000..860257425b --- /dev/null +++ b/third_party/curl/lib/progress.h @@ -0,0 +1,79 @@ +#ifndef HEADER_CURL_PROGRESS_H +#define HEADER_CURL_PROGRESS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "timeval.h" + + +typedef enum { + TIMER_NONE, + TIMER_STARTOP, + TIMER_STARTSINGLE, /* start of transfer, might get queued */ + TIMER_POSTQUEUE, /* start, immediately after dequeue */ + TIMER_NAMELOOKUP, + TIMER_CONNECT, + TIMER_APPCONNECT, + TIMER_PRETRANSFER, + TIMER_STARTTRANSFER, + TIMER_POSTRANSFER, + TIMER_STARTACCEPT, + TIMER_REDIRECT, + TIMER_LAST /* must be last */ +} timerid; + +int Curl_pgrsDone(struct Curl_easy *data); +void Curl_pgrsStartNow(struct Curl_easy *data); +void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size); +void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size); + +/* It is fine to not check the return code if 'size' is set to 0 */ +CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size); + +void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size); +void Curl_ratelimit(struct Curl_easy *data, struct curltime now); +int Curl_pgrsUpdate(struct Curl_easy *data); +void Curl_pgrsUpdate_nometer(struct Curl_easy *data); + +void Curl_pgrsResetTransferSizes(struct Curl_easy *data); +struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer); +timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, + curl_off_t startsize, + curl_off_t limit, + struct curltime start, + struct curltime now); +/** + * Update progress timer with the elapsed time from its start to `timestamp`. + * This allows updating timers later and is used by happy eyeballing, where + * we only want to record the winner's times. + */ +void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, + struct curltime timestamp); + +#define PGRS_HIDE (1<<4) +#define PGRS_UL_SIZE_KNOWN (1<<5) +#define PGRS_DL_SIZE_KNOWN (1<<6) +#define PGRS_HEADERS_OUT (1<<7) /* set when the headers have been written */ + +#endif /* HEADER_CURL_PROGRESS_H */ diff --git a/third_party/curl/lib/psl.c b/third_party/curl/lib/psl.c new file mode 100644 index 0000000000..626a203a6d --- /dev/null +++ b/third_party/curl/lib/psl.c @@ -0,0 +1,113 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#ifdef USE_LIBPSL + +#include "psl.h" +#include "share.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +void Curl_psl_destroy(struct PslCache *pslcache) +{ + if(pslcache->psl) { + if(pslcache->dynamic) + psl_free((psl_ctx_t *) pslcache->psl); + pslcache->psl = NULL; + pslcache->dynamic = FALSE; + } +} + +static time_t now_seconds(void) +{ + struct curltime now = Curl_now(); + + return now.tv_sec; +} + +const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) +{ + struct PslCache *pslcache = easy->psl; + const psl_ctx_t *psl; + time_t now; + + if(!pslcache) + return NULL; + + Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SHARED); + now = now_seconds(); + if(!pslcache->psl || pslcache->expires <= now) { + /* Let a chance to other threads to do the job: avoids deadlock. */ + Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); + + /* Update cache: this needs an exclusive lock. */ + Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SINGLE); + + /* Recheck in case another thread did the job. */ + now = now_seconds(); + if(!pslcache->psl || pslcache->expires <= now) { + bool dynamic = FALSE; + time_t expires = TIME_T_MAX; + +#if defined(PSL_VERSION_NUMBER) && PSL_VERSION_NUMBER >= 0x001000 + psl = psl_latest(NULL); + dynamic = psl != NULL; + /* Take care of possible time computation overflow. */ + expires = now < TIME_T_MAX - PSL_TTL? now + PSL_TTL: TIME_T_MAX; + + /* Only get the built-in PSL if we do not already have the "latest". */ + if(!psl && !pslcache->dynamic) +#endif + + psl = psl_builtin(); + + if(psl) { + Curl_psl_destroy(pslcache); + pslcache->psl = psl; + pslcache->dynamic = dynamic; + pslcache->expires = expires; + } + } + Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); /* Release exclusive lock. */ + Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SHARED); + } + psl = pslcache->psl; + if(!psl) + Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); + return psl; +} + +void Curl_psl_release(struct Curl_easy *easy) +{ + Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); +} + +#endif /* USE_LIBPSL */ diff --git a/third_party/curl/lib/psl.h b/third_party/curl/lib/psl.h new file mode 100644 index 0000000000..23cfa921c4 --- /dev/null +++ b/third_party/curl/lib/psl.h @@ -0,0 +1,49 @@ +#ifndef HEADER_PSL_H +#define HEADER_PSL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#ifdef USE_LIBPSL +#include + +#define PSL_TTL (72 * 3600) /* PSL time to live before a refresh. */ + +struct PslCache { + const psl_ctx_t *psl; /* The PSL. */ + time_t expires; /* Time this PSL life expires. */ + bool dynamic; /* PSL should be released when no longer needed. */ +}; + +const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy); +void Curl_psl_release(struct Curl_easy *easy); +void Curl_psl_destroy(struct PslCache *pslcache); + +#else + +#define Curl_psl_use(easy) NULL +#define Curl_psl_release(easy) +#define Curl_psl_destroy(pslcache) + +#endif /* USE_LIBPSL */ +#endif /* HEADER_PSL_H */ diff --git a/third_party/curl/lib/rand.c b/third_party/curl/lib/rand.c new file mode 100644 index 0000000000..4ee96fbb65 --- /dev/null +++ b/third_party/curl/lib/rand.c @@ -0,0 +1,292 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#include +#include "urldata.h" +#include "vtls/vtls.h" +#include "sendf.h" +#include "timeval.h" +#include "rand.h" +#include "escape.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#ifdef _WIN32 + +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 && \ + !defined(CURL_WINDOWS_APP) +# define HAVE_WIN_BCRYPTGENRANDOM +# include +# ifdef _MSC_VER +# pragma comment(lib, "bcrypt.lib") +# endif +# ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG +# define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002 +# endif +# ifndef STATUS_SUCCESS +# define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +# endif +#elif defined(USE_WIN32_CRYPTO) +# include +# ifdef _MSC_VER +# pragma comment(lib, "advapi32.lib") +# endif +#endif + +CURLcode Curl_win32_random(unsigned char *entropy, size_t length) +{ + memset(entropy, 0, length); + +#if defined(HAVE_WIN_BCRYPTGENRANDOM) + if(BCryptGenRandom(NULL, entropy, (ULONG)length, + BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS) + return CURLE_FAILED_INIT; + + return CURLE_OK; +#elif defined(USE_WIN32_CRYPTO) + { + HCRYPTPROV hCryptProv = 0; + + if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return CURLE_FAILED_INIT; + + if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) { + CryptReleaseContext(hCryptProv, 0UL); + return CURLE_FAILED_INIT; + } + + CryptReleaseContext(hCryptProv, 0UL); + } + return CURLE_OK; +#else + return CURLE_NOT_BUILT_IN; +#endif +} +#endif + +static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) +{ + CURLcode result = CURLE_OK; + static unsigned int randseed; + static bool seeded = FALSE; + +#ifdef DEBUGBUILD + char *force_entropy = getenv("CURL_ENTROPY"); + if(force_entropy) { + if(!seeded) { + unsigned int seed = 0; + size_t elen = strlen(force_entropy); + size_t clen = sizeof(seed); + size_t min = elen < clen ? elen : clen; + memcpy((char *)&seed, force_entropy, min); + randseed = ntohl(seed); + seeded = TRUE; + } + else + randseed++; + *rnd = randseed; + return CURLE_OK; + } +#endif + + /* data may be NULL! */ + result = Curl_ssl_random(data, (unsigned char *)rnd, sizeof(*rnd)); + if(result != CURLE_NOT_BUILT_IN) + /* only if there is no random function in the TLS backend do the non crypto + version, otherwise return result */ + return result; + + /* ---- non-cryptographic version following ---- */ + +#ifdef _WIN32 + if(!seeded) { + result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd)); + if(result != CURLE_NOT_BUILT_IN) + return result; + } +#endif + +#if defined(HAVE_ARC4RANDOM) && !defined(USE_OPENSSL) + if(!seeded) { + *rnd = (unsigned int)arc4random(); + return CURLE_OK; + } +#endif + +#if defined(RANDOM_FILE) && !defined(_WIN32) + if(!seeded) { + /* if there is a random file to read a seed from, use it */ + int fd = open(RANDOM_FILE, O_RDONLY); + if(fd > -1) { + /* read random data into the randseed variable */ + ssize_t nread = read(fd, &randseed, sizeof(randseed)); + if(nread == sizeof(randseed)) + seeded = TRUE; + close(fd); + } + } +#endif + + if(!seeded) { + struct curltime now = Curl_now(); + infof(data, "WARNING: using weak random seed"); + randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec; + randseed = randseed * 1103515245 + 12345; + randseed = randseed * 1103515245 + 12345; + randseed = randseed * 1103515245 + 12345; + seeded = TRUE; + } + + { + unsigned int r; + /* Return an unsigned 32-bit pseudo-random number. */ + r = randseed = randseed * 1103515245 + 12345; + *rnd = (r << 16) | ((r >> 16) & 0xFFFF); + } + return CURLE_OK; +} + +/* + * Curl_rand() stores 'num' number of random unsigned characters in the buffer + * 'rnd' points to. + * + * If libcurl is built without TLS support or with a TLS backend that lacks a + * proper random API (rustls or mbedTLS), this function will use "weak" + * random. + * + * When built *with* TLS support and a backend that offers strong random, it + * will return error if it cannot provide strong random values. + * + * NOTE: 'data' may be passed in as NULL when coming from external API without + * easy handle! + * + */ + +CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num) +{ + CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; + + DEBUGASSERT(num); + + while(num) { + unsigned int r; + size_t left = num < sizeof(unsigned int) ? num : sizeof(unsigned int); + + result = randit(data, &r); + if(result) + return result; + + while(left) { + *rnd++ = (unsigned char)(r & 0xFF); + r >>= 8; + --num; + --left; + } + } + + return result; +} + +/* + * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random + * hexadecimal digits PLUS a null-terminating byte. It must be an odd number + * size. + */ + +CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, + size_t num) +{ + CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; + unsigned char buffer[128]; + DEBUGASSERT(num > 1); + +#ifdef __clang_analyzer__ + /* This silences a scan-build warning about accessing this buffer with + uninitialized memory. */ + memset(buffer, 0, sizeof(buffer)); +#endif + + if((num/2 >= sizeof(buffer)) || !(num&1)) { + /* make sure it fits in the local buffer and that it is an odd number! */ + DEBUGF(infof(data, "invalid buffer size with Curl_rand_hex")); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + num--; /* save one for null-termination */ + + result = Curl_rand(data, buffer, num/2); + if(result) + return result; + + Curl_hexencode(buffer, num/2, rnd, num + 1); + return result; +} + +/* + * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random + * alphanumerical chars PLUS a null-terminating byte. + */ + +static const char alnum[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, + size_t num) +{ + CURLcode result = CURLE_OK; + const unsigned int alnumspace = sizeof(alnum) - 1; + unsigned int r; + DEBUGASSERT(num > 1); + + num--; /* save one for null-termination */ + + while(num) { + do { + result = randit(data, &r); + if(result) + return result; + } while(r >= (UINT_MAX - UINT_MAX % alnumspace)); + + *rnd++ = (unsigned char)alnum[r % alnumspace]; + num--; + } + *rnd = 0; + + return result; +} diff --git a/third_party/curl/lib/rand.h b/third_party/curl/lib/rand.h new file mode 100644 index 0000000000..bc05239e45 --- /dev/null +++ b/third_party/curl/lib/rand.h @@ -0,0 +1,50 @@ +#ifndef HEADER_CURL_RAND_H +#define HEADER_CURL_RAND_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num); + +/* + * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random + * hexadecimal digits PLUS a null-terminating byte. It must be an odd number + * size. + */ +CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, + size_t num); + +/* + * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random + * alphanumerical chars PLUS a null-terminating byte. + */ +CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, + size_t num); + +#ifdef _WIN32 +/* Random generator shared between the Schannel vtls and Curl_rand*() + functions */ +CURLcode Curl_win32_random(unsigned char *entropy, size_t length); +#endif + +#endif /* HEADER_CURL_RAND_H */ diff --git a/third_party/curl/lib/rename.c b/third_party/curl/lib/rename.c new file mode 100644 index 0000000000..8715a4306d --- /dev/null +++ b/third_party/curl/lib/rename.c @@ -0,0 +1,73 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "rename.h" + +#include "curl_setup.h" + +#if (!defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES)) || \ + !defined(CURL_DISABLE_ALTSVC) + +#include "curl_multibyte.h" +#include "timeval.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* return 0 on success, 1 on error */ +int Curl_rename(const char *oldpath, const char *newpath) +{ +#ifdef _WIN32 + /* rename() on Windows does not overwrite, so we cannot use it here. + MoveFileEx() will overwrite and is usually atomic, however it fails + when there are open handles to the file. */ + const int max_wait_ms = 1000; + struct curltime start = Curl_now(); + TCHAR *tchar_oldpath = curlx_convert_UTF8_to_tchar((char *)oldpath); + TCHAR *tchar_newpath = curlx_convert_UTF8_to_tchar((char *)newpath); + for(;;) { + timediff_t diff; + if(MoveFileEx(tchar_oldpath, tchar_newpath, MOVEFILE_REPLACE_EXISTING)) { + curlx_unicodefree(tchar_oldpath); + curlx_unicodefree(tchar_newpath); + break; + } + diff = Curl_timediff(Curl_now(), start); + if(diff < 0 || diff > max_wait_ms) { + curlx_unicodefree(tchar_oldpath); + curlx_unicodefree(tchar_newpath); + return 1; + } + Sleep(1); + } +#else + if(rename(oldpath, newpath)) + return 1; +#endif + return 0; +} + +#endif diff --git a/third_party/curl/lib/rename.h b/third_party/curl/lib/rename.h new file mode 100644 index 0000000000..04440820c5 --- /dev/null +++ b/third_party/curl/lib/rename.h @@ -0,0 +1,29 @@ +#ifndef HEADER_CURL_RENAME_H +#define HEADER_CURL_RENAME_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +int Curl_rename(const char *oldpath, const char *newpath); + +#endif /* HEADER_CURL_RENAME_H */ diff --git a/third_party/curl/lib/request.c b/third_party/curl/lib/request.c new file mode 100644 index 0000000000..5c8d52f696 --- /dev/null +++ b/third_party/curl/lib/request.c @@ -0,0 +1,411 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "urldata.h" +#include "cfilters.h" +#include "dynbuf.h" +#include "doh.h" +#include "multiif.h" +#include "progress.h" +#include "request.h" +#include "sendf.h" +#include "transfer.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" + +void Curl_req_init(struct SingleRequest *req) +{ + memset(req, 0, sizeof(*req)); +} + +CURLcode Curl_req_soft_reset(struct SingleRequest *req, + struct Curl_easy *data) +{ + CURLcode result; + + req->done = FALSE; + req->upload_done = FALSE; + req->download_done = FALSE; + req->ignorebody = FALSE; + req->shutdown = FALSE; + req->bytecount = 0; + req->writebytecount = 0; + req->header = TRUE; /* assume header */ + req->headerline = 0; + req->headerbytecount = 0; + req->allheadercount = 0; + req->deductheadercount = 0; + + result = Curl_client_start(data); + if(result) + return result; + + if(!req->sendbuf_init) { + Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1, + BUFQ_OPT_SOFT_LIMIT); + req->sendbuf_init = TRUE; + } + else { + Curl_bufq_reset(&req->sendbuf); + if(data->set.upload_buffer_size != req->sendbuf.chunk_size) { + Curl_bufq_free(&req->sendbuf); + Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1, + BUFQ_OPT_SOFT_LIMIT); + } + } + + return CURLE_OK; +} + +CURLcode Curl_req_start(struct SingleRequest *req, + struct Curl_easy *data) +{ + req->start = Curl_now(); + return Curl_req_soft_reset(req, data); +} + +static CURLcode req_flush(struct Curl_easy *data); + +CURLcode Curl_req_done(struct SingleRequest *req, + struct Curl_easy *data, bool aborted) +{ + (void)req; + if(!aborted) + (void)req_flush(data); + Curl_client_reset(data); + return CURLE_OK; +} + +void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) +{ + struct curltime t0 = {0, 0}; + + /* This is a bit ugly. `req->p` is a union and we assume we can + * free this safely without leaks. */ + Curl_safefree(req->p.ftp); + Curl_safefree(req->newurl); + Curl_client_reset(data); + if(req->sendbuf_init) + Curl_bufq_reset(&req->sendbuf); + +#ifndef CURL_DISABLE_DOH + Curl_doh_close(data); +#endif + /* Can no longer memset() this struct as we need to keep some state */ + req->size = -1; + req->maxdownload = -1; + req->bytecount = 0; + req->writebytecount = 0; + req->start = t0; + req->headerbytecount = 0; + req->allheadercount = 0; + req->deductheadercount = 0; + req->headerline = 0; + req->offset = 0; + req->httpcode = 0; + req->keepon = 0; + req->upgr101 = UPGR101_INIT; + req->timeofdoc = 0; + req->location = NULL; + req->newurl = NULL; +#ifndef CURL_DISABLE_COOKIES + req->setcookies = 0; +#endif + req->header = FALSE; + req->content_range = FALSE; + req->download_done = FALSE; + req->eos_written = FALSE; + req->eos_read = FALSE; + req->upload_done = FALSE; + req->upload_aborted = FALSE; + req->ignorebody = FALSE; + req->http_bodyless = FALSE; + req->chunk = FALSE; + req->ignore_cl = FALSE; + req->upload_chunky = FALSE; + req->getheader = FALSE; + req->no_body = data->set.opt_no_body; + req->authneg = FALSE; + req->shutdown = FALSE; +#ifdef USE_HYPER + req->bodywritten = FALSE; +#endif +} + +void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data) +{ + /* This is a bit ugly. `req->p` is a union and we assume we can + * free this safely without leaks. */ + Curl_safefree(req->p.ftp); + Curl_safefree(req->newurl); + if(req->sendbuf_init) + Curl_bufq_free(&req->sendbuf); + Curl_client_cleanup(data); + +#ifndef CURL_DISABLE_DOH + Curl_doh_cleanup(data); +#endif +} + +static CURLcode xfer_send(struct Curl_easy *data, + const char *buf, size_t blen, + size_t hds_len, size_t *pnwritten) +{ + CURLcode result = CURLE_OK; + + *pnwritten = 0; +#ifdef DEBUGBUILD + { + /* Allow debug builds to override this logic to force short initial + sends + */ + char *p = getenv("CURL_SMALLREQSEND"); + if(p) { + size_t altsize = (size_t)strtoul(p, NULL, 10); + if(altsize && altsize < blen) + blen = altsize; + } + } +#endif + /* Make sure this does not send more body bytes than what the max send + speed says. The headers do not count to the max speed. */ + if(data->set.max_send_speed) { + size_t body_bytes = blen - hds_len; + if((curl_off_t)body_bytes > data->set.max_send_speed) + blen = hds_len + (size_t)data->set.max_send_speed; + } + + result = Curl_xfer_send(data, buf, blen, pnwritten); + if(!result && *pnwritten) { + if(hds_len) + Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf, + CURLMIN(hds_len, *pnwritten)); + if(*pnwritten > hds_len) { + size_t body_len = *pnwritten - hds_len; + Curl_debug(data, CURLINFO_DATA_OUT, (char *)buf + hds_len, body_len); + data->req.writebytecount += body_len; + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); + } + } + return result; +} + +static CURLcode req_send_buffer_flush(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + const unsigned char *buf; + size_t blen; + + while(Curl_bufq_peek(&data->req.sendbuf, &buf, &blen)) { + size_t nwritten, hds_len = CURLMIN(data->req.sendbuf_hds_len, blen); + result = xfer_send(data, (const char *)buf, blen, hds_len, &nwritten); + if(result) + break; + + Curl_bufq_skip(&data->req.sendbuf, nwritten); + if(hds_len) { + data->req.sendbuf_hds_len -= CURLMIN(hds_len, nwritten); + } + /* leave if we could not send all. Maybe network blocking or + * speed limits on transfer */ + if(nwritten < blen) + break; + } + return result; +} + +static CURLcode req_set_upload_done(struct Curl_easy *data) +{ + DEBUGASSERT(!data->req.upload_done); + data->req.upload_done = TRUE; + data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we are done sending */ + + Curl_creader_done(data, data->req.upload_aborted); + + if(data->req.upload_aborted) { + if(data->req.writebytecount) + infof(data, "abort upload after having sent %" CURL_FORMAT_CURL_OFF_T + " bytes", data->req.writebytecount); + else + infof(data, "abort upload"); + } + else if(data->req.writebytecount) + infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T + " bytes", data->req.writebytecount); + else if(!data->req.download_done) + infof(data, Curl_creader_total_length(data)? + "We are completely uploaded and fine" : + "Request completely sent off"); + + return Curl_xfer_send_close(data); +} + +static CURLcode req_flush(struct Curl_easy *data) +{ + CURLcode result; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + + if(!Curl_bufq_is_empty(&data->req.sendbuf)) { + result = req_send_buffer_flush(data); + if(result) + return result; + if(!Curl_bufq_is_empty(&data->req.sendbuf)) { + return CURLE_AGAIN; + } + } + + if(!data->req.upload_done && data->req.eos_read && + Curl_bufq_is_empty(&data->req.sendbuf)) { + if(data->req.shutdown) { + bool done; + result = Curl_xfer_send_shutdown(data, &done); + if(result) + return result; + if(!done) + return CURLE_AGAIN; + } + return req_set_upload_done(data); + } + return CURLE_OK; +} + +static ssize_t add_from_client(void *reader_ctx, + unsigned char *buf, size_t buflen, + CURLcode *err) +{ + struct Curl_easy *data = reader_ctx; + size_t nread; + bool eos; + + *err = Curl_client_read(data, (char *)buf, buflen, &nread, &eos); + if(*err) + return -1; + if(eos) + data->req.eos_read = TRUE; + return (ssize_t)nread; +} + +#ifndef USE_HYPER + +static CURLcode req_send_buffer_add(struct Curl_easy *data, + const char *buf, size_t blen, + size_t hds_len) +{ + CURLcode result = CURLE_OK; + ssize_t n; + n = Curl_bufq_write(&data->req.sendbuf, + (const unsigned char *)buf, blen, &result); + if(n < 0) + return result; + /* We rely on a SOFTLIMIT on sendbuf, so it can take all data in */ + DEBUGASSERT((size_t)n == blen); + data->req.sendbuf_hds_len += hds_len; + return CURLE_OK; +} + +CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req) +{ + CURLcode result; + const char *buf; + size_t blen, nwritten; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + + buf = Curl_dyn_ptr(req); + blen = Curl_dyn_len(req); + if(!Curl_creader_total_length(data)) { + /* Request without body. Try to send directly from the buf given. */ + data->req.eos_read = TRUE; + result = xfer_send(data, buf, blen, blen, &nwritten); + if(result) + return result; + buf += nwritten; + blen -= nwritten; + } + + if(blen) { + /* Either we have a request body, or we could not send the complete + * request in one go. Buffer the remainder and try to add as much + * body bytes as room is left in the buffer. Then flush. */ + result = req_send_buffer_add(data, buf, blen, blen); + if(result) + return result; + + return Curl_req_send_more(data); + } + return CURLE_OK; +} +#endif /* !USE_HYPER */ + +bool Curl_req_want_send(struct Curl_easy *data) +{ + return data->req.sendbuf_init && !Curl_bufq_is_empty(&data->req.sendbuf); +} + +bool Curl_req_done_sending(struct Curl_easy *data) +{ + if(data->req.upload_done) { + DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf)); + return TRUE; + } + return FALSE; +} + +CURLcode Curl_req_send_more(struct Curl_easy *data) +{ + CURLcode result; + + /* Fill our send buffer if more from client can be read. */ + if(!data->req.eos_read && !Curl_bufq_is_full(&data->req.sendbuf)) { + ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0, + add_from_client, data, &result); + if(nread < 0 && result != CURLE_AGAIN) + return result; + } + + result = req_flush(data); + if(result == CURLE_AGAIN) + result = CURLE_OK; + + return result; +} + +CURLcode Curl_req_abort_sending(struct Curl_easy *data) +{ + if(!data->req.upload_done) { + Curl_bufq_reset(&data->req.sendbuf); + data->req.upload_aborted = TRUE; + return req_set_upload_done(data); + } + return CURLE_OK; +} diff --git a/third_party/curl/lib/request.h b/third_party/curl/lib/request.h new file mode 100644 index 0000000000..413dacaa49 --- /dev/null +++ b/third_party/curl/lib/request.h @@ -0,0 +1,229 @@ +#ifndef HEADER_CURL_REQUEST_H +#define HEADER_CURL_REQUEST_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* This file is for lib internal stuff */ + +#include "curl_setup.h" + +#include "bufq.h" + +/* forward declarations */ +struct UserDefined; + +enum expect100 { + EXP100_SEND_DATA, /* enough waiting, just send the body now */ + EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */ + EXP100_SENDING_REQUEST, /* still sending the request but will wait for + the 100 header once done with the request */ + EXP100_FAILED /* used on 417 Expectation Failed */ +}; + +enum upgrade101 { + UPGR101_INIT, /* default state */ + UPGR101_WS, /* upgrade to WebSockets requested */ + UPGR101_H2, /* upgrade to HTTP/2 requested */ + UPGR101_RECEIVED, /* 101 response received */ + UPGR101_WORKING /* talking upgraded protocol */ +}; + + +/* + * Request specific data in the easy handle (Curl_easy). Previously, + * these members were on the connectdata struct but since a conn struct may + * now be shared between different Curl_easys, we store connection-specific + * data here. This struct only keeps stuff that is interesting for *this* + * request, as it will be cleared between multiple ones + */ +struct SingleRequest { + curl_off_t size; /* -1 if unknown at this point */ + curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch, + -1 means unlimited */ + curl_off_t bytecount; /* total number of bytes read */ + curl_off_t writebytecount; /* number of bytes written */ + + struct curltime start; /* transfer started at this time */ + unsigned int headerbytecount; /* received server headers (not CONNECT + headers) */ + unsigned int allheadercount; /* all received headers (server + CONNECT) */ + unsigned int deductheadercount; /* this amount of bytes does not count when + we check if anything has been transferred + at the end of a connection. We use this + counter to make only a 100 reply (without + a following second response code) result + in a CURLE_GOT_NOTHING error code */ + int headerline; /* counts header lines to better track the + first one */ + curl_off_t offset; /* possible resume offset read from the + Content-Range: header */ + int httpversion; /* Version in response (09, 10, 11, etc.) */ + int httpcode; /* error code from the 'HTTP/1.? XXX' or + 'RTSP/1.? XXX' line */ + int keepon; + enum upgrade101 upgr101; /* 101 upgrade state */ + + /* Client Writer stack, handles transfer- and content-encodings, protocol + * checks, pausing by client callbacks. */ + struct Curl_cwriter *writer_stack; + /* Client Reader stack, handles transfer- and content-encodings, protocol + * checks, pausing by client callbacks. */ + struct Curl_creader *reader_stack; + struct bufq sendbuf; /* data which needs to be send to the server */ + size_t sendbuf_hds_len; /* amount of header bytes in sendbuf */ + time_t timeofdoc; + char *location; /* This points to an allocated version of the Location: + header data */ + char *newurl; /* Set to the new URL to use when a redirect or a retry is + wanted */ + + /* Allocated protocol-specific data. Each protocol handler makes sure this + points to data it needs. */ + union { + struct FILEPROTO *file; + struct FTP *ftp; + struct IMAP *imap; + struct ldapreqinfo *ldap; + struct MQTT *mqtt; + struct POP3 *pop3; + struct RTSP *rtsp; + struct smb_request *smb; + struct SMTP *smtp; + struct SSHPROTO *ssh; + struct TELNET *telnet; + } p; +#ifndef CURL_DISABLE_DOH + struct dohdata *doh; /* DoH specific data for this request */ +#endif +#ifndef CURL_DISABLE_COOKIES + unsigned char setcookies; +#endif + BIT(header); /* incoming data has HTTP header */ + BIT(done); /* request is done, e.g. no more send/recv should + * happen. This can be TRUE before `upload_done` or + * `download_done` is TRUE. */ + BIT(content_range); /* set TRUE if Content-Range: was found */ + BIT(download_done); /* set to TRUE when download is complete */ + BIT(eos_written); /* iff EOS has been written to client */ + BIT(eos_read); /* iff EOS has been read from the client */ + BIT(rewind_read); /* iff reader needs rewind at next start */ + BIT(upload_done); /* set to TRUE when all request data has been sent */ + BIT(upload_aborted); /* set to TRUE when upload was aborted. Will also + * show `upload_done` as TRUE. */ + BIT(ignorebody); /* we read a response-body but we ignore it! */ + BIT(http_bodyless); /* HTTP response status code is between 100 and 199, + 204 or 304 */ + BIT(chunk); /* if set, this is a chunked transfer-encoding */ + BIT(ignore_cl); /* ignore content-length */ + BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding + on upload */ + BIT(getheader); /* TRUE if header parsing is wanted */ + BIT(no_body); /* the response has no body */ + BIT(authneg); /* TRUE when the auth phase has started, which means + that we are creating a request with an auth header, + but it is not the final request in the auth + negotiation. */ + BIT(sendbuf_init); /* sendbuf is initialized */ + BIT(shutdown); /* request end will shutdown connection */ +#ifdef USE_HYPER + BIT(bodywritten); +#endif +}; + +/** + * Initialize the state of the request for first use. + */ +void Curl_req_init(struct SingleRequest *req); + +/** + * The request is about to start. Record time and do a soft reset. + */ +CURLcode Curl_req_start(struct SingleRequest *req, + struct Curl_easy *data); + +/** + * The request may continue with a follow up. Reset + * members, but keep start time for overall duration calc. + */ +CURLcode Curl_req_soft_reset(struct SingleRequest *req, + struct Curl_easy *data); + +/** + * The request is done. If not aborted, make sure that buffers are + * flushed to the client. + * @param req the request + * @param data the transfer + * @param aborted TRUE iff the request was aborted/errored + */ +CURLcode Curl_req_done(struct SingleRequest *req, + struct Curl_easy *data, bool aborted); + +/** + * Free the state of the request, not usable afterwards. + */ +void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data); + +/** + * Hard reset the state of the request to virgin state base on + * transfer settings. + */ +void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data); + +#ifndef USE_HYPER +/** + * Send request headers. If not all could be sent + * they will be buffered. Use `Curl_req_flush()` to make sure + * bytes are really send. + * @param data the transfer making the request + * @param buf the complete header bytes, no body + * @return CURLE_OK (on blocking with *pnwritten == 0) or error. + */ +CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf); + +#endif /* !USE_HYPER */ + +/** + * TRUE iff the request has sent all request headers and data. + */ +bool Curl_req_done_sending(struct Curl_easy *data); + +/* + * Read more from client and flush all buffered request bytes. + * @return CURLE_OK on success or the error on the sending. + * Never returns CURLE_AGAIN. + */ +CURLcode Curl_req_send_more(struct Curl_easy *data); + +/** + * TRUE iff the request wants to send, e.g. has buffered bytes. + */ +bool Curl_req_want_send(struct Curl_easy *data); + +/** + * Stop sending any more request data to the server. + * Will clear the send buffer and mark request sending as done. + */ +CURLcode Curl_req_abort_sending(struct Curl_easy *data); + +#endif /* HEADER_CURL_REQUEST_H */ diff --git a/third_party/curl/lib/rtsp.c b/third_party/curl/lib/rtsp.c new file mode 100644 index 0000000000..a9050806d6 --- /dev/null +++ b/third_party/curl/lib/rtsp.c @@ -0,0 +1,1042 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_RTSP) && !defined(USE_HYPER) + +#include "urldata.h" +#include +#include "transfer.h" +#include "sendf.h" +#include "multiif.h" +#include "http.h" +#include "url.h" +#include "progress.h" +#include "rtsp.h" +#include "strcase.h" +#include "select.h" +#include "connect.h" +#include "cfilters.h" +#include "strdup.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \ + ((unsigned int)((unsigned char)((p)[3])))) + +/* protocol-specific functions set up to be called by the main engine */ +static CURLcode rtsp_do(struct Curl_easy *data, bool *done); +static CURLcode rtsp_done(struct Curl_easy *data, CURLcode, bool premature); +static CURLcode rtsp_connect(struct Curl_easy *data, bool *done); +static CURLcode rtsp_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead); +static int rtsp_getsock_do(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); + +/* + * Parse and write out an RTSP response. + * @param data the transfer + * @param conn the connection + * @param buf data read from connection + * @param blen amount of data in buf + * @param is_eos TRUE iff this is the last write + * @param readmore out, TRUE iff complete buf was consumed and more data + * is needed + */ +static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, + const char *buf, + size_t blen, + bool is_eos); + +static CURLcode rtsp_setup_connection(struct Curl_easy *data, + struct connectdata *conn); +static unsigned int rtsp_conncheck(struct Curl_easy *data, + struct connectdata *check, + unsigned int checks_to_perform); + +/* this returns the socket to wait for in the DO and DOING state for the multi + interface and then we are always _sending_ a request and thus we wait for + the single socket to become writable only */ +static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t *socks) +{ + /* write mode */ + (void)data; + socks[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_WRITESOCK(0); +} + +static +CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len); +static +CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport); + + +/* + * RTSP handler interface. + */ +const struct Curl_handler Curl_handler_rtsp = { + "rtsp", /* scheme */ + rtsp_setup_connection, /* setup_connection */ + rtsp_do, /* do_it */ + rtsp_done, /* done */ + ZERO_NULL, /* do_more */ + rtsp_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + rtsp_getsock_do, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + rtsp_disconnect, /* disconnect */ + rtsp_rtp_write_resp, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + rtsp_conncheck, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_RTSP, /* defport */ + CURLPROTO_RTSP, /* protocol */ + CURLPROTO_RTSP, /* family */ + PROTOPT_NONE /* flags */ +}; + +#define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */ + +static CURLcode rtsp_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + struct RTSP *rtsp; + (void)conn; + + data->req.p.rtsp = rtsp = calloc(1, sizeof(struct RTSP)); + if(!rtsp) + return CURLE_OUT_OF_MEMORY; + + Curl_dyn_init(&conn->proto.rtspc.buf, MAX_RTP_BUFFERSIZE); + return CURLE_OK; +} + + +/* + * Function to check on various aspects of a connection. + */ +static unsigned int rtsp_conncheck(struct Curl_easy *data, + struct connectdata *conn, + unsigned int checks_to_perform) +{ + unsigned int ret_val = CONNRESULT_NONE; + (void)data; + + if(checks_to_perform & CONNCHECK_ISDEAD) { + bool input_pending; + if(!Curl_conn_is_alive(data, conn, &input_pending)) + ret_val |= CONNRESULT_DEAD; + } + + return ret_val; +} + + +static CURLcode rtsp_connect(struct Curl_easy *data, bool *done) +{ + CURLcode httpStatus; + + httpStatus = Curl_http_connect(data, done); + + /* Initialize the CSeq if not already done */ + if(data->state.rtsp_next_client_CSeq == 0) + data->state.rtsp_next_client_CSeq = 1; + if(data->state.rtsp_next_server_CSeq == 0) + data->state.rtsp_next_server_CSeq = 1; + + data->conn->proto.rtspc.rtp_channel = -1; + + return httpStatus; +} + +static CURLcode rtsp_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead) +{ + (void) dead; + (void) data; + Curl_dyn_free(&conn->proto.rtspc.buf); + return CURLE_OK; +} + + +static CURLcode rtsp_done(struct Curl_easy *data, + CURLcode status, bool premature) +{ + struct RTSP *rtsp = data->req.p.rtsp; + CURLcode httpStatus; + + /* Bypass HTTP empty-reply checks on receive */ + if(data->set.rtspreq == RTSPREQ_RECEIVE) + premature = TRUE; + + httpStatus = Curl_http_done(data, status, premature); + + if(rtsp && !status && !httpStatus) { + /* Check the sequence numbers */ + long CSeq_sent = rtsp->CSeq_sent; + long CSeq_recv = rtsp->CSeq_recv; + if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) { + failf(data, + "The CSeq of this request %ld did not match the response %ld", + CSeq_sent, CSeq_recv); + return CURLE_RTSP_CSEQ_ERROR; + } + if(data->set.rtspreq == RTSPREQ_RECEIVE && + (data->conn->proto.rtspc.rtp_channel == -1)) { + infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv); + } + } + + return httpStatus; +} + +static CURLcode rtsp_do(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + Curl_RtspReq rtspreq = data->set.rtspreq; + struct RTSP *rtsp = data->req.p.rtsp; + struct dynbuf req_buffer; + + const char *p_request = NULL; + const char *p_session_id = NULL; + const char *p_accept = NULL; + const char *p_accept_encoding = NULL; + const char *p_range = NULL; + const char *p_referrer = NULL; + const char *p_stream_uri = NULL; + const char *p_transport = NULL; + const char *p_uagent = NULL; + const char *p_proxyuserpwd = NULL; + const char *p_userpwd = NULL; + + *done = TRUE; + /* Initialize a dynamic send buffer */ + Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER); + + rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; + rtsp->CSeq_recv = 0; + + /* Setup the first_* fields to allow auth details get sent + to this origin */ + + if(!data->state.first_host) { + data->state.first_host = strdup(conn->host.name); + if(!data->state.first_host) + return CURLE_OUT_OF_MEMORY; + + data->state.first_remote_port = conn->remote_port; + data->state.first_remote_protocol = conn->handler->protocol; + } + + /* Setup the 'p_request' pointer to the proper p_request string + * Since all RTSP requests are included here, there is no need to + * support custom requests like HTTP. + **/ + data->req.no_body = TRUE; /* most requests do not contain a body */ + switch(rtspreq) { + default: + failf(data, "Got invalid RTSP request"); + return CURLE_BAD_FUNCTION_ARGUMENT; + case RTSPREQ_OPTIONS: + p_request = "OPTIONS"; + break; + case RTSPREQ_DESCRIBE: + p_request = "DESCRIBE"; + data->req.no_body = FALSE; + break; + case RTSPREQ_ANNOUNCE: + p_request = "ANNOUNCE"; + break; + case RTSPREQ_SETUP: + p_request = "SETUP"; + break; + case RTSPREQ_PLAY: + p_request = "PLAY"; + break; + case RTSPREQ_PAUSE: + p_request = "PAUSE"; + break; + case RTSPREQ_TEARDOWN: + p_request = "TEARDOWN"; + break; + case RTSPREQ_GET_PARAMETER: + /* GET_PARAMETER's no_body status is determined later */ + p_request = "GET_PARAMETER"; + data->req.no_body = FALSE; + break; + case RTSPREQ_SET_PARAMETER: + p_request = "SET_PARAMETER"; + break; + case RTSPREQ_RECORD: + p_request = "RECORD"; + break; + case RTSPREQ_RECEIVE: + p_request = ""; + /* Treat interleaved RTP as body */ + data->req.no_body = FALSE; + break; + case RTSPREQ_LAST: + failf(data, "Got invalid RTSP request: RTSPREQ_LAST"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + if(rtspreq == RTSPREQ_RECEIVE) { + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, TRUE); + goto out; + } + + p_session_id = data->set.str[STRING_RTSP_SESSION_ID]; + if(!p_session_id && + (rtspreq & ~(Curl_RtspReq)(RTSPREQ_OPTIONS | + RTSPREQ_DESCRIBE | + RTSPREQ_SETUP))) { + failf(data, "Refusing to issue an RTSP request [%s] without a session ID.", + p_request); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + /* Stream URI. Default to server '*' if not specified */ + if(data->set.str[STRING_RTSP_STREAM_URI]) { + p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI]; + } + else { + p_stream_uri = "*"; + } + + /* Transport Header for SETUP requests */ + p_transport = Curl_checkheaders(data, STRCONST("Transport")); + if(rtspreq == RTSPREQ_SETUP && !p_transport) { + /* New Transport: setting? */ + if(data->set.str[STRING_RTSP_TRANSPORT]) { + Curl_safefree(data->state.aptr.rtsp_transport); + + data->state.aptr.rtsp_transport = + aprintf("Transport: %s\r\n", + data->set.str[STRING_RTSP_TRANSPORT]); + if(!data->state.aptr.rtsp_transport) + return CURLE_OUT_OF_MEMORY; + } + else { + failf(data, + "Refusing to issue an RTSP SETUP without a Transport: header."); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + p_transport = data->state.aptr.rtsp_transport; + } + + /* Accept Headers for DESCRIBE requests */ + if(rtspreq == RTSPREQ_DESCRIBE) { + /* Accept Header */ + p_accept = Curl_checkheaders(data, STRCONST("Accept"))? + NULL:"Accept: application/sdp\r\n"; + + /* Accept-Encoding header */ + if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && + data->set.str[STRING_ENCODING]) { + Curl_safefree(data->state.aptr.accept_encoding); + data->state.aptr.accept_encoding = + aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); + + if(!data->state.aptr.accept_encoding) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + p_accept_encoding = data->state.aptr.accept_encoding; + } + } + + /* The User-Agent string might have been allocated in url.c already, because + it might have been used in the proxy connect, but if we have got a header + with the user-agent string specified, we erase the previously made string + here. */ + if(Curl_checkheaders(data, STRCONST("User-Agent")) && + data->state.aptr.uagent) { + Curl_safefree(data->state.aptr.uagent); + } + else if(!Curl_checkheaders(data, STRCONST("User-Agent")) && + data->set.str[STRING_USERAGENT]) { + p_uagent = data->state.aptr.uagent; + } + + /* setup the authentication headers */ + result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET, + p_stream_uri, FALSE); + if(result) + goto out; + +#ifndef CURL_DISABLE_PROXY + p_proxyuserpwd = data->state.aptr.proxyuserpwd; +#endif + p_userpwd = data->state.aptr.userpwd; + + /* Referrer */ + Curl_safefree(data->state.aptr.ref); + if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) + data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); + + p_referrer = data->state.aptr.ref; + + /* + * Range Header + * Only applies to PLAY, PAUSE, RECORD + * + * Go ahead and use the Range stuff supplied for HTTP + */ + if(data->state.use_range && + (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) { + + /* Check to see if there is a range set in the custom headers */ + if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) { + Curl_safefree(data->state.aptr.rangeline); + data->state.aptr.rangeline = aprintf("Range: %s\r\n", data->state.range); + p_range = data->state.aptr.rangeline; + } + } + + /* + * Sanity check the custom headers + */ + if(Curl_checkheaders(data, STRCONST("CSeq"))) { + failf(data, "CSeq cannot be set as a custom header."); + result = CURLE_RTSP_CSEQ_ERROR; + goto out; + } + if(Curl_checkheaders(data, STRCONST("Session"))) { + failf(data, "Session ID cannot be set as a custom header."); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + result = + Curl_dyn_addf(&req_buffer, + "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ + "CSeq: %ld\r\n", /* CSeq */ + p_request, p_stream_uri, rtsp->CSeq_sent); + if(result) + goto out; + + /* + * Rather than do a normal alloc line, keep the session_id unformatted + * to make comparison easier + */ + if(p_session_id) { + result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id); + if(result) + goto out; + } + + /* + * Shared HTTP-like options + */ + result = Curl_dyn_addf(&req_buffer, + "%s" /* transport */ + "%s" /* accept */ + "%s" /* accept-encoding */ + "%s" /* range */ + "%s" /* referrer */ + "%s" /* user-agent */ + "%s" /* proxyuserpwd */ + "%s" /* userpwd */ + , + p_transport ? p_transport : "", + p_accept ? p_accept : "", + p_accept_encoding ? p_accept_encoding : "", + p_range ? p_range : "", + p_referrer ? p_referrer : "", + p_uagent ? p_uagent : "", + p_proxyuserpwd ? p_proxyuserpwd : "", + p_userpwd ? p_userpwd : ""); + + /* + * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM + * with basic and digest, it will be freed anyway by the next request + */ + Curl_safefree(data->state.aptr.userpwd); + + if(result) + goto out; + + if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) { + result = Curl_add_timecondition(data, &req_buffer); + if(result) + goto out; + } + + result = Curl_add_custom_headers(data, FALSE, &req_buffer); + if(result) + goto out; + + if(rtspreq == RTSPREQ_ANNOUNCE || + rtspreq == RTSPREQ_SET_PARAMETER || + rtspreq == RTSPREQ_GET_PARAMETER) { + curl_off_t req_clen; /* request content length */ + + if(data->state.upload) { + req_clen = data->state.infilesize; + data->state.httpreq = HTTPREQ_PUT; + result = Curl_creader_set_fread(data, req_clen); + if(result) + goto out; + } + else { + if(data->set.postfields) { + size_t plen = strlen(data->set.postfields); + req_clen = (curl_off_t)plen; + result = Curl_creader_set_buf(data, data->set.postfields, plen); + } + else if(data->state.infilesize >= 0) { + req_clen = data->state.infilesize; + result = Curl_creader_set_fread(data, req_clen); + } + else { + req_clen = 0; + result = Curl_creader_set_null(data); + } + if(result) + goto out; + } + + if(req_clen > 0) { + /* As stated in the http comments, it is probably not wise to + * actually set a custom Content-Length in the headers */ + if(!Curl_checkheaders(data, STRCONST("Content-Length"))) { + result = + Curl_dyn_addf(&req_buffer, + "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", + req_clen); + if(result) + goto out; + } + + if(rtspreq == RTSPREQ_SET_PARAMETER || + rtspreq == RTSPREQ_GET_PARAMETER) { + if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { + result = Curl_dyn_addn(&req_buffer, + STRCONST("Content-Type: " + "text/parameters\r\n")); + if(result) + goto out; + } + } + + if(rtspreq == RTSPREQ_ANNOUNCE) { + if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { + result = Curl_dyn_addn(&req_buffer, + STRCONST("Content-Type: " + "application/sdp\r\n")); + if(result) + goto out; + } + } + } + else if(rtspreq == RTSPREQ_GET_PARAMETER) { + /* Check for an empty GET_PARAMETER (heartbeat) request */ + data->state.httpreq = HTTPREQ_HEAD; + data->req.no_body = TRUE; + } + } + else { + result = Curl_creader_set_null(data); + if(result) + goto out; + } + + /* Finish the request buffer */ + result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n")); + if(result) + goto out; + + Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE); + + /* issue the request */ + result = Curl_req_send(data, &req_buffer); + if(result) { + failf(data, "Failed sending RTSP request"); + goto out; + } + + /* Increment the CSeq on success */ + data->state.rtsp_next_client_CSeq++; + + if(data->req.writebytecount) { + /* if a request-body has been sent off, we make sure this progress is + noted properly */ + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + } +out: + Curl_dyn_free(&req_buffer); + return result; +} + +/** + * write any BODY bytes missing to the client, ignore the rest. + */ +static CURLcode rtp_write_body_junk(struct Curl_easy *data, + const char *buf, + size_t blen) +{ + struct rtsp_conn *rtspc = &(data->conn->proto.rtspc); + curl_off_t body_remain; + bool in_body; + + in_body = (data->req.headerline && !rtspc->in_header) && + (data->req.size >= 0) && + (data->req.bytecount < data->req.size); + body_remain = in_body? (data->req.size - data->req.bytecount) : 0; + DEBUGASSERT(body_remain >= 0); + if(body_remain) { + if((curl_off_t)blen > body_remain) + blen = (size_t)body_remain; + return Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen); + } + return CURLE_OK; +} + +static CURLcode rtsp_filter_rtp(struct Curl_easy *data, + const char *buf, + size_t blen, + size_t *pconsumed) +{ + struct rtsp_conn *rtspc = &(data->conn->proto.rtspc); + CURLcode result = CURLE_OK; + size_t skip_len = 0; + + *pconsumed = 0; + while(blen) { + bool in_body = (data->req.headerline && !rtspc->in_header) && + (data->req.size >= 0) && + (data->req.bytecount < data->req.size); + switch(rtspc->state) { + + case RTP_PARSE_SKIP: { + DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0); + while(blen && buf[0] != '$') { + if(!in_body && buf[0] == 'R' && + data->set.rtspreq != RTSPREQ_RECEIVE) { + if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) { + /* This could be the next response, no consume and return */ + if(*pconsumed) { + DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, " + "skipping %zd bytes of junk", *pconsumed)); + } + rtspc->state = RTP_PARSE_SKIP; + rtspc->in_header = TRUE; + goto out; + } + } + /* junk/BODY, consume without buffering */ + *pconsumed += 1; + ++buf; + --blen; + ++skip_len; + } + if(blen && buf[0] == '$') { + /* possible start of an RTP message, buffer */ + if(skip_len) { + /* end of junk/BODY bytes, flush */ + result = rtp_write_body_junk(data, + (char *)(buf - skip_len), skip_len); + skip_len = 0; + if(result) + goto out; + } + if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + *pconsumed += 1; + ++buf; + --blen; + rtspc->state = RTP_PARSE_CHANNEL; + } + break; + } + + case RTP_PARSE_CHANNEL: { + int idx = ((unsigned char)buf[0]) / 8; + int off = ((unsigned char)buf[0]) % 8; + DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 1); + if(!(data->state.rtp_channel_mask[idx] & (1 << off))) { + /* invalid channel number, junk or BODY data */ + rtspc->state = RTP_PARSE_SKIP; + DEBUGASSERT(skip_len == 0); + /* we do not consume this byte, it is BODY data */ + DEBUGF(infof(data, "RTSP: invalid RTP channel %d, skipping", idx)); + if(*pconsumed == 0) { + /* We did not consume the initial '$' in our buffer, but had + * it from an earlier call. We cannot un-consume it and have + * to write it directly as BODY data */ + result = rtp_write_body_junk(data, Curl_dyn_ptr(&rtspc->buf), 1); + if(result) + goto out; + } + else { + /* count the '$' as skip and continue */ + skip_len = 1; + } + Curl_dyn_free(&rtspc->buf); + break; + } + /* a valid channel, so we expect this to be a real RTP message */ + rtspc->rtp_channel = (unsigned char)buf[0]; + if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + *pconsumed += 1; + ++buf; + --blen; + rtspc->state = RTP_PARSE_LEN; + break; + } + + case RTP_PARSE_LEN: { + size_t rtp_len = Curl_dyn_len(&rtspc->buf); + const char *rtp_buf; + DEBUGASSERT(rtp_len >= 2 && rtp_len < 4); + if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + *pconsumed += 1; + ++buf; + --blen; + if(rtp_len == 2) + break; + rtp_buf = Curl_dyn_ptr(&rtspc->buf); + rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4; + rtspc->state = RTP_PARSE_DATA; + break; + } + + case RTP_PARSE_DATA: { + size_t rtp_len = Curl_dyn_len(&rtspc->buf); + size_t needed; + DEBUGASSERT(rtp_len < rtspc->rtp_len); + needed = rtspc->rtp_len - rtp_len; + if(needed <= blen) { + if(Curl_dyn_addn(&rtspc->buf, buf, needed)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + *pconsumed += needed; + buf += needed; + blen -= needed; + /* complete RTP message in buffer */ + DEBUGF(infof(data, "RTP write channel %d rtp_len %zu", + rtspc->rtp_channel, rtspc->rtp_len)); + result = rtp_client_write(data, Curl_dyn_ptr(&rtspc->buf), + rtspc->rtp_len); + Curl_dyn_free(&rtspc->buf); + rtspc->state = RTP_PARSE_SKIP; + if(result) + goto out; + } + else { + if(Curl_dyn_addn(&rtspc->buf, buf, blen)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + *pconsumed += blen; + buf += blen; + blen = 0; + } + break; + } + + default: + DEBUGASSERT(0); + return CURLE_RECV_ERROR; + } + } +out: + if(!result && skip_len) + result = rtp_write_body_junk(data, (char *)(buf - skip_len), skip_len); + return result; +} + +static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, + const char *buf, + size_t blen, + bool is_eos) +{ + struct rtsp_conn *rtspc = &(data->conn->proto.rtspc); + CURLcode result = CURLE_OK; + size_t consumed = 0; + + if(!data->req.header) + rtspc->in_header = FALSE; + if(!blen) { + goto out; + } + + DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, eos=%d)", + blen, rtspc->in_header, is_eos)); + + /* If header parsing is not ongoing, extract RTP messages */ + if(!rtspc->in_header) { + result = rtsp_filter_rtp(data, buf, blen, &consumed); + if(result) + goto out; + buf += consumed; + blen -= consumed; + /* either we consumed all or are at the start of header parsing */ + if(blen && !data->req.header) + DEBUGF(infof(data, "RTSP: %zu bytes, possibly excess in response body", + blen)); + } + + /* we want to parse headers, do so */ + if(data->req.header && blen) { + rtspc->in_header = TRUE; + result = Curl_http_write_resp_hds(data, buf, blen, &consumed); + if(result) + goto out; + + buf += consumed; + blen -= consumed; + + if(!data->req.header) + rtspc->in_header = FALSE; + + if(!rtspc->in_header) { + /* If header parsing is done, extract interleaved RTP messages */ + if(data->req.size <= -1) { + /* Respect section 4.4 of rfc2326: If the Content-Length header is + absent, a length 0 must be assumed. */ + data->req.size = 0; + data->req.download_done = TRUE; + } + result = rtsp_filter_rtp(data, buf, blen, &consumed); + if(result) + goto out; + blen -= consumed; + } + } + + if(rtspc->state != RTP_PARSE_SKIP) + data->req.done = FALSE; + /* we SHOULD have consumed all bytes, unless the response is borked. + * In which case we write out the left over bytes, letting the client + * writer deal with it (it will report EXCESS and fail the transfer). */ + DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d " + " rtspc->state=%d, req.size=%" CURL_FORMAT_CURL_OFF_T ")", + blen, rtspc->in_header, data->req.done, rtspc->state, + data->req.size)); + if(!result && (is_eos || blen)) { + result = Curl_client_write(data, CLIENTWRITE_BODY| + (is_eos? CLIENTWRITE_EOS:0), + (char *)buf, blen); + } + +out: + if((data->set.rtspreq == RTSPREQ_RECEIVE) && + (rtspc->state == RTP_PARSE_SKIP)) { + /* In special mode RECEIVE, we just process one chunk of network + * data, so we stop the transfer here, if we have no incomplete + * RTP message pending. */ + data->req.download_done = TRUE; + } + return result; +} + +static +CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len) +{ + size_t wrote; + curl_write_callback writeit; + void *user_ptr; + + if(len == 0) { + failf(data, "Cannot write a 0 size RTP packet."); + return CURLE_WRITE_ERROR; + } + + /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that + function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP + data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA + pointer to write out the RTP data. */ + if(data->set.fwrite_rtp) { + writeit = data->set.fwrite_rtp; + user_ptr = data->set.rtp_out; + } + else { + writeit = data->set.fwrite_func; + user_ptr = data->set.out; + } + + Curl_set_in_callback(data, true); + wrote = writeit((char *)ptr, 1, len, user_ptr); + Curl_set_in_callback(data, false); + + if(CURL_WRITEFUNC_PAUSE == wrote) { + failf(data, "Cannot pause RTP"); + return CURLE_WRITE_ERROR; + } + + if(wrote != len) { + failf(data, "Failed writing RTP data"); + return CURLE_WRITE_ERROR; + } + + return CURLE_OK; +} + +CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) +{ + if(checkprefix("CSeq:", header)) { + long CSeq = 0; + char *endp; + const char *p = &header[5]; + while(ISBLANK(*p)) + p++; + CSeq = strtol(p, &endp, 10); + if(p != endp) { + struct RTSP *rtsp = data->req.p.rtsp; + rtsp->CSeq_recv = CSeq; /* mark the request */ + data->state.rtsp_CSeq_recv = CSeq; /* update the handle */ + } + else { + failf(data, "Unable to read the CSeq header: [%s]", header); + return CURLE_RTSP_CSEQ_ERROR; + } + } + else if(checkprefix("Session:", header)) { + const char *start, *end; + size_t idlen; + + /* Find the first non-space letter */ + start = header + 8; + while(*start && ISBLANK(*start)) + start++; + + if(!*start) { + failf(data, "Got a blank Session ID"); + return CURLE_RTSP_SESSION_ERROR; + } + + /* Find the end of Session ID + * + * Allow any non whitespace content, up to the field separator or end of + * line. RFC 2326 is not 100% clear on the session ID and for example + * gstreamer does url-encoded session ID's not covered by the standard. + */ + end = start; + while(*end && *end != ';' && !ISSPACE(*end)) + end++; + idlen = end - start; + + if(data->set.str[STRING_RTSP_SESSION_ID]) { + + /* If the Session ID is set, then compare */ + if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen || + strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) { + failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]", + start, data->set.str[STRING_RTSP_SESSION_ID]); + return CURLE_RTSP_SESSION_ERROR; + } + } + else { + /* If the Session ID is not set, and we find it in a response, then set + * it. + */ + + /* Copy the id substring into a new buffer */ + data->set.str[STRING_RTSP_SESSION_ID] = Curl_memdup0(start, idlen); + if(!data->set.str[STRING_RTSP_SESSION_ID]) + return CURLE_OUT_OF_MEMORY; + } + } + else if(checkprefix("Transport:", header)) { + CURLcode result; + result = rtsp_parse_transport(data, header + 10); + if(result) + return result; + } + return CURLE_OK; +} + +static +CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport) +{ + /* If we receive multiple Transport response-headers, the linterleaved + channels of each response header is recorded and used together for + subsequent data validity checks.*/ + /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */ + const char *start, *end; + start = transport; + while(start && *start) { + while(*start && ISBLANK(*start) ) + start++; + end = strchr(start, ';'); + if(checkprefix("interleaved=", start)) { + long chan1, chan2, chan; + char *endp; + const char *p = start + 12; + chan1 = strtol(p, &endp, 10); + if(p != endp && chan1 >= 0 && chan1 <= 255) { + unsigned char *rtp_channel_mask = data->state.rtp_channel_mask; + chan2 = chan1; + if(*endp == '-') { + p = endp + 1; + chan2 = strtol(p, &endp, 10); + if(p == endp || chan2 < 0 || chan2 > 255) { + infof(data, "Unable to read the interleaved parameter from " + "Transport header: [%s]", transport); + chan2 = chan1; + } + } + for(chan = chan1; chan <= chan2; chan++) { + long idx = chan / 8; + long off = chan % 8; + rtp_channel_mask[idx] |= (unsigned char)(1 << off); + } + } + else { + infof(data, "Unable to read the interleaved parameter from " + "Transport header: [%s]", transport); + } + break; + } + /* skip to next parameter */ + start = (!end) ? end : (end + 1); + } + return CURLE_OK; +} + + +#endif /* CURL_DISABLE_RTSP or using Hyper */ diff --git a/third_party/curl/lib/rtsp.h b/third_party/curl/lib/rtsp.h new file mode 100644 index 0000000000..41b09503ff --- /dev/null +++ b/third_party/curl/lib/rtsp.h @@ -0,0 +1,70 @@ +#ifndef HEADER_CURL_RTSP_H +#define HEADER_CURL_RTSP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#ifdef USE_HYPER +#define CURL_DISABLE_RTSP 1 +#endif + +#ifndef CURL_DISABLE_RTSP + +extern const struct Curl_handler Curl_handler_rtsp; + +CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header); + +#else +/* disabled */ +#define Curl_rtsp_parseheader(x,y) CURLE_NOT_BUILT_IN + +#endif /* CURL_DISABLE_RTSP */ + +typedef enum { + RTP_PARSE_SKIP, + RTP_PARSE_CHANNEL, + RTP_PARSE_LEN, + RTP_PARSE_DATA +} rtp_parse_st; +/* + * RTSP Connection data + * + * Currently, only used for tracking incomplete RTP data reads + */ +struct rtsp_conn { + struct dynbuf buf; + int rtp_channel; + size_t rtp_len; + rtp_parse_st state; + BIT(in_header); +}; + +/**************************************************************************** + * RTSP unique setup + ***************************************************************************/ +struct RTSP { + long CSeq_sent; /* CSeq of this request */ + long CSeq_recv; /* CSeq received */ +}; + + +#endif /* HEADER_CURL_RTSP_H */ diff --git a/third_party/curl/lib/select.c b/third_party/curl/lib/select.c new file mode 100644 index 0000000000..96b0632ff2 --- /dev/null +++ b/third_party/curl/lib/select.c @@ -0,0 +1,551 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#ifdef HAVE_SYS_SELECT_H +#include +#elif defined(HAVE_UNISTD_H) +#include +#endif + +#if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE) +#error "We cannot compile without select() or poll() support." +#endif + +#ifdef MSDOS +#include /* delay() */ +#endif + +#include + +#include "urldata.h" +#include "connect.h" +#include "select.h" +#include "timediff.h" +#include "warnless.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Internal function used for waiting a specific amount of ms + * in Curl_socket_check() and Curl_poll() when no file descriptor + * is provided to wait on, just being used to delay execution. + * WinSock select() and poll() timeout mechanisms need a valid + * socket descriptor in a not null file descriptor set to work. + * Waiting indefinitely with this function is not allowed, a + * zero or negative timeout value will return immediately. + * Timeout resolution, accuracy, as well as maximum supported + * value is system dependent, neither factor is a critical issue + * for the intended use of this function in the library. + * + * Return values: + * -1 = system call error, or invalid timeout value + * 0 = specified timeout has elapsed, or interrupted + */ +int Curl_wait_ms(timediff_t timeout_ms) +{ + int r = 0; + + if(!timeout_ms) + return 0; + if(timeout_ms < 0) { + SET_SOCKERRNO(EINVAL); + return -1; + } +#if defined(MSDOS) + delay(timeout_ms); +#elif defined(_WIN32) + /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */ +#if TIMEDIFF_T_MAX >= ULONG_MAX + if(timeout_ms >= ULONG_MAX) + timeout_ms = ULONG_MAX-1; + /* do not use ULONG_MAX, because that is equal to INFINITE */ +#endif + Sleep((ULONG)timeout_ms); +#else +#if defined(HAVE_POLL_FINE) + /* prevent overflow, timeout_ms is typecast to int. */ +#if TIMEDIFF_T_MAX > INT_MAX + if(timeout_ms > INT_MAX) + timeout_ms = INT_MAX; +#endif + r = poll(NULL, 0, (int)timeout_ms); +#else + { + struct timeval pending_tv; + r = select(0, NULL, NULL, NULL, curlx_mstotv(&pending_tv, timeout_ms)); + } +#endif /* HAVE_POLL_FINE */ +#endif /* USE_WINSOCK */ + if(r) { + if((r == -1) && (SOCKERRNO == EINTR)) + /* make EINTR from select or poll not a "lethal" error */ + r = 0; + else + r = -1; + } + return r; +} + +#ifndef HAVE_POLL_FINE +/* + * This is a wrapper around select() to aid in Windows compatibility. + * A negative timeout value makes this function wait indefinitely, + * unless no valid file descriptor is given, when this happens the + * negative timeout is ignored and the function times out immediately. + * + * Return values: + * -1 = system call error or fd >= FD_SETSIZE + * 0 = timeout + * N = number of signalled file descriptors + */ +static int our_select(curl_socket_t maxfd, /* highest socket number */ + fd_set *fds_read, /* sockets ready for reading */ + fd_set *fds_write, /* sockets ready for writing */ + fd_set *fds_err, /* sockets with errors */ + timediff_t timeout_ms) /* milliseconds to wait */ +{ + struct timeval pending_tv; + struct timeval *ptimeout; + +#ifdef USE_WINSOCK + /* WinSock select() cannot handle zero events. See the comment below. */ + if((!fds_read || fds_read->fd_count == 0) && + (!fds_write || fds_write->fd_count == 0) && + (!fds_err || fds_err->fd_count == 0)) { + /* no sockets, just wait */ + return Curl_wait_ms(timeout_ms); + } +#endif + + ptimeout = curlx_mstotv(&pending_tv, timeout_ms); + +#ifdef USE_WINSOCK + /* WinSock select() must not be called with an fd_set that contains zero + fd flags, or it will return WSAEINVAL. But, it also cannot be called + with no fd_sets at all! From the documentation: + + Any two of the parameters, readfds, writefds, or exceptfds, can be + given as null. At least one must be non-null, and any non-null + descriptor set must contain at least one handle to a socket. + + It is unclear why WinSock does not just handle this for us instead of + calling this an error. Luckily, with WinSock, we can _also_ ask how + many bits are set on an fd_set. So, let's just check it beforehand. + */ + return select((int)maxfd + 1, + fds_read && fds_read->fd_count ? fds_read : NULL, + fds_write && fds_write->fd_count ? fds_write : NULL, + fds_err && fds_err->fd_count ? fds_err : NULL, ptimeout); +#else + return select((int)maxfd + 1, fds_read, fds_write, fds_err, ptimeout); +#endif +} + +#endif + +/* + * Wait for read or write events on a set of file descriptors. It uses poll() + * when a fine poll() is available, in order to avoid limits with FD_SETSIZE, + * otherwise select() is used. An error is returned if select() is being used + * and a file descriptor is too large for FD_SETSIZE. + * + * A negative timeout value makes this function wait indefinitely, + * unless no valid file descriptor is given, when this happens the + * negative timeout is ignored and the function times out immediately. + * + * Return values: + * -1 = system call error or fd >= FD_SETSIZE + * 0 = timeout + * [bitmask] = action as described below + * + * CURL_CSELECT_IN - first socket is readable + * CURL_CSELECT_IN2 - second socket is readable + * CURL_CSELECT_OUT - write socket is writable + * CURL_CSELECT_ERR - an error condition occurred + */ +int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ + curl_socket_t readfd1, + curl_socket_t writefd, /* socket to write to */ + timediff_t timeout_ms) /* milliseconds to wait */ +{ + struct pollfd pfd[3]; + int num; + int r; + + if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) && + (writefd == CURL_SOCKET_BAD)) { + /* no sockets, just wait */ + return Curl_wait_ms(timeout_ms); + } + + /* Avoid initial timestamp, avoid Curl_now() call, when elapsed + time in this function does not need to be measured. This happens + when function is called with a zero timeout or a negative timeout + value indicating a blocking call should be performed. */ + + num = 0; + if(readfd0 != CURL_SOCKET_BAD) { + pfd[num].fd = readfd0; + pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; + pfd[num].revents = 0; + num++; + } + if(readfd1 != CURL_SOCKET_BAD) { + pfd[num].fd = readfd1; + pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; + pfd[num].revents = 0; + num++; + } + if(writefd != CURL_SOCKET_BAD) { + pfd[num].fd = writefd; + pfd[num].events = POLLWRNORM|POLLOUT|POLLPRI; + pfd[num].revents = 0; + num++; + } + + r = Curl_poll(pfd, (unsigned int)num, timeout_ms); + if(r <= 0) + return r; + + r = 0; + num = 0; + if(readfd0 != CURL_SOCKET_BAD) { + if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP)) + r |= CURL_CSELECT_IN; + if(pfd[num].revents & (POLLPRI|POLLNVAL)) + r |= CURL_CSELECT_ERR; + num++; + } + if(readfd1 != CURL_SOCKET_BAD) { + if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP)) + r |= CURL_CSELECT_IN2; + if(pfd[num].revents & (POLLPRI|POLLNVAL)) + r |= CURL_CSELECT_ERR; + num++; + } + if(writefd != CURL_SOCKET_BAD) { + if(pfd[num].revents & (POLLWRNORM|POLLOUT)) + r |= CURL_CSELECT_OUT; + if(pfd[num].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) + r |= CURL_CSELECT_ERR; + } + + return r; +} + +/* + * This is a wrapper around poll(). If poll() does not exist, then + * select() is used instead. An error is returned if select() is + * being used and a file descriptor is too large for FD_SETSIZE. + * A negative timeout value makes this function wait indefinitely, + * unless no valid file descriptor is given, when this happens the + * negative timeout is ignored and the function times out immediately. + * + * Return values: + * -1 = system call error or fd >= FD_SETSIZE + * 0 = timeout + * N = number of structures with non zero revent fields + */ +int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) +{ +#ifdef HAVE_POLL_FINE + int pending_ms; +#else + fd_set fds_read; + fd_set fds_write; + fd_set fds_err; + curl_socket_t maxfd; +#endif + bool fds_none = TRUE; + unsigned int i; + int r; + + if(ufds) { + for(i = 0; i < nfds; i++) { + if(ufds[i].fd != CURL_SOCKET_BAD) { + fds_none = FALSE; + break; + } + } + } + if(fds_none) { + /* no sockets, just wait */ + return Curl_wait_ms(timeout_ms); + } + + /* Avoid initial timestamp, avoid Curl_now() call, when elapsed + time in this function does not need to be measured. This happens + when function is called with a zero timeout or a negative timeout + value indicating a blocking call should be performed. */ + +#ifdef HAVE_POLL_FINE + + /* prevent overflow, timeout_ms is typecast to int. */ +#if TIMEDIFF_T_MAX > INT_MAX + if(timeout_ms > INT_MAX) + timeout_ms = INT_MAX; +#endif + if(timeout_ms > 0) + pending_ms = (int)timeout_ms; + else if(timeout_ms < 0) + pending_ms = -1; + else + pending_ms = 0; + r = poll(ufds, nfds, pending_ms); + if(r <= 0) { + if((r == -1) && (SOCKERRNO == EINTR)) + /* make EINTR from select or poll not a "lethal" error */ + r = 0; + return r; + } + + for(i = 0; i < nfds; i++) { + if(ufds[i].fd == CURL_SOCKET_BAD) + continue; + if(ufds[i].revents & POLLHUP) + ufds[i].revents |= POLLIN; + if(ufds[i].revents & POLLERR) + ufds[i].revents |= POLLIN|POLLOUT; + } + +#else /* HAVE_POLL_FINE */ + + FD_ZERO(&fds_read); + FD_ZERO(&fds_write); + FD_ZERO(&fds_err); + maxfd = (curl_socket_t)-1; + + for(i = 0; i < nfds; i++) { + ufds[i].revents = 0; + if(ufds[i].fd == CURL_SOCKET_BAD) + continue; + VERIFY_SOCK(ufds[i].fd); + if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI| + POLLRDNORM|POLLWRNORM|POLLRDBAND)) { + if(ufds[i].fd > maxfd) + maxfd = ufds[i].fd; + if(ufds[i].events & (POLLRDNORM|POLLIN)) + FD_SET(ufds[i].fd, &fds_read); + if(ufds[i].events & (POLLWRNORM|POLLOUT)) + FD_SET(ufds[i].fd, &fds_write); + if(ufds[i].events & (POLLRDBAND|POLLPRI)) + FD_SET(ufds[i].fd, &fds_err); + } + } + + /* + Note also that WinSock ignores the first argument, so we do not worry + about the fact that maxfd is computed incorrectly with WinSock (since + curl_socket_t is unsigned in such cases and thus -1 is the largest + value). + */ + r = our_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms); + if(r <= 0) { + if((r == -1) && (SOCKERRNO == EINTR)) + /* make EINTR from select or poll not a "lethal" error */ + r = 0; + return r; + } + + r = 0; + for(i = 0; i < nfds; i++) { + ufds[i].revents = 0; + if(ufds[i].fd == CURL_SOCKET_BAD) + continue; + if(FD_ISSET(ufds[i].fd, &fds_read)) { + if(ufds[i].events & POLLRDNORM) + ufds[i].revents |= POLLRDNORM; + if(ufds[i].events & POLLIN) + ufds[i].revents |= POLLIN; + } + if(FD_ISSET(ufds[i].fd, &fds_write)) { + if(ufds[i].events & POLLWRNORM) + ufds[i].revents |= POLLWRNORM; + if(ufds[i].events & POLLOUT) + ufds[i].revents |= POLLOUT; + } + if(FD_ISSET(ufds[i].fd, &fds_err)) { + if(ufds[i].events & POLLRDBAND) + ufds[i].revents |= POLLRDBAND; + if(ufds[i].events & POLLPRI) + ufds[i].revents |= POLLPRI; + } + if(ufds[i].revents) + r++; + } + +#endif /* HAVE_POLL_FINE */ + + return r; +} + +void Curl_pollfds_init(struct curl_pollfds *cpfds, + struct pollfd *static_pfds, + unsigned int static_count) +{ + DEBUGASSERT(cpfds); + memset(cpfds, 0, sizeof(*cpfds)); + if(static_pfds && static_count) { + cpfds->pfds = static_pfds; + cpfds->count = static_count; + } +} + +void Curl_pollfds_cleanup(struct curl_pollfds *cpfds) +{ + DEBUGASSERT(cpfds); + if(cpfds->allocated_pfds) { + free(cpfds->pfds); + } + memset(cpfds, 0, sizeof(*cpfds)); +} + +static CURLcode cpfds_increase(struct curl_pollfds *cpfds, unsigned int inc) +{ + struct pollfd *new_fds; + unsigned int new_count = cpfds->count + inc; + + new_fds = calloc(new_count, sizeof(struct pollfd)); + if(!new_fds) + return CURLE_OUT_OF_MEMORY; + + memcpy(new_fds, cpfds->pfds, cpfds->count * sizeof(struct pollfd)); + if(cpfds->allocated_pfds) + free(cpfds->pfds); + cpfds->pfds = new_fds; + cpfds->count = new_count; + cpfds->allocated_pfds = TRUE; + return CURLE_OK; +} + +static CURLcode cpfds_add_sock(struct curl_pollfds *cpfds, + curl_socket_t sock, short events, bool fold) +{ + int i; + + if(fold && cpfds->n <= INT_MAX) { + for(i = (int)cpfds->n - 1; i >= 0; --i) { + if(sock == cpfds->pfds[i].fd) { + cpfds->pfds[i].events |= events; + return CURLE_OK; + } + } + } + /* not folded, add new entry */ + if(cpfds->n >= cpfds->count) { + if(cpfds_increase(cpfds, 100)) + return CURLE_OUT_OF_MEMORY; + } + cpfds->pfds[cpfds->n].fd = sock; + cpfds->pfds[cpfds->n].events = events; + ++cpfds->n; + return CURLE_OK; +} + +CURLcode Curl_pollfds_add_sock(struct curl_pollfds *cpfds, + curl_socket_t sock, short events) +{ + return cpfds_add_sock(cpfds, sock, events, FALSE); +} + +CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds, + struct easy_pollset *ps) +{ + size_t i; + + DEBUGASSERT(cpfds); + DEBUGASSERT(ps); + for(i = 0; i < ps->num; i++) { + short events = 0; + if(ps->actions[i] & CURL_POLL_IN) + events |= POLLIN; + if(ps->actions[i] & CURL_POLL_OUT) + events |= POLLOUT; + if(events) { + if(cpfds_add_sock(cpfds, ps->sockets[i], events, TRUE)) + return CURLE_OUT_OF_MEMORY; + } + } + return CURLE_OK; +} + +void Curl_waitfds_init(struct curl_waitfds *cwfds, + struct curl_waitfd *static_wfds, + unsigned int static_count) +{ + DEBUGASSERT(cwfds); + DEBUGASSERT(static_wfds); + memset(cwfds, 0, sizeof(*cwfds)); + cwfds->wfds = static_wfds; + cwfds->count = static_count; +} + +static CURLcode cwfds_add_sock(struct curl_waitfds *cwfds, + curl_socket_t sock, short events) +{ + int i; + + if(cwfds->n <= INT_MAX) { + for(i = (int)cwfds->n - 1; i >= 0; --i) { + if(sock == cwfds->wfds[i].fd) { + cwfds->wfds[i].events |= events; + return CURLE_OK; + } + } + } + /* not folded, add new entry */ + if(cwfds->n >= cwfds->count) + return CURLE_OUT_OF_MEMORY; + cwfds->wfds[cwfds->n].fd = sock; + cwfds->wfds[cwfds->n].events = events; + ++cwfds->n; + return CURLE_OK; +} + +CURLcode Curl_waitfds_add_ps(struct curl_waitfds *cwfds, + struct easy_pollset *ps) +{ + size_t i; + + DEBUGASSERT(cwfds); + DEBUGASSERT(ps); + for(i = 0; i < ps->num; i++) { + short events = 0; + if(ps->actions[i] & CURL_POLL_IN) + events |= CURL_WAIT_POLLIN; + if(ps->actions[i] & CURL_POLL_OUT) + events |= CURL_WAIT_POLLOUT; + if(events) { + if(cwfds_add_sock(cwfds, ps->sockets[i], events)) + return CURLE_OUT_OF_MEMORY; + } + } + return CURLE_OK; +} diff --git a/third_party/curl/lib/select.h b/third_party/curl/lib/select.h new file mode 100644 index 0000000000..f01acbdefc --- /dev/null +++ b/third_party/curl/lib/select.h @@ -0,0 +1,147 @@ +#ifndef HEADER_CURL_SELECT_H +#define HEADER_CURL_SELECT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_POLL_H +#include +#elif defined(HAVE_SYS_POLL_H) +#include +#endif + +/* + * Definition of pollfd struct and constants for platforms lacking them. + */ + +#if !defined(HAVE_SYS_POLL_H) && \ + !defined(HAVE_POLL_H) && \ + !defined(POLLIN) + +#define POLLIN 0x01 +#define POLLPRI 0x02 +#define POLLOUT 0x04 +#define POLLERR 0x08 +#define POLLHUP 0x10 +#define POLLNVAL 0x20 + +struct pollfd +{ + curl_socket_t fd; + short events; + short revents; +}; + +#endif + +#ifndef POLLRDNORM +#define POLLRDNORM POLLIN +#endif + +#ifndef POLLWRNORM +#define POLLWRNORM POLLOUT +#endif + +#ifndef POLLRDBAND +#define POLLRDBAND POLLPRI +#endif + +/* there are three CSELECT defines that are defined in the public header that + are exposed to users, but this *IN2 bit is only ever used internally and + therefore defined here */ +#define CURL_CSELECT_IN2 (CURL_CSELECT_ERR << 1) + +int Curl_socket_check(curl_socket_t readfd, curl_socket_t readfd2, + curl_socket_t writefd, + timediff_t timeout_ms); +#define SOCKET_READABLE(x,z) \ + Curl_socket_check(x, CURL_SOCKET_BAD, CURL_SOCKET_BAD, z) +#define SOCKET_WRITABLE(x,z) \ + Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, x, z) + +int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms); +int Curl_wait_ms(timediff_t timeout_ms); + +/* + With Winsock the valid range is [0..INVALID_SOCKET-1] according to + https://docs.microsoft.com/en-us/windows/win32/winsock/socket-data-type-2 +*/ +#ifdef USE_WINSOCK +#define VALID_SOCK(s) ((s) < INVALID_SOCKET) +#define FDSET_SOCK(x) 1 +#define VERIFY_SOCK(x) do { \ + if(!VALID_SOCK(x)) { \ + SET_SOCKERRNO(WSAEINVAL); \ + return -1; \ + } \ +} while(0) +#else +#define VALID_SOCK(s) ((s) >= 0) + +/* If the socket is small enough to get set or read from an fdset */ +#define FDSET_SOCK(s) ((s) < FD_SETSIZE) + +#define VERIFY_SOCK(x) do { \ + if(!VALID_SOCK(x) || !FDSET_SOCK(x)) { \ + SET_SOCKERRNO(EINVAL); \ + return -1; \ + } \ + } while(0) +#endif + +struct curl_pollfds { + struct pollfd *pfds; + unsigned int n; + unsigned int count; + BIT(allocated_pfds); +}; + +void Curl_pollfds_init(struct curl_pollfds *cpfds, + struct pollfd *static_pfds, + unsigned int static_count); + +void Curl_pollfds_cleanup(struct curl_pollfds *cpfds); + +CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds, + struct easy_pollset *ps); + +CURLcode Curl_pollfds_add_sock(struct curl_pollfds *cpfds, + curl_socket_t sock, short events); + +struct curl_waitfds { + struct curl_waitfd *wfds; + unsigned int n; + unsigned int count; +}; + +void Curl_waitfds_init(struct curl_waitfds *cwfds, + struct curl_waitfd *static_wfds, + unsigned int static_count); + +CURLcode Curl_waitfds_add_ps(struct curl_waitfds *cwfds, + struct easy_pollset *ps); + + +#endif /* HEADER_CURL_SELECT_H */ diff --git a/third_party/curl/lib/sendf.c b/third_party/curl/lib/sendf.c new file mode 100644 index 0000000000..24b90d2ad0 --- /dev/null +++ b/third_party/curl/lib/sendf.c @@ -0,0 +1,1438 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_LINUX_TCP_H +#include +#elif defined(HAVE_NETINET_TCP_H) +#include +#endif + +#include + +#include "urldata.h" +#include "sendf.h" +#include "cfilters.h" +#include "connect.h" +#include "content_encoding.h" +#include "cw-out.h" +#include "vtls/vtls.h" +#include "vssh/ssh.h" +#include "easyif.h" +#include "multiif.h" +#include "strerror.h" +#include "select.h" +#include "strdup.h" +#include "http2.h" +#include "progress.h" +#include "warnless.h" +#include "ws.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +static CURLcode do_init_writer_stack(struct Curl_easy *data); + +/* Curl_client_write() sends data to the write callback(s) + + The bit pattern defines to what "streams" to write to. Body and/or header. + The defines are in sendf.h of course. + */ +CURLcode Curl_client_write(struct Curl_easy *data, + int type, const char *buf, size_t blen) +{ + CURLcode result; + + /* it is one of those, at least */ + DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO)); + /* BODY is only BODY (with optional EOS) */ + DEBUGASSERT(!(type & CLIENTWRITE_BODY) || + ((type & ~(CLIENTWRITE_BODY|CLIENTWRITE_EOS)) == 0)); + /* INFO is only INFO (with optional EOS) */ + DEBUGASSERT(!(type & CLIENTWRITE_INFO) || + ((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0)); + + if(!data->req.writer_stack) { + result = do_init_writer_stack(data); + if(result) + return result; + DEBUGASSERT(data->req.writer_stack); + } + + result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen); + CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d", + type, blen, result); + return result; +} + +static void cl_reset_writer(struct Curl_easy *data) +{ + struct Curl_cwriter *writer = data->req.writer_stack; + while(writer) { + data->req.writer_stack = writer->next; + writer->cwt->do_close(data, writer); + free(writer); + writer = data->req.writer_stack; + } +} + +static void cl_reset_reader(struct Curl_easy *data) +{ + struct Curl_creader *reader = data->req.reader_stack; + while(reader) { + data->req.reader_stack = reader->next; + reader->crt->do_close(data, reader); + free(reader); + reader = data->req.reader_stack; + } +} + +void Curl_client_cleanup(struct Curl_easy *data) +{ + cl_reset_reader(data); + cl_reset_writer(data); + + data->req.bytecount = 0; + data->req.headerline = 0; +} + +void Curl_client_reset(struct Curl_easy *data) +{ + if(data->req.rewind_read) { + /* already requested */ + CURL_TRC_READ(data, "client_reset, will rewind reader"); + } + else { + CURL_TRC_READ(data, "client_reset, clear readers"); + cl_reset_reader(data); + } + cl_reset_writer(data); + + data->req.bytecount = 0; + data->req.headerline = 0; +} + +CURLcode Curl_client_start(struct Curl_easy *data) +{ + if(data->req.rewind_read) { + struct Curl_creader *r = data->req.reader_stack; + CURLcode result = CURLE_OK; + + CURL_TRC_READ(data, "client start, rewind readers"); + while(r) { + result = r->crt->rewind(data, r); + if(result) { + failf(data, "rewind of client reader '%s' failed: %d", + r->crt->name, result); + return result; + } + r = r->next; + } + data->req.rewind_read = FALSE; + cl_reset_reader(data); + } + return CURLE_OK; +} + +bool Curl_creader_will_rewind(struct Curl_easy *data) +{ + return data->req.rewind_read; +} + +void Curl_creader_set_rewind(struct Curl_easy *data, bool enable) +{ + data->req.rewind_read = !!enable; +} + +/* Write data using an unencoding writer stack. */ +CURLcode Curl_cwriter_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + if(!writer) + return CURLE_WRITE_ERROR; + return writer->cwt->do_write(data, writer, type, buf, nbytes); +} + +CURLcode Curl_cwriter_def_init(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + (void)data; + (void)writer; + return CURLE_OK; +} + +CURLcode Curl_cwriter_def_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); +} + +void Curl_cwriter_def_close(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + (void) data; + (void) writer; +} + +static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit) +{ + if(limit != -1) { + /* How much more are we allowed to write? */ + curl_off_t remain_diff; + remain_diff = limit - data->req.bytecount; + if(remain_diff < 0) { + /* already written too much! */ + return 0; + } +#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T + else if(remain_diff > SSIZE_T_MAX) { + return SIZE_T_MAX; + } +#endif + else { + return (size_t)remain_diff; + } + } + return SIZE_T_MAX; +} + +struct cw_download_ctx { + struct Curl_cwriter super; + BIT(started_response); +}; +/* Download client writer in phase CURL_CW_PROTOCOL that + * sees the "real" download body data. */ +static CURLcode cw_download_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + struct cw_download_ctx *ctx = writer->ctx; + CURLcode result; + size_t nwrite, excess_len = 0; + bool is_connect = !!(type & CLIENTWRITE_CONNECT); + + if(!is_connect && !ctx->started_response) { + Curl_pgrsTime(data, TIMER_STARTTRANSFER); + ctx->started_response = TRUE; + } + + if(!(type & CLIENTWRITE_BODY)) { + if(is_connect && data->set.suppress_connect_headers) + return CURLE_OK; + result = Curl_cwriter_write(data, writer->next, type, buf, nbytes); + CURL_TRC_WRITE(data, "download_write header(type=%x, blen=%zu) -> %d", + type, nbytes, result); + return result; + } + + /* Here, we deal with REAL BODY bytes. All filtering and transfer + * encodings have been applied and only the true content, e.g. BODY, + * bytes are passed here. + * This allows us to check sizes, update stats, etc. independent + * from the protocol in play. */ + + if(data->req.no_body && nbytes > 0) { + /* BODY arrives although we want none, bail out */ + streamclose(data->conn, "ignoring body"); + CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu), " + "did not want a BODY", type, nbytes); + data->req.download_done = TRUE; + if(data->info.header_size) + /* if headers have been received, this is fine */ + return CURLE_OK; + return CURLE_WEIRD_SERVER_REPLY; + } + + /* Determine if we see any bytes in excess to what is allowed. + * We write the allowed bytes and handle excess further below. + * This gives deterministic BODY writes on varying buffer receive + * lengths. */ + nwrite = nbytes; + if(-1 != data->req.maxdownload) { + size_t wmax = get_max_body_write_len(data, data->req.maxdownload); + if(nwrite > wmax) { + excess_len = nbytes - wmax; + nwrite = wmax; + } + + if(nwrite == wmax) { + data->req.download_done = TRUE; + } + + if((type & CLIENTWRITE_EOS) && !data->req.no_body && + (data->req.maxdownload > data->req.bytecount)) { + failf(data, "end of response with %" CURL_FORMAT_CURL_OFF_T + " bytes missing", data->req.maxdownload - data->req.bytecount); + return CURLE_PARTIAL_FILE; + } + } + + /* Error on too large filesize is handled below, after writing + * the permitted bytes */ + if(data->set.max_filesize) { + size_t wmax = get_max_body_write_len(data, data->set.max_filesize); + if(nwrite > wmax) { + nwrite = wmax; + } + } + + if(!data->req.ignorebody && (nwrite || (type & CLIENTWRITE_EOS))) { + result = Curl_cwriter_write(data, writer->next, type, buf, nwrite); + CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu) -> %d", + type, nbytes, result); + if(result) + return result; + } + /* Update stats, write and report progress */ + data->req.bytecount += nwrite; +#ifdef USE_HYPER + data->req.bodywritten = TRUE; +#endif + result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); + if(result) + return result; + + if(excess_len) { + if(!data->req.ignorebody) { + infof(data, + "Excess found writing body:" + " excess = %zu" + ", size = %" CURL_FORMAT_CURL_OFF_T + ", maxdownload = %" CURL_FORMAT_CURL_OFF_T + ", bytecount = %" CURL_FORMAT_CURL_OFF_T, + excess_len, data->req.size, data->req.maxdownload, + data->req.bytecount); + connclose(data->conn, "excess found in a read"); + } + } + else if(nwrite < nbytes) { + failf(data, "Exceeded the maximum allowed file size " + "(%" CURL_FORMAT_CURL_OFF_T ") with %" + CURL_FORMAT_CURL_OFF_T " bytes", + data->set.max_filesize, data->req.bytecount); + return CURLE_FILESIZE_EXCEEDED; + } + + return CURLE_OK; +} + +static const struct Curl_cwtype cw_download = { + "protocol", + NULL, + Curl_cwriter_def_init, + cw_download_write, + Curl_cwriter_def_close, + sizeof(struct cw_download_ctx) +}; + +/* RAW client writer in phase CURL_CW_RAW that + * enabled tracing of raw data. */ +static CURLcode cw_raw_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) { + Curl_debug(data, CURLINFO_DATA_IN, (char *)buf, nbytes); + } + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); +} + +static const struct Curl_cwtype cw_raw = { + "raw", + NULL, + Curl_cwriter_def_init, + cw_raw_write, + Curl_cwriter_def_close, + sizeof(struct Curl_cwriter) +}; + +/* Create an unencoding writer stage using the given handler. */ +CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, + struct Curl_easy *data, + const struct Curl_cwtype *cwt, + Curl_cwriter_phase phase) +{ + struct Curl_cwriter *writer = NULL; + CURLcode result = CURLE_OUT_OF_MEMORY; + void *p; + + DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter)); + p = calloc(1, cwt->cwriter_size); + if(!p) + goto out; + + writer = (struct Curl_cwriter *)p; + writer->cwt = cwt; + writer->ctx = p; + writer->phase = phase; + result = cwt->do_init(data, writer); + +out: + *pwriter = result? NULL : writer; + if(result) + free(writer); + return result; +} + +void Curl_cwriter_free(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + if(writer) { + writer->cwt->do_close(data, writer); + free(writer); + } +} + +size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase) +{ + struct Curl_cwriter *w; + size_t n = 0; + + for(w = data->req.writer_stack; w; w = w->next) { + if(w->phase == phase) + ++n; + } + return n; +} + +static CURLcode do_init_writer_stack(struct Curl_easy *data) +{ + struct Curl_cwriter *writer; + CURLcode result; + + DEBUGASSERT(!data->req.writer_stack); + result = Curl_cwriter_create(&data->req.writer_stack, + data, &Curl_cwt_out, CURL_CW_CLIENT); + if(result) + return result; + + result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL); + if(result) + return result; + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + } + + result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW); + if(result) + return result; + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + } + return result; +} + +CURLcode Curl_cwriter_add(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + CURLcode result; + struct Curl_cwriter **anchor = &data->req.writer_stack; + + if(!*anchor) { + result = do_init_writer_stack(data); + if(result) + return result; + } + + /* Insert the writer as first in its phase. + * Skip existing writers of lower phases. */ + while(*anchor && (*anchor)->phase < writer->phase) + anchor = &((*anchor)->next); + writer->next = *anchor; + *anchor = writer; + return CURLE_OK; +} + +struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data, + const char *name) +{ + struct Curl_cwriter *writer; + for(writer = data->req.writer_stack; writer; writer = writer->next) { + if(!strcmp(name, writer->cwt->name)) + return writer; + } + return NULL; +} + +struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data, + const struct Curl_cwtype *cwt) +{ + struct Curl_cwriter *writer; + for(writer = data->req.writer_stack; writer; writer = writer->next) { + if(writer->cwt == cwt) + return writer; + } + return NULL; +} + +void Curl_cwriter_remove_by_name(struct Curl_easy *data, + const char *name) +{ + struct Curl_cwriter **anchor = &data->req.writer_stack; + + while(*anchor) { + if(!strcmp(name, (*anchor)->cwt->name)) { + struct Curl_cwriter *w = (*anchor); + *anchor = w->next; + Curl_cwriter_free(data, w); + continue; + } + anchor = &((*anchor)->next); + } +} + +bool Curl_cwriter_is_paused(struct Curl_easy *data) +{ + return Curl_cw_out_is_paused(data); +} + +CURLcode Curl_cwriter_unpause(struct Curl_easy *data) +{ + return Curl_cw_out_unpause(data); +} + +CURLcode Curl_creader_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, size_t *nread, bool *eos) +{ + *nread = 0; + *eos = FALSE; + if(!reader) + return CURLE_READ_ERROR; + return reader->crt->do_read(data, reader, buf, blen, nread, eos); +} + +CURLcode Curl_creader_def_init(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return CURLE_OK; +} + +void Curl_creader_def_close(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; +} + +CURLcode Curl_creader_def_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *nread, bool *eos) +{ + if(reader->next) + return reader->next->crt->do_read(data, reader->next, buf, blen, + nread, eos); + else { + *nread = 0; + *eos = FALSE; + return CURLE_READ_ERROR; + } +} + +bool Curl_creader_def_needs_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return FALSE; +} + +curl_off_t Curl_creader_def_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + return reader->next? + reader->next->crt->total_length(data, reader->next) : -1; +} + +CURLcode Curl_creader_def_resume_from(struct Curl_easy *data, + struct Curl_creader *reader, + curl_off_t offset) +{ + (void)data; + (void)reader; + (void)offset; + return CURLE_READ_ERROR; +} + +CURLcode Curl_creader_def_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return CURLE_OK; +} + +CURLcode Curl_creader_def_unpause(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return CURLE_OK; +} + +bool Curl_creader_def_is_paused(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return FALSE; +} + +void Curl_creader_def_done(struct Curl_easy *data, + struct Curl_creader *reader, int premature) +{ + (void)data; + (void)reader; + (void)premature; +} + +struct cr_in_ctx { + struct Curl_creader super; + curl_read_callback read_cb; + void *cb_user_data; + curl_off_t total_len; + curl_off_t read_len; + CURLcode error_result; + BIT(seen_eos); + BIT(errored); + BIT(has_used_cb); + BIT(is_paused); +}; + +static CURLcode cr_in_init(struct Curl_easy *data, struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + ctx->read_cb = data->state.fread_func; + ctx->cb_user_data = data->state.in; + ctx->total_len = -1; + ctx->read_len = 0; + return CURLE_OK; +} + +/* Real client reader to installed client callbacks. */ +static CURLcode cr_in_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_in_ctx *ctx = reader->ctx; + size_t nread; + + ctx->is_paused = FALSE; + + /* Once we have errored, we will return the same error forever */ + if(ctx->errored) { + *pnread = 0; + *peos = FALSE; + return ctx->error_result; + } + if(ctx->seen_eos) { + *pnread = 0; + *peos = TRUE; + return CURLE_OK; + } + /* respect length limitations */ + if(ctx->total_len >= 0) { + curl_off_t remain = ctx->total_len - ctx->read_len; + if(remain <= 0) + blen = 0; + else if(remain < (curl_off_t)blen) + blen = (size_t)remain; + } + nread = 0; + if(ctx->read_cb && blen) { + Curl_set_in_callback(data, true); + nread = ctx->read_cb(buf, 1, blen, ctx->cb_user_data); + Curl_set_in_callback(data, false); + ctx->has_used_cb = TRUE; + } + + switch(nread) { + case 0: + if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { + failf(data, "client read function EOF fail, " + "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T + " of needed bytes read", ctx->read_len, ctx->total_len); + return CURLE_READ_ERROR; + } + *pnread = 0; + *peos = TRUE; + ctx->seen_eos = TRUE; + break; + + case CURL_READFUNC_ABORT: + failf(data, "operation aborted by callback"); + *pnread = 0; + *peos = FALSE; + ctx->errored = TRUE; + ctx->error_result = CURLE_ABORTED_BY_CALLBACK; + return CURLE_ABORTED_BY_CALLBACK; + + case CURL_READFUNC_PAUSE: + if(data->conn->handler->flags & PROTOPT_NONETWORK) { + /* protocols that work without network cannot be paused. This is + actually only FILE:// just now, and it cannot pause since the transfer + is not done using the "normal" procedure. */ + failf(data, "Read callback asked for PAUSE when not supported"); + return CURLE_READ_ERROR; + } + /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ + CURL_TRC_READ(data, "cr_in_read, callback returned CURL_READFUNC_PAUSE"); + ctx->is_paused = TRUE; + data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ + *pnread = 0; + *peos = FALSE; + break; /* nothing was read */ + + default: + if(nread > blen) { + /* the read function returned a too large value */ + failf(data, "read function returned funny value"); + *pnread = 0; + *peos = FALSE; + ctx->errored = TRUE; + ctx->error_result = CURLE_READ_ERROR; + return CURLE_READ_ERROR; + } + ctx->read_len += nread; + if(ctx->total_len >= 0) + ctx->seen_eos = (ctx->read_len >= ctx->total_len); + *pnread = nread; + *peos = ctx->seen_eos; + break; + } + CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T + ", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, nread=%zu, eos=%d", + blen, ctx->total_len, ctx->read_len, CURLE_OK, + *pnread, *peos); + return CURLE_OK; +} + +static bool cr_in_needs_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + return ctx->has_used_cb; +} + +static curl_off_t cr_in_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + return ctx->total_len; +} + +static CURLcode cr_in_resume_from(struct Curl_easy *data, + struct Curl_creader *reader, + curl_off_t offset) +{ + struct cr_in_ctx *ctx = reader->ctx; + int seekerr = CURL_SEEKFUNC_CANTSEEK; + + DEBUGASSERT(data->conn); + /* already started reading? */ + if(ctx->read_len) + return CURLE_READ_ERROR; + + if(data->set.seek_func) { + Curl_set_in_callback(data, true); + seekerr = data->set.seek_func(data->set.seek_client, offset, SEEK_SET); + Curl_set_in_callback(data, false); + } + + if(seekerr != CURL_SEEKFUNC_OK) { + curl_off_t passed = 0; + + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_READ_ERROR; + } + /* when seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ + do { + char scratch[4*1024]; + size_t readthisamountnow = + (offset - passed > (curl_off_t)sizeof(scratch)) ? + sizeof(scratch) : + curlx_sotouz(offset - passed); + size_t actuallyread; + + Curl_set_in_callback(data, true); + actuallyread = ctx->read_cb(scratch, 1, readthisamountnow, + ctx->cb_user_data); + Curl_set_in_callback(data, false); + + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T + " bytes from the input", passed); + return CURLE_READ_ERROR; + } + } while(passed < offset); + } + + /* now, decrease the size of the read */ + if(ctx->total_len > 0) { + ctx->total_len -= offset; + + if(ctx->total_len <= 0) { + failf(data, "File already completely uploaded"); + return CURLE_PARTIAL_FILE; + } + } + /* we have passed, proceed as normal */ + return CURLE_OK; +} + +static CURLcode cr_in_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + + /* If we never invoked the callback, there is noting to rewind */ + if(!ctx->has_used_cb) + return CURLE_OK; + + if(data->set.seek_func) { + int err; + + Curl_set_in_callback(data, true); + err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); + Curl_set_in_callback(data, false); + CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err); + if(err) { + failf(data, "seek callback returned error %d", (int)err); + return CURLE_SEND_FAIL_REWIND; + } + } + else if(data->set.ioctl_func) { + curlioerr err; + + Curl_set_in_callback(data, true); + err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, + data->set.ioctl_client); + Curl_set_in_callback(data, false); + CURL_TRC_READ(data, "cr_in, rewind via set.ioctl_func -> %d", (int)err); + if(err) { + failf(data, "ioctl callback returned error %d", (int)err); + return CURLE_SEND_FAIL_REWIND; + } + } + else { + /* If no CURLOPT_READFUNCTION is used, we know that we operate on a + given FILE * stream and we can actually attempt to rewind that + ourselves with fseek() */ + if(data->state.fread_func == (curl_read_callback)fread) { + int err = fseek(data->state.in, 0, SEEK_SET); + CURL_TRC_READ(data, "cr_in, rewind via fseek -> %d(%d)", + (int)err, (int)errno); + if(-1 != err) + /* successful rewind */ + return CURLE_OK; + } + + /* no callback set or failure above, makes us fail at once */ + failf(data, "necessary data rewind was not possible"); + return CURLE_SEND_FAIL_REWIND; + } + return CURLE_OK; +} + +static CURLcode cr_in_unpause(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + ctx->is_paused = FALSE; + return CURLE_OK; +} + +static bool cr_in_is_paused(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + return ctx->is_paused; +} + +static const struct Curl_crtype cr_in = { + "cr-in", + cr_in_init, + cr_in_read, + Curl_creader_def_close, + cr_in_needs_rewind, + cr_in_total_length, + cr_in_resume_from, + cr_in_rewind, + cr_in_unpause, + cr_in_is_paused, + Curl_creader_def_done, + sizeof(struct cr_in_ctx) +}; + +CURLcode Curl_creader_create(struct Curl_creader **preader, + struct Curl_easy *data, + const struct Curl_crtype *crt, + Curl_creader_phase phase) +{ + struct Curl_creader *reader = NULL; + CURLcode result = CURLE_OUT_OF_MEMORY; + void *p; + + DEBUGASSERT(crt->creader_size >= sizeof(struct Curl_creader)); + p = calloc(1, crt->creader_size); + if(!p) + goto out; + + reader = (struct Curl_creader *)p; + reader->crt = crt; + reader->ctx = p; + reader->phase = phase; + result = crt->do_init(data, reader); + +out: + *preader = result? NULL : reader; + if(result) + free(reader); + return result; +} + +void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader) +{ + if(reader) { + reader->crt->do_close(data, reader); + free(reader); + } +} + +struct cr_lc_ctx { + struct Curl_creader super; + struct bufq buf; + BIT(read_eos); /* we read an EOS from the next reader */ + BIT(eos); /* we have returned an EOS */ +}; + +static CURLcode cr_lc_init(struct Curl_easy *data, struct Curl_creader *reader) +{ + struct cr_lc_ctx *ctx = reader->ctx; + (void)data; + Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT); + return CURLE_OK; +} + +static void cr_lc_close(struct Curl_easy *data, struct Curl_creader *reader) +{ + struct cr_lc_ctx *ctx = reader->ctx; + (void)data; + Curl_bufq_free(&ctx->buf); +} + +/* client reader doing line end conversions. */ +static CURLcode cr_lc_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_lc_ctx *ctx = reader->ctx; + CURLcode result; + size_t nread, i, start, n; + bool eos; + + if(ctx->eos) { + *pnread = 0; + *peos = TRUE; + return CURLE_OK; + } + + if(Curl_bufq_is_empty(&ctx->buf)) { + if(ctx->read_eos) { + ctx->eos = TRUE; + *pnread = 0; + *peos = TRUE; + return CURLE_OK; + } + /* Still getting data form the next reader, ctx->buf is empty */ + result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); + if(result) + return result; + ctx->read_eos = eos; + + if(!nread || !memchr(buf, '\n', nread)) { + /* nothing to convert, return this right away */ + if(ctx->read_eos) + ctx->eos = TRUE; + *pnread = nread; + *peos = ctx->eos; + goto out; + } + + /* at least one \n needs conversion to '\r\n', place into ctx->buf */ + for(i = start = 0; i < nread; ++i) { + if(buf[i] != '\n') + continue; + /* on a soft limit bufq, we do not need to check length */ + result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); + if(!result) + result = Curl_bufq_cwrite(&ctx->buf, STRCONST("\r\n"), &n); + if(result) + return result; + start = i + 1; + if(!data->set.crlf && (data->state.infilesize != -1)) { + /* we are here only because FTP is in ASCII mode... + bump infilesize for the LF we just added */ + data->state.infilesize++; + /* comment: this might work for FTP, but in HTTP we could not change + * the content length after having started the request... */ + } + } + + if(start < i) { /* leftover */ + result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); + if(result) + return result; + } + } + + DEBUGASSERT(!Curl_bufq_is_empty(&ctx->buf)); + *peos = FALSE; + result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread); + if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { + /* no more data, read all, done. */ + ctx->eos = TRUE; + *peos = TRUE; + } + +out: + CURL_TRC_READ(data, "cr_lc_read(len=%zu) -> %d, nread=%zu, eos=%d", + blen, result, *pnread, *peos); + return result; +} + +static curl_off_t cr_lc_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + /* this reader changes length depending on input */ + (void)data; + (void)reader; + return -1; +} + +static const struct Curl_crtype cr_lc = { + "cr-lineconv", + cr_lc_init, + cr_lc_read, + cr_lc_close, + Curl_creader_def_needs_rewind, + cr_lc_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_rewind, + Curl_creader_def_unpause, + Curl_creader_def_is_paused, + Curl_creader_def_done, + sizeof(struct cr_lc_ctx) +}; + +static CURLcode cr_lc_add(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &cr_lc, + CURL_CR_CONTENT_ENCODE); + if(!result) + result = Curl_creader_add(data, reader); + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + +static CURLcode do_init_reader_stack(struct Curl_easy *data, + struct Curl_creader *r) +{ + CURLcode result = CURLE_OK; + curl_off_t clen; + + DEBUGASSERT(r); + DEBUGASSERT(r->crt); + DEBUGASSERT(r->phase == CURL_CR_CLIENT); + DEBUGASSERT(!data->req.reader_stack); + + data->req.reader_stack = r; + clen = r->crt->total_length(data, r); + /* if we do not have 0 length init, and crlf conversion is wanted, + * add the reader for it */ + if(clen && (data->set.crlf +#ifdef CURL_DO_LINEEND_CONV + || data->state.prefer_ascii +#endif + )) { + result = cr_lc_add(data); + if(result) + return result; + } + + return result; +} + +CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len) +{ + CURLcode result; + struct Curl_creader *r; + struct cr_in_ctx *ctx; + + result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT); + if(result) + goto out; + ctx = r->ctx; + ctx->total_len = len; + + cl_reset_reader(data); + result = do_init_reader_stack(data, r); +out: + CURL_TRC_READ(data, "add fread reader, len=%"CURL_FORMAT_CURL_OFF_T + " -> %d", len, result); + return result; +} + +CURLcode Curl_creader_add(struct Curl_easy *data, + struct Curl_creader *reader) +{ + CURLcode result; + struct Curl_creader **anchor = &data->req.reader_stack; + + if(!*anchor) { + result = Curl_creader_set_fread(data, data->state.infilesize); + if(result) + return result; + } + + /* Insert the writer as first in its phase. + * Skip existing readers of lower phases. */ + while(*anchor && (*anchor)->phase < reader->phase) + anchor = &((*anchor)->next); + reader->next = *anchor; + *anchor = reader; + return CURLE_OK; +} + +CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r) +{ + CURLcode result; + + DEBUGASSERT(r); + DEBUGASSERT(r->crt); + DEBUGASSERT(r->phase == CURL_CR_CLIENT); + + cl_reset_reader(data); + result = do_init_reader_stack(data, r); + if(result) + Curl_creader_free(data, r); + return result; +} + +CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, + size_t *nread, bool *eos) +{ + CURLcode result; + + DEBUGASSERT(buf); + DEBUGASSERT(blen); + DEBUGASSERT(nread); + DEBUGASSERT(eos); + + if(!data->req.reader_stack) { + result = Curl_creader_set_fread(data, data->state.infilesize); + if(result) + return result; + DEBUGASSERT(data->req.reader_stack); + } + + result = Curl_creader_read(data, data->req.reader_stack, buf, blen, + nread, eos); + CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d", + blen, result, *nread, *eos); + return result; +} + +bool Curl_creader_needs_rewind(struct Curl_easy *data) +{ + struct Curl_creader *reader = data->req.reader_stack; + while(reader) { + if(reader->crt->needs_rewind(data, reader)) { + CURL_TRC_READ(data, "client reader needs rewind before next request"); + return TRUE; + } + reader = reader->next; + } + return FALSE; +} + +static CURLcode cr_null_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + (void)data; + (void)reader; + (void)buf; + (void)blen; + *pnread = 0; + *peos = TRUE; + return CURLE_OK; +} + +static curl_off_t cr_null_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + /* this reader changes length depending on input */ + (void)data; + (void)reader; + return 0; +} + +static const struct Curl_crtype cr_null = { + "cr-null", + Curl_creader_def_init, + cr_null_read, + Curl_creader_def_close, + Curl_creader_def_needs_rewind, + cr_null_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_rewind, + Curl_creader_def_unpause, + Curl_creader_def_is_paused, + Curl_creader_def_done, + sizeof(struct Curl_creader) +}; + +CURLcode Curl_creader_set_null(struct Curl_easy *data) +{ + struct Curl_creader *r; + CURLcode result; + + result = Curl_creader_create(&r, data, &cr_null, CURL_CR_CLIENT); + if(result) + return result; + + cl_reset_reader(data); + return do_init_reader_stack(data, r); +} + +struct cr_buf_ctx { + struct Curl_creader super; + const char *buf; + size_t blen; + size_t index; +}; + +static CURLcode cr_buf_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_buf_ctx *ctx = reader->ctx; + size_t nread = ctx->blen - ctx->index; + + (void)data; + if(!nread || !ctx->buf) { + *pnread = 0; + *peos = TRUE; + } + else { + if(nread > blen) + nread = blen; + memcpy(buf, ctx->buf + ctx->index, nread); + *pnread = nread; + ctx->index += nread; + *peos = (ctx->index == ctx->blen); + } + CURL_TRC_READ(data, "cr_buf_read(len=%zu) -> 0, nread=%zu, eos=%d", + blen, *pnread, *peos); + return CURLE_OK; +} + +static bool cr_buf_needs_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_buf_ctx *ctx = reader->ctx; + (void)data; + return ctx->index > 0; +} + +static curl_off_t cr_buf_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_buf_ctx *ctx = reader->ctx; + (void)data; + return (curl_off_t)ctx->blen; +} + +static CURLcode cr_buf_resume_from(struct Curl_easy *data, + struct Curl_creader *reader, + curl_off_t offset) +{ + struct cr_buf_ctx *ctx = reader->ctx; + size_t boffset; + + (void)data; + DEBUGASSERT(data->conn); + /* already started reading? */ + if(ctx->index) + return CURLE_READ_ERROR; + if(offset <= 0) + return CURLE_OK; + boffset = (size_t)offset; + if(boffset > ctx->blen) + return CURLE_READ_ERROR; + + ctx->buf += boffset; + ctx->blen -= boffset; + return CURLE_OK; +} + +static const struct Curl_crtype cr_buf = { + "cr-buf", + Curl_creader_def_init, + cr_buf_read, + Curl_creader_def_close, + cr_buf_needs_rewind, + cr_buf_total_length, + cr_buf_resume_from, + Curl_creader_def_rewind, + Curl_creader_def_unpause, + Curl_creader_def_is_paused, + Curl_creader_def_done, + sizeof(struct cr_buf_ctx) +}; + +CURLcode Curl_creader_set_buf(struct Curl_easy *data, + const char *buf, size_t blen) +{ + CURLcode result; + struct Curl_creader *r; + struct cr_buf_ctx *ctx; + + result = Curl_creader_create(&r, data, &cr_buf, CURL_CR_CLIENT); + if(result) + goto out; + ctx = r->ctx; + ctx->buf = buf; + ctx->blen = blen; + ctx->index = 0; + + cl_reset_reader(data); + result = do_init_reader_stack(data, r); +out: + CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, result); + return result; +} + +curl_off_t Curl_creader_total_length(struct Curl_easy *data) +{ + struct Curl_creader *r = data->req.reader_stack; + return r? r->crt->total_length(data, r) : -1; +} + +curl_off_t Curl_creader_client_length(struct Curl_easy *data) +{ + struct Curl_creader *r = data->req.reader_stack; + while(r && r->phase != CURL_CR_CLIENT) + r = r->next; + return r? r->crt->total_length(data, r) : -1; +} + +CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset) +{ + struct Curl_creader *r = data->req.reader_stack; + while(r && r->phase != CURL_CR_CLIENT) + r = r->next; + return r? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR; +} + +CURLcode Curl_creader_unpause(struct Curl_easy *data) +{ + struct Curl_creader *reader = data->req.reader_stack; + CURLcode result = CURLE_OK; + + while(reader) { + result = reader->crt->unpause(data, reader); + if(result) + break; + reader = reader->next; + } + return result; +} + +bool Curl_creader_is_paused(struct Curl_easy *data) +{ + struct Curl_creader *reader = data->req.reader_stack; + + while(reader) { + if(reader->crt->is_paused(data, reader)) + return TRUE; + reader = reader->next; + } + return FALSE; +} + +void Curl_creader_done(struct Curl_easy *data, int premature) +{ + struct Curl_creader *reader = data->req.reader_stack; + while(reader) { + reader->crt->done(data, reader, premature); + reader = reader->next; + } +} + +struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data, + const struct Curl_crtype *crt) +{ + struct Curl_creader *r; + for(r = data->req.reader_stack; r; r = r->next) { + if(r->crt == crt) + return r; + } + return NULL; + +} diff --git a/third_party/curl/lib/sendf.h b/third_party/curl/lib/sendf.h new file mode 100644 index 0000000000..dc1b82edfe --- /dev/null +++ b/third_party/curl/lib/sendf.h @@ -0,0 +1,415 @@ +#ifndef HEADER_CURL_SENDF_H +#define HEADER_CURL_SENDF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "curl_trc.h" + +/** + * Type of data that is being written to the client (application) + * - data written can be either BODY or META data + * - META data is either INFO or HEADER + * - INFO is meta information, e.g. not BODY, that cannot be interpreted + * as headers of a response. Example FTP/IMAP pingpong answers. + * - HEADER can have additional bits set (more than one) + * - STATUS special "header", e.g. response status line in HTTP + * - CONNECT header was received during proxying the connection + * - 1XX header is part of an intermediate response, e.g. HTTP 1xx code + * - TRAILER header is trailing response data, e.g. HTTP trailers + * BODY, INFO and HEADER should not be mixed, as this would lead to + * confusion on how to interpret/format/convert the data. + */ +#define CLIENTWRITE_BODY (1<<0) /* non-meta information, BODY */ +#define CLIENTWRITE_INFO (1<<1) /* meta information, not a HEADER */ +#define CLIENTWRITE_HEADER (1<<2) /* meta information, HEADER */ +#define CLIENTWRITE_STATUS (1<<3) /* a special status HEADER */ +#define CLIENTWRITE_CONNECT (1<<4) /* a CONNECT related HEADER */ +#define CLIENTWRITE_1XX (1<<5) /* a 1xx response related HEADER */ +#define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */ +#define CLIENTWRITE_EOS (1<<7) /* End Of transfer download Stream */ + +/** + * Write `len` bytes at `prt` to the client. `type` indicates what + * kind of data is being written. + */ +CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *ptr, + size_t len) WARN_UNUSED_RESULT; + +/** + * Free all resources related to client writing. + */ +void Curl_client_cleanup(struct Curl_easy *data); + +/** + * Reset readers and writer chains, keep rewind information + * when necessary. + */ +void Curl_client_reset(struct Curl_easy *data); + +/** + * A new request is starting, perform any ops like rewinding + * previous readers when needed. + */ +CURLcode Curl_client_start(struct Curl_easy *data); + +/** + * Client Writers - a chain passing transfer BODY data to the client. + * Main application: HTTP and related protocols + * Other uses: monitoring of download progress + * + * Writers in the chain are order by their `phase`. First come all + * writers in CURL_CW_RAW, followed by any in CURL_CW_TRANSFER_DECODE, + * followed by any in CURL_CW_PROTOCOL, etc. + * + * When adding a writer, it is inserted as first in its phase. This means + * the order of adding writers of the same phase matters, but writers for + * different phases may be added in any order. + * + * Writers which do modify the BODY data written are expected to be of + * phases TRANSFER_DECODE or CONTENT_DECODE. The other phases are intended + * for monitoring writers. Which do *not* modify the data but gather + * statistics or update progress reporting. + */ + +/* Phase a writer operates at. */ +typedef enum { + CURL_CW_RAW, /* raw data written, before any decoding */ + CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */ + CURL_CW_PROTOCOL, /* after transfer, but before content decoding */ + CURL_CW_CONTENT_DECODE, /* remove content-encodings */ + CURL_CW_CLIENT /* data written to client */ +} Curl_cwriter_phase; + +/* Client Writer Type, provides the implementation */ +struct Curl_cwtype { + const char *name; /* writer name. */ + const char *alias; /* writer name alias, maybe NULL. */ + CURLcode (*do_init)(struct Curl_easy *data, + struct Curl_cwriter *writer); + CURLcode (*do_write)(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes); + void (*do_close)(struct Curl_easy *data, + struct Curl_cwriter *writer); + size_t cwriter_size; /* sizeof() allocated struct Curl_cwriter */ +}; + +/* Client writer instance, allocated on creation. + * `void *ctx` is the pointer from the allocation of + * the `struct Curl_cwriter` itself. This is suitable for "downcasting" + * by the writers implementation. See https://github.com/curl/curl/pull/13054 + * for the alignment problems that arise otherwise. + */ +struct Curl_cwriter { + const struct Curl_cwtype *cwt; /* type implementation */ + struct Curl_cwriter *next; /* Downstream writer. */ + void *ctx; /* allocated instance pointer */ + Curl_cwriter_phase phase; /* phase at which it operates */ +}; + +/** + * Create a new cwriter instance with given type and phase. Is not + * inserted into the writer chain by this call. + * Invokes `writer->do_init()`. + */ +CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, + struct Curl_easy *data, + const struct Curl_cwtype *ce_handler, + Curl_cwriter_phase phase); + +/** + * Free a cwriter instance. + * Invokes `writer->do_close()`. + */ +void Curl_cwriter_free(struct Curl_easy *data, + struct Curl_cwriter *writer); + +/** + * Count the number of writers installed of the given phase. + */ +size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase); + +/** + * Adds a writer to the transfer's writer chain. + * The writers `phase` determines where in the chain it is inserted. + */ +CURLcode Curl_cwriter_add(struct Curl_easy *data, + struct Curl_cwriter *writer); + +/** + * Look up an installed client writer on `data` by its type. + * @return first writer with that type or NULL + */ +struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data, + const struct Curl_cwtype *cwt); + +void Curl_cwriter_remove_by_name(struct Curl_easy *data, + const char *name); + +struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data, + const char *name); + +/** + * Convenience method for calling `writer->do_write()` that + * checks for NULL writer. + */ +CURLcode Curl_cwriter_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes); + +/** + * Return TRUE iff client writer is paused. + */ +bool Curl_cwriter_is_paused(struct Curl_easy *data); + +/** + * Unpause client writer and flush any buffered date to the client. + */ +CURLcode Curl_cwriter_unpause(struct Curl_easy *data); + +/** + * Default implementations for do_init, do_write, do_close that + * do nothing and pass the data through. + */ +CURLcode Curl_cwriter_def_init(struct Curl_easy *data, + struct Curl_cwriter *writer); +CURLcode Curl_cwriter_def_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes); +void Curl_cwriter_def_close(struct Curl_easy *data, + struct Curl_cwriter *writer); + + + +/* Client Reader Type, provides the implementation */ +struct Curl_crtype { + const char *name; /* writer name. */ + CURLcode (*do_init)(struct Curl_easy *data, struct Curl_creader *reader); + CURLcode (*do_read)(struct Curl_easy *data, struct Curl_creader *reader, + char *buf, size_t blen, size_t *nread, bool *eos); + void (*do_close)(struct Curl_easy *data, struct Curl_creader *reader); + bool (*needs_rewind)(struct Curl_easy *data, struct Curl_creader *reader); + curl_off_t (*total_length)(struct Curl_easy *data, + struct Curl_creader *reader); + CURLcode (*resume_from)(struct Curl_easy *data, + struct Curl_creader *reader, curl_off_t offset); + CURLcode (*rewind)(struct Curl_easy *data, struct Curl_creader *reader); + CURLcode (*unpause)(struct Curl_easy *data, struct Curl_creader *reader); + bool (*is_paused)(struct Curl_easy *data, struct Curl_creader *reader); + void (*done)(struct Curl_easy *data, + struct Curl_creader *reader, int premature); + size_t creader_size; /* sizeof() allocated struct Curl_creader */ +}; + +/* Phase a reader operates at. */ +typedef enum { + CURL_CR_NET, /* data send to the network (connection filters) */ + CURL_CR_TRANSFER_ENCODE, /* add transfer-encodings */ + CURL_CR_PROTOCOL, /* before transfer, but after content decoding */ + CURL_CR_CONTENT_ENCODE, /* add content-encodings */ + CURL_CR_CLIENT /* data read from client */ +} Curl_creader_phase; + +/* Client reader instance, allocated on creation. + * `void *ctx` is the pointer from the allocation of + * the `struct Curl_cwriter` itself. This is suitable for "downcasting" + * by the writers implementation. See https://github.com/curl/curl/pull/13054 + * for the alignment problems that arise otherwise. + */ +struct Curl_creader { + const struct Curl_crtype *crt; /* type implementation */ + struct Curl_creader *next; /* Downstream reader. */ + void *ctx; + Curl_creader_phase phase; /* phase at which it operates */ +}; + +/** + * Default implementations for do_init, do_write, do_close that + * do nothing and pass the data through. + */ +CURLcode Curl_creader_def_init(struct Curl_easy *data, + struct Curl_creader *reader); +void Curl_creader_def_close(struct Curl_easy *data, + struct Curl_creader *reader); +CURLcode Curl_creader_def_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *nread, bool *eos); +bool Curl_creader_def_needs_rewind(struct Curl_easy *data, + struct Curl_creader *reader); +curl_off_t Curl_creader_def_total_length(struct Curl_easy *data, + struct Curl_creader *reader); +CURLcode Curl_creader_def_resume_from(struct Curl_easy *data, + struct Curl_creader *reader, + curl_off_t offset); +CURLcode Curl_creader_def_rewind(struct Curl_easy *data, + struct Curl_creader *reader); +CURLcode Curl_creader_def_unpause(struct Curl_easy *data, + struct Curl_creader *reader); +bool Curl_creader_def_is_paused(struct Curl_easy *data, + struct Curl_creader *reader); +void Curl_creader_def_done(struct Curl_easy *data, + struct Curl_creader *reader, int premature); + +/** + * Convenience method for calling `reader->do_read()` that + * checks for NULL reader. + */ +CURLcode Curl_creader_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, size_t *nread, bool *eos); + +/** + * Create a new creader instance with given type and phase. Is not + * inserted into the writer chain by this call. + * Invokes `reader->do_init()`. + */ +CURLcode Curl_creader_create(struct Curl_creader **preader, + struct Curl_easy *data, + const struct Curl_crtype *cr_handler, + Curl_creader_phase phase); + +/** + * Free a creader instance. + * Invokes `reader->do_close()`. + */ +void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader); + +/** + * Adds a reader to the transfer's reader chain. + * The readers `phase` determines where in the chain it is inserted. + */ +CURLcode Curl_creader_add(struct Curl_easy *data, + struct Curl_creader *reader); + +/** + * Set the given reader, which needs to be of type CURL_CR_CLIENT, + * as the new first reader. Discard any installed readers and init + * the reader chain anew. + * The function takes ownership of `r`. + */ +CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r); + +/** + * Read at most `blen` bytes at `buf` from the client. + * @param data the transfer to read client bytes for + * @param buf the memory location to read to + * @param blen the amount of memory at `buf` + * @param nread on return the number of bytes read into `buf` + * @param eos TRUE iff bytes are the end of data from client + * @return CURLE_OK on successful read (even 0 length) or error + */ +CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, + size_t *nread, bool *eos) WARN_UNUSED_RESULT; + +/** + * TRUE iff client reader needs rewing before it can be used for + * a retry request. + */ +bool Curl_creader_needs_rewind(struct Curl_easy *data); + +/** + * TRUE iff client reader will rewind at next start + */ +bool Curl_creader_will_rewind(struct Curl_easy *data); + +/** + * En-/disable rewind of client reader at next start. + */ +void Curl_creader_set_rewind(struct Curl_easy *data, bool enable); + +/** + * Get the total length of bytes provided by the installed readers. + * This is independent of the amount already delivered and is calculated + * by all readers in the stack. If a reader like "chunked" or + * "crlf conversion" is installed, the returned length will be -1. + * @return -1 if length is indeterminate + */ +curl_off_t Curl_creader_total_length(struct Curl_easy *data); + +/** + * Get the total length of bytes provided by the reader at phase + * CURL_CR_CLIENT. This may not match the amount of bytes read + * for a request, depending if other, encoding readers are also installed. + * However it allows for rough estimation of the overall length. + * @return -1 if length is indeterminate + */ +curl_off_t Curl_creader_client_length(struct Curl_easy *data); + +/** + * Ask the installed reader at phase CURL_CR_CLIENT to start + * reading from the given offset. On success, this will reduce + * the `total_length()` by the amount. + * @param data the transfer to read client bytes for + * @param offset the offset where to start reads from, negative + * values will be ignored. + * @return CURLE_OK if offset could be set + * CURLE_READ_ERROR if not supported by reader or seek/read failed + * of offset larger then total length + * CURLE_PARTIAL_FILE if offset led to 0 total length + */ +CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset); + +/** + * Unpause all installed readers. + */ +CURLcode Curl_creader_unpause(struct Curl_easy *data); + +/** + * Return TRUE iff any of the installed readers is paused. + */ +bool Curl_creader_is_paused(struct Curl_easy *data); + +/** + * Tell all client readers that they are done. + */ +void Curl_creader_done(struct Curl_easy *data, int premature); + +/** + * Look up an installed client reader on `data` by its type. + * @return first reader with that type or NULL + */ +struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data, + const struct Curl_crtype *crt); + + +/** + * Set the client reader to provide 0 bytes, immediate EOS. + */ +CURLcode Curl_creader_set_null(struct Curl_easy *data); + +/** + * Set the client reader the reads from fread callback. + */ +CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len); + +/** + * Set the client reader the reads from the supplied buf (NOT COPIED). + */ +CURLcode Curl_creader_set_buf(struct Curl_easy *data, + const char *buf, size_t blen); + +#endif /* HEADER_CURL_SENDF_H */ diff --git a/third_party/curl/lib/setopt.c b/third_party/curl/lib/setopt.c new file mode 100644 index 0000000000..7d35651d10 --- /dev/null +++ b/third_party/curl/lib/setopt.c @@ -0,0 +1,3264 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_LINUX_TCP_H +#include +#elif defined(HAVE_NETINET_TCP_H) +#include +#endif + +#include "urldata.h" +#include "url.h" +#include "progress.h" +#include "content_encoding.h" +#include "strcase.h" +#include "share.h" +#include "vtls/vtls.h" +#include "warnless.h" +#include "sendf.h" +#include "http2.h" +#include "setopt.h" +#include "multiif.h" +#include "altsvc.h" +#include "hsts.h" +#include "tftp.h" +#include "strdup.h" +#include "escape.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +CURLcode Curl_setstropt(char **charp, const char *s) +{ + /* Release the previous storage at `charp' and replace by a dynamic storage + copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */ + + Curl_safefree(*charp); + + if(s) { + if(strlen(s) > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + + *charp = strdup(s); + if(!*charp) + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +CURLcode Curl_setblobopt(struct curl_blob **blobp, + const struct curl_blob *blob) +{ + /* free the previous storage at `blobp' and replace by a dynamic storage + copy of blob. If CURL_BLOB_COPY is set, the data is copied. */ + + Curl_safefree(*blobp); + + if(blob) { + struct curl_blob *nblob; + if(blob->len > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + nblob = (struct curl_blob *) + malloc(sizeof(struct curl_blob) + + ((blob->flags & CURL_BLOB_COPY) ? blob->len : 0)); + if(!nblob) + return CURLE_OUT_OF_MEMORY; + *nblob = *blob; + if(blob->flags & CURL_BLOB_COPY) { + /* put the data after the blob struct in memory */ + nblob->data = (char *)nblob + sizeof(struct curl_blob); + memcpy(nblob->data, blob->data, blob->len); + } + + *blobp = nblob; + return CURLE_OK; + } + + return CURLE_OK; +} + +static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) +{ + char *user = NULL; + char *passwd = NULL; + + DEBUGASSERT(userp); + DEBUGASSERT(passwdp); + + /* Parse the login details if specified. It not then we treat NULL as a hint + to clear the existing data */ + if(option) { + size_t len = strlen(option); + CURLcode result; + if(len > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + + result = Curl_parse_login_details(option, len, &user, &passwd, NULL); + if(result) + return result; + } + + free(*userp); + *userp = user; + + free(*passwdp); + *passwdp = passwd; + + return CURLE_OK; +} + +static CURLcode setstropt_interface( + char *option, char **devp, char **ifacep, char **hostp) +{ + char *dev = NULL; + char *iface = NULL; + char *host = NULL; + size_t len; + CURLcode result; + + DEBUGASSERT(devp); + DEBUGASSERT(ifacep); + DEBUGASSERT(hostp); + + /* Parse the interface details */ + if(!option || !*option) + return CURLE_BAD_FUNCTION_ARGUMENT; + len = strlen(option); + if(len > 255) + return CURLE_BAD_FUNCTION_ARGUMENT; + + result = Curl_parse_interface(option, len, &dev, &iface, &host); + if(result) + return result; + + free(*devp); + *devp = dev; + + free(*ifacep); + *ifacep = iface; + + free(*hostp); + *hostp = host; + + return CURLE_OK; +} + +#define C_SSLVERSION_VALUE(x) (x & 0xffff) +#define C_SSLVERSION_MAX_VALUE(x) ((unsigned long)x & 0xffff0000) + +static CURLcode protocol2num(const char *str, curl_prot_t *val) +{ + /* + * We are asked to cherry-pick protocols, so play it safe and disallow all + * protocols to start with, and re-add the wanted ones back in. + */ + *val = 0; + + if(!str) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(curl_strequal(str, "all")) { + *val = ~(curl_prot_t) 0; + return CURLE_OK; + } + + do { + const char *token = str; + size_t tlen; + + str = strchr(str, ','); + tlen = str? (size_t) (str - token): strlen(token); + if(tlen) { + const struct Curl_handler *h = Curl_getn_scheme_handler(token, tlen); + + if(!h) + return CURLE_UNSUPPORTED_PROTOCOL; + + *val |= h->protocol; + } + } while(str && str++); + + if(!*val) + /* no protocol listed */ + return CURLE_BAD_FUNCTION_ARGUMENT; + return CURLE_OK; +} + +/* + * Do not make Curl_vsetopt() static: it is called from + * packages/OS400/ccsidcurl.c. + */ +CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) +{ + char *argptr; + CURLcode result = CURLE_OK; + long arg; + unsigned long uarg; + curl_off_t bigsize; + + switch(option) { + case CURLOPT_DNS_CACHE_TIMEOUT: + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; + + data->set.dns_cache_timeout = (int)arg; + break; + case CURLOPT_CA_CACHE_TIMEOUT: + if(Curl_ssl_supports(data, SSLSUPP_CA_CACHE)) { + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; + + data->set.general_ssl.ca_cache_timeout = (int)arg; + } + else + return CURLE_NOT_BUILT_IN; + break; + case CURLOPT_DNS_USE_GLOBAL_CACHE: + /* deprecated */ + break; + case CURLOPT_SSL_CIPHER_LIST: + /* set a list of cipher we want to use in the SSL connection */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST], + va_arg(param, char *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSL_CIPHER_LIST: + /* set a list of cipher we want to use in the SSL connection for proxy */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_PROXY], + va_arg(param, char *)); + break; +#endif + case CURLOPT_TLS13_CIPHERS: + 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 *)); + } + else + return CURLE_NOT_BUILT_IN; + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_TLS13_CIPHERS: + 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 *)); + } + else + return CURLE_NOT_BUILT_IN; + break; +#endif + case CURLOPT_RANDOM_FILE: + break; + case CURLOPT_EGDSOCKET: + break; + case CURLOPT_MAXCONNECTS: + /* + * Set the absolute number of maximum simultaneous alive connection that + * libcurl is allowed to have. + */ + uarg = va_arg(param, unsigned long); + if(uarg > UINT_MAX) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.maxconnects = (unsigned int)uarg; + break; + case CURLOPT_FORBID_REUSE: + /* + * When this transfer is done, it must not be left to be reused by a + * subsequent transfer but shall be closed immediately. + */ + data->set.reuse_forbid = (0 != va_arg(param, long)); + break; + case CURLOPT_FRESH_CONNECT: + /* + * This transfer shall not use a previously cached connection but + * should be made with a fresh new connect! + */ + data->set.reuse_fresh = (0 != va_arg(param, long)); + break; + case CURLOPT_VERBOSE: + /* + * Verbose means infof() calls that give a lot of information about + * the connection and transfer procedures as well as internal choices. + */ + data->set.verbose = (0 != va_arg(param, long)); + break; + case CURLOPT_HEADER: + /* + * Set to include the header in the general data output stream. + */ + data->set.include_header = (0 != va_arg(param, long)); + break; + case CURLOPT_NOPROGRESS: + /* + * Shut off the internal supported progress meter + */ + data->set.hide_progress = (0 != va_arg(param, long)); + if(data->set.hide_progress) + data->progress.flags |= PGRS_HIDE; + else + data->progress.flags &= ~PGRS_HIDE; + break; + case CURLOPT_NOBODY: + /* + * Do not include the body part in the output data stream. + */ + data->set.opt_no_body = (0 != va_arg(param, long)); +#ifndef CURL_DISABLE_HTTP + if(data->set.opt_no_body) + /* in HTTP lingo, no body means using the HEAD request... */ + data->set.method = HTTPREQ_HEAD; + else if(data->set.method == HTTPREQ_HEAD) + data->set.method = HTTPREQ_GET; +#endif + break; + case CURLOPT_FAILONERROR: + /* + * Do not output the >=400 error code HTML-page, but instead only + * return error. + */ + data->set.http_fail_on_error = (0 != va_arg(param, long)); + break; + case CURLOPT_KEEP_SENDING_ON_ERROR: + data->set.http_keep_sending_on_error = (0 != va_arg(param, long)); + break; + case CURLOPT_UPLOAD: + case CURLOPT_PUT: + /* + * We want to sent data to the remote host. If this is HTTP, that equals + * using the PUT request. + */ + arg = va_arg(param, long); + if(arg) { + /* If this is HTTP, PUT is what's needed to "upload" */ + data->set.method = HTTPREQ_PUT; + data->set.opt_no_body = FALSE; /* this is implied */ + } + else + /* In HTTP, the opposite of upload is GET (unless NOBODY is true as + then this can be changed to HEAD later on) */ + data->set.method = HTTPREQ_GET; + break; + case CURLOPT_REQUEST_TARGET: + result = Curl_setstropt(&data->set.str[STRING_TARGET], + va_arg(param, char *)); + break; + case CURLOPT_FILETIME: + /* + * Try to get the file time of the remote document. The time will + * later (possibly) become available using curl_easy_getinfo(). + */ + data->set.get_filetime = (0 != va_arg(param, long)); + break; + case CURLOPT_SERVER_RESPONSE_TIMEOUT: + /* + * Option that specifies how quickly a server response must be obtained + * before it is considered failure. For pingpong protocols. + */ + arg = va_arg(param, long); + if((arg >= 0) && (arg <= (INT_MAX/1000))) + data->set.server_response_timeout = (unsigned int)arg * 1000; + else + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + case CURLOPT_SERVER_RESPONSE_TIMEOUT_MS: + /* + * Option that specifies how quickly a server response must be obtained + * before it is considered failure. For pingpong protocols. + */ + arg = va_arg(param, long); + if((arg >= 0) && (arg <= INT_MAX)) + data->set.server_response_timeout = (unsigned int)arg; + else + return CURLE_BAD_FUNCTION_ARGUMENT; + break; +#ifndef CURL_DISABLE_TFTP + case CURLOPT_TFTP_NO_OPTIONS: + /* + * Option that prevents libcurl from sending TFTP option requests to the + * server. + */ + data->set.tftp_no_options = va_arg(param, long) != 0; + break; + case CURLOPT_TFTP_BLKSIZE: + /* + * TFTP option that specifies the block size to use for data transmission. + */ + arg = va_arg(param, long); + if(arg > TFTP_BLKSIZE_MAX || arg < TFTP_BLKSIZE_MIN) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.tftp_blksize = arg; + break; +#endif +#ifndef CURL_DISABLE_NETRC + case CURLOPT_NETRC: + /* + * Parse the $HOME/.netrc file + */ + arg = va_arg(param, long); + if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.use_netrc = (unsigned char)arg; + break; + case CURLOPT_NETRC_FILE: + /* + * Use this file instead of the $HOME/.netrc file + */ + result = Curl_setstropt(&data->set.str[STRING_NETRC_FILE], + va_arg(param, char *)); + break; +#endif + case CURLOPT_TRANSFERTEXT: + /* + * This option was previously named 'FTPASCII'. Renamed to work with + * more protocols than merely FTP. + * + * Transfer using ASCII (instead of BINARY). + */ + data->set.prefer_ascii = (0 != va_arg(param, long)); + break; + case CURLOPT_TIMECONDITION: + /* + * Set HTTP time condition. This must be one of the defines in the + * curl/curl.h header file. + */ + arg = va_arg(param, long); + if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.timecondition = (unsigned char)(curl_TimeCond)arg; + break; + case CURLOPT_TIMEVALUE: + /* + * This is the value to compare with the remote document with the + * method set with CURLOPT_TIMECONDITION + */ + data->set.timevalue = (time_t)va_arg(param, long); + break; + + case CURLOPT_TIMEVALUE_LARGE: + /* + * This is the value to compare with the remote document with the + * method set with CURLOPT_TIMECONDITION + */ + data->set.timevalue = (time_t)va_arg(param, curl_off_t); + break; + + case CURLOPT_SSLVERSION: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLVERSION: +#endif + /* + * Set explicit SSL version to try to connect with, as some SSL + * implementations are lame. + */ +#ifdef USE_SSL + { + long version, version_max; + struct ssl_primary_config *primary = &data->set.ssl.primary; +#ifndef CURL_DISABLE_PROXY + if(option != CURLOPT_SSLVERSION) + primary = &data->set.proxy_ssl.primary; +#endif + + arg = va_arg(param, long); + + version = C_SSLVERSION_VALUE(arg); + version_max = (long)C_SSLVERSION_MAX_VALUE(arg); + + if(version < CURL_SSLVERSION_DEFAULT || + version == CURL_SSLVERSION_SSLv2 || + version == CURL_SSLVERSION_SSLv3 || + version >= CURL_SSLVERSION_LAST || + version_max < CURL_SSLVERSION_MAX_NONE || + version_max >= CURL_SSLVERSION_MAX_LAST) + return CURLE_BAD_FUNCTION_ARGUMENT; + + primary->version = (unsigned char)version; + primary->version_max = (unsigned int)version_max; + } +#else + result = CURLE_NOT_BUILT_IN; +#endif + break; + + /* MQTT "borrows" some of the HTTP options */ +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) + case CURLOPT_COPYPOSTFIELDS: + /* + * A string with POST data. Makes curl HTTP POST. Even if it is NULL. + * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to + * CURLOPT_COPYPOSTFIELDS and not altered later. + */ + argptr = va_arg(param, char *); + + if(!argptr || data->set.postfieldsize == -1) + result = Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr); + else { + /* + * Check that requested length does not overflow the size_t type. + */ + + if((data->set.postfieldsize < 0) || + ((sizeof(curl_off_t) != sizeof(size_t)) && + (data->set.postfieldsize > (curl_off_t)((size_t)-1)))) + result = CURLE_OUT_OF_MEMORY; + else { + /* Allocate even when size == 0. This satisfies the need of possible + later address compare to detect the COPYPOSTFIELDS mode, and to + mark that postfields is used rather than read function or form + data. + */ + char *p = Curl_memdup0(argptr, (size_t)data->set.postfieldsize); + if(!p) + result = CURLE_OUT_OF_MEMORY; + else { + free(data->set.str[STRING_COPYPOSTFIELDS]); + data->set.str[STRING_COPYPOSTFIELDS] = p; + } + } + } + + data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS]; + data->set.method = HTTPREQ_POST; + break; + + case CURLOPT_POSTFIELDS: + /* + * Like above, but use static data instead of copying it. + */ + data->set.postfields = va_arg(param, void *); + /* Release old copied data. */ + Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); + data->set.method = HTTPREQ_POST; + break; + + case CURLOPT_POSTFIELDSIZE: + /* + * The size of the POSTFIELD data to prevent libcurl to do strlen() to + * figure it out. Enables binary posts. + */ + bigsize = va_arg(param, long); + if(bigsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(data->set.postfieldsize < bigsize && + data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { + /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ + Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); + data->set.postfields = NULL; + } + + data->set.postfieldsize = bigsize; + break; + + case CURLOPT_POSTFIELDSIZE_LARGE: + /* + * The size of the POSTFIELD data to prevent libcurl to do strlen() to + * figure it out. Enables binary posts. + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(data->set.postfieldsize < bigsize && + data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { + /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ + Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); + data->set.postfields = NULL; + } + + data->set.postfieldsize = bigsize; + break; +#endif +#ifndef CURL_DISABLE_HTTP + case CURLOPT_AUTOREFERER: + /* + * Switch on automatic referer that gets set if curl follows locations. + */ + data->set.http_auto_referer = (0 != va_arg(param, long)); + break; + + case CURLOPT_ACCEPT_ENCODING: + /* + * String to use at the value of Accept-Encoding header. + * + * If the encoding is set to "" we use an Accept-Encoding header that + * encompasses all the encodings we support. + * If the encoding is set to NULL we do not send an Accept-Encoding header + * and ignore an received Content-Encoding header. + * + */ + argptr = va_arg(param, char *); + if(argptr && !*argptr) { + char all[256]; + Curl_all_content_encodings(all, sizeof(all)); + result = Curl_setstropt(&data->set.str[STRING_ENCODING], all); + } + else + result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr); + break; + + case CURLOPT_TRANSFER_ENCODING: + data->set.http_transfer_encoding = (0 != va_arg(param, long)); + break; + + case CURLOPT_FOLLOWLOCATION: + /* + * Follow Location: header hints on an HTTP-server. + */ + data->set.http_follow_location = (0 != va_arg(param, long)); + break; + + case CURLOPT_UNRESTRICTED_AUTH: + /* + * Send authentication (user+password) when following locations, even when + * hostname changed. + */ + data->set.allow_auth_to_other_hosts = (0 != va_arg(param, long)); + break; + + case CURLOPT_MAXREDIRS: + /* + * The maximum amount of hops you allow curl to follow Location: + * headers. This should mostly be used to detect never-ending loops. + */ + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.maxredirs = arg; + break; + + case CURLOPT_POSTREDIR: + /* + * Set the behavior of POST when redirecting + * CURL_REDIR_GET_ALL - POST is changed to GET after 301 and 302 + * CURL_REDIR_POST_301 - POST is kept as POST after 301 + * CURL_REDIR_POST_302 - POST is kept as POST after 302 + * CURL_REDIR_POST_303 - POST is kept as POST after 303 + * CURL_REDIR_POST_ALL - POST is kept as POST after 301, 302 and 303 + * other - POST is kept as POST after 301 and 302 + */ + arg = va_arg(param, long); + if(arg < CURL_REDIR_GET_ALL) + /* no return error on too high numbers since the bitmask could be + extended in a future */ + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.keep_post = arg & CURL_REDIR_POST_ALL; + break; + + case CURLOPT_POST: + /* Does this option serve a purpose anymore? Yes it does, when + CURLOPT_POSTFIELDS is not used and the POST data is read off the + callback! */ + if(va_arg(param, long)) { + data->set.method = HTTPREQ_POST; + data->set.opt_no_body = FALSE; /* this is implied */ + } + else + data->set.method = HTTPREQ_GET; + break; + +#ifndef CURL_DISABLE_FORM_API + case CURLOPT_HTTPPOST: + /* + * Set to make us do HTTP POST. Legacy API-style. + */ + data->set.httppost = va_arg(param, struct curl_httppost *); + data->set.method = HTTPREQ_POST_FORM; + data->set.opt_no_body = FALSE; /* this is implied */ + Curl_mime_cleanpart(data->state.formp); + Curl_safefree(data->state.formp); + data->state.mimepost = NULL; + break; +#endif + +#if !defined(CURL_DISABLE_AWS) + case CURLOPT_AWS_SIGV4: + /* + * String that is merged to some authentication + * parameters are used by the algorithm. + */ + result = Curl_setstropt(&data->set.str[STRING_AWS_SIGV4], + va_arg(param, char *)); + /* + * Basic been set by default it need to be unset here + */ + if(data->set.str[STRING_AWS_SIGV4]) + data->set.httpauth = CURLAUTH_AWS_SIGV4; + break; +#endif + + case CURLOPT_REFERER: + /* + * String to set in the HTTP Referer: field. + */ + if(data->state.referer_alloc) { + Curl_safefree(data->state.referer); + data->state.referer_alloc = FALSE; + } + result = Curl_setstropt(&data->set.str[STRING_SET_REFERER], + va_arg(param, char *)); + data->state.referer = data->set.str[STRING_SET_REFERER]; + break; + + case CURLOPT_USERAGENT: + /* + * String to use in the HTTP User-Agent field + */ + result = Curl_setstropt(&data->set.str[STRING_USERAGENT], + va_arg(param, char *)); + break; + +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXYHEADER: + /* + * Set a list with proxy headers to use (or replace internals with) + * + * Since CURLOPT_HTTPHEADER was the only way to set HTTP headers for a + * long time we remain doing it this way until CURLOPT_PROXYHEADER is + * used. As soon as this option has been used, if set to anything but + * NULL, custom headers for proxies are only picked from this list. + * + * Set this option to NULL to restore the previous behavior. + */ + data->set.proxyheaders = va_arg(param, struct curl_slist *); + break; +#endif + case CURLOPT_HEADEROPT: + /* + * Set header option. + */ + arg = va_arg(param, long); + data->set.sep_headers = !!(arg & CURLHEADER_SEPARATE); + break; + +#if !defined(CURL_DISABLE_COOKIES) + case CURLOPT_COOKIE: + /* + * Cookie string to send to the remote server in the request. + */ + result = Curl_setstropt(&data->set.str[STRING_COOKIE], + va_arg(param, char *)); + break; + + case CURLOPT_COOKIEFILE: + /* + * Set cookie file to read and parse. Can be used multiple times. + */ + argptr = (char *)va_arg(param, void *); + if(argptr) { + struct curl_slist *cl; + /* general protection against mistakes and abuse */ + if(strlen(argptr) > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + /* append the cookie filename to the list of filenames, and deal with + them later */ + cl = curl_slist_append(data->state.cookielist, argptr); + if(!cl) { + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; + return CURLE_OUT_OF_MEMORY; + } + data->state.cookielist = cl; /* store the list for later use */ + } + else { + /* clear the list of cookie files */ + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; + + if(!data->share || !data->share->cookies) { + /* throw away all existing cookies if this is not a shared cookie + container */ + Curl_cookie_clearall(data->cookies); + Curl_cookie_cleanup(data->cookies); + } + /* disable the cookie engine */ + data->cookies = NULL; + } + break; + + case CURLOPT_COOKIEJAR: + /* + * Set cookie filename to dump all cookies to when we are done. + */ + result = Curl_setstropt(&data->set.str[STRING_COOKIEJAR], + va_arg(param, char *)); + if(!result) { + /* + * Activate the cookie parser. This may or may not already + * have been made. + */ + struct CookieInfo *newcookies = + Curl_cookie_init(data, NULL, data->cookies, data->set.cookiesession); + if(!newcookies) + result = CURLE_OUT_OF_MEMORY; + data->cookies = newcookies; + } + break; + + case CURLOPT_COOKIESESSION: + /* + * Set this option to TRUE to start a new "cookie session". It will + * prevent the forthcoming read-cookies-from-file actions to accept + * cookies that are marked as being session cookies, as they belong to a + * previous session. + */ + data->set.cookiesession = (0 != va_arg(param, long)); + break; + + case CURLOPT_COOKIELIST: + argptr = va_arg(param, char *); + + if(!argptr) + break; + + if(strcasecompare(argptr, "ALL")) { + /* clear all cookies */ + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_clearall(data->cookies); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + else if(strcasecompare(argptr, "SESS")) { + /* clear session cookies */ + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_clearsess(data->cookies); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + else if(strcasecompare(argptr, "FLUSH")) { + /* flush cookies to file, takes care of the locking */ + Curl_flush_cookies(data, FALSE); + } + else if(strcasecompare(argptr, "RELOAD")) { + /* reload cookies from file */ + Curl_cookie_loadfiles(data); + break; + } + else { + if(!data->cookies) + /* if cookie engine was not running, activate it */ + data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); + + /* general protection against mistakes and abuse */ + if(strlen(argptr) > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + argptr = strdup(argptr); + if(!argptr || !data->cookies) { + result = CURLE_OUT_OF_MEMORY; + free(argptr); + } + else { + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + + if(checkprefix("Set-Cookie:", argptr)) + /* HTTP Header format line */ + Curl_cookie_add(data, data->cookies, TRUE, FALSE, argptr + 11, NULL, + NULL, TRUE); + + else + /* Netscape format line */ + Curl_cookie_add(data, data->cookies, FALSE, FALSE, argptr, NULL, + NULL, TRUE); + + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + free(argptr); + } + } + + break; +#endif /* !CURL_DISABLE_COOKIES */ + + case CURLOPT_HTTPGET: + /* + * Set to force us do HTTP GET + */ + if(va_arg(param, long)) { + data->set.method = HTTPREQ_GET; + data->set.opt_no_body = FALSE; /* this is implied */ + } + break; + + case CURLOPT_HTTP_VERSION: + /* + * This sets a requested HTTP version to be used. The value is one of + * the listed enums in curl/curl.h. + */ + arg = va_arg(param, long); + switch(arg) { + case CURL_HTTP_VERSION_NONE: +#ifdef USE_HTTP2 + /* TODO: this seems an undesirable quirk to force a behaviour on + * lower implementations that they should recognize independently? */ + arg = CURL_HTTP_VERSION_2TLS; +#endif + /* accepted */ + break; + case CURL_HTTP_VERSION_1_0: + case CURL_HTTP_VERSION_1_1: + /* accepted */ + break; +#ifdef USE_HTTP2 + case CURL_HTTP_VERSION_2_0: + case CURL_HTTP_VERSION_2TLS: + case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE: + /* accepted */ + break; +#endif +#ifdef USE_HTTP3 + case CURL_HTTP_VERSION_3: + case CURL_HTTP_VERSION_3ONLY: + /* accepted */ + break; +#endif + default: + /* not accepted */ + if(arg < CURL_HTTP_VERSION_NONE) + return CURLE_BAD_FUNCTION_ARGUMENT; + return CURLE_UNSUPPORTED_PROTOCOL; + } + data->set.httpwant = (unsigned char)arg; + break; + + case CURLOPT_EXPECT_100_TIMEOUT_MS: + /* + * Time to wait for a response to an HTTP request containing an + * Expect: 100-continue header before sending the data anyway. + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.expect_100_timeout = arg; + break; + + case CURLOPT_HTTP09_ALLOWED: + arg = (long)va_arg(param, unsigned long); + if(arg > 1L) + return CURLE_BAD_FUNCTION_ARGUMENT; +#ifdef USE_HYPER + /* Hyper does not support HTTP/0.9 */ + if(arg) + return CURLE_BAD_FUNCTION_ARGUMENT; +#else + data->set.http09_allowed = !!arg; +#endif + break; + + case CURLOPT_HTTP200ALIASES: + /* + * Set a list of aliases for HTTP 200 in response header + */ + data->set.http200aliases = va_arg(param, struct curl_slist *); + break; +#endif /* CURL_DISABLE_HTTP */ + +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_IMAP) +# if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MIME) + case CURLOPT_HTTPHEADER: + /* + * Set a list with HTTP headers to use (or replace internals with) + */ + data->set.headers = va_arg(param, struct curl_slist *); + break; +# endif + +# ifndef CURL_DISABLE_MIME + case CURLOPT_MIMEPOST: + /* + * Set to make us do MIME POST + */ + result = Curl_mime_set_subparts(&data->set.mimepost, + va_arg(param, curl_mime *), FALSE); + if(!result) { + data->set.method = HTTPREQ_POST_MIME; + data->set.opt_no_body = FALSE; /* this is implied */ +#ifndef CURL_DISABLE_FORM_API + Curl_mime_cleanpart(data->state.formp); + Curl_safefree(data->state.formp); + data->state.mimepost = NULL; +#endif + } + break; + + case CURLOPT_MIME_OPTIONS: + arg = va_arg(param, long); + data->set.mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE); + break; +# endif +#endif + + case CURLOPT_HTTPAUTH: + /* + * Set HTTP Authentication type BITMASK. + */ + { + int bitcheck; + bool authbits; + unsigned long auth = va_arg(param, unsigned long); + + if(auth == CURLAUTH_NONE) { + data->set.httpauth = auth; + break; + } + + /* the DIGEST_IE bit is only used to set a special marker, for all the + rest we need to handle it as normal DIGEST */ + data->state.authhost.iestyle = !!(auth & CURLAUTH_DIGEST_IE); + + if(auth & CURLAUTH_DIGEST_IE) { + auth |= CURLAUTH_DIGEST; /* set standard digest bit */ + auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ + } + + /* switch off bits we cannot support */ +#ifndef USE_NTLM + auth &= ~CURLAUTH_NTLM; /* no NTLM support */ +#endif +#ifndef USE_SPNEGO + auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without + GSS-API or SSPI */ +#endif + + /* check if any auth bit lower than CURLAUTH_ONLY is still set */ + bitcheck = 0; + authbits = FALSE; + while(bitcheck < 31) { + if(auth & (1UL << bitcheck++)) { + authbits = TRUE; + break; + } + } + if(!authbits) + return CURLE_NOT_BUILT_IN; /* no supported types left! */ + + data->set.httpauth = auth; + } + break; + + case CURLOPT_CUSTOMREQUEST: + /* + * Set a custom string to use as request + */ + result = Curl_setstropt(&data->set.str[STRING_CUSTOMREQUEST], + va_arg(param, char *)); + + /* we do not set + data->set.method = HTTPREQ_CUSTOM; + here, we continue as if we were using the already set type + and this just changes the actual request keyword */ + break; + +#ifndef CURL_DISABLE_PROXY + case CURLOPT_HTTPPROXYTUNNEL: + /* + * Tunnel operations through the proxy instead of normal proxy use + */ + data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)); + break; + + case CURLOPT_PROXYPORT: + /* + * Explicitly set HTTP proxy port number. + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.proxyport = (unsigned short)arg; + break; + + case CURLOPT_PROXYAUTH: + /* + * Set HTTP Authentication type BITMASK. + */ + { + int bitcheck; + bool authbits; + unsigned long auth = va_arg(param, unsigned long); + + if(auth == CURLAUTH_NONE) { + data->set.proxyauth = auth; + break; + } + + /* the DIGEST_IE bit is only used to set a special marker, for all the + rest we need to handle it as normal DIGEST */ + data->state.authproxy.iestyle = !!(auth & CURLAUTH_DIGEST_IE); + + if(auth & CURLAUTH_DIGEST_IE) { + auth |= CURLAUTH_DIGEST; /* set standard digest bit */ + auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ + } + /* switch off bits we cannot support */ +#ifndef USE_NTLM + auth &= ~CURLAUTH_NTLM; /* no NTLM support */ +#endif +#ifndef USE_SPNEGO + auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without + GSS-API or SSPI */ +#endif + + /* check if any auth bit lower than CURLAUTH_ONLY is still set */ + bitcheck = 0; + authbits = FALSE; + while(bitcheck < 31) { + if(auth & (1UL << bitcheck++)) { + authbits = TRUE; + break; + } + } + if(!authbits) + return CURLE_NOT_BUILT_IN; /* no supported types left! */ + + data->set.proxyauth = auth; + } + break; + + case CURLOPT_PROXY: + /* + * Set proxy server:port to use as proxy. + * + * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL) + * we explicitly say that we do not want to use a proxy + * (even though there might be environment variables saying so). + * + * Setting it to NULL, means no proxy but allows the environment variables + * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL). + */ + result = Curl_setstropt(&data->set.str[STRING_PROXY], + va_arg(param, char *)); + break; + + case CURLOPT_PRE_PROXY: + /* + * Set proxy server:port to use as SOCKS proxy. + * + * If the proxy is set to "" or NULL we explicitly say that we do not want + * to use the socks proxy. + */ + result = Curl_setstropt(&data->set.str[STRING_PRE_PROXY], + va_arg(param, char *)); + break; + + case CURLOPT_PROXYTYPE: + /* + * Set proxy type. + */ + arg = va_arg(param, long); + if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.proxytype = (unsigned char)(curl_proxytype)arg; + break; + + case CURLOPT_PROXY_TRANSFER_MODE: + /* + * set transfer mode (;type=) when doing FTP via an HTTP proxy + */ + switch(va_arg(param, long)) { + case 0: + data->set.proxy_transfer_mode = FALSE; + break; + case 1: + data->set.proxy_transfer_mode = TRUE; + break; + default: + /* reserve other values for future use */ + result = CURLE_BAD_FUNCTION_ARGUMENT; + break; + } + break; + + case CURLOPT_SOCKS5_AUTH: + data->set.socks5auth = (unsigned char)va_arg(param, unsigned long); + if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) + result = CURLE_NOT_BUILT_IN; + break; +#endif /* CURL_DISABLE_PROXY */ + +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + case CURLOPT_SOCKS5_GSSAPI_NEC: + /* + * Set flag for NEC SOCK5 support + */ + data->set.socks5_gssapi_nec = (0 != va_arg(param, long)); + break; +#endif +#ifndef CURL_DISABLE_PROXY + case CURLOPT_SOCKS5_GSSAPI_SERVICE: + case CURLOPT_PROXY_SERVICE_NAME: + /* + * Set proxy authentication service name for Kerberos 5 and SPNEGO + */ + result = Curl_setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME], + va_arg(param, char *)); + break; +#endif + case CURLOPT_SERVICE_NAME: + /* + * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO + */ + result = Curl_setstropt(&data->set.str[STRING_SERVICE_NAME], + va_arg(param, char *)); + break; + + case CURLOPT_HEADERDATA: + /* + * Custom pointer to pass the header write callback function + */ + data->set.writeheader = (void *)va_arg(param, void *); + break; + case CURLOPT_ERRORBUFFER: + /* + * Error buffer provided by the caller to get the human readable + * error string in. + */ + data->set.errorbuffer = va_arg(param, char *); + break; + case CURLOPT_WRITEDATA: + /* + * FILE pointer to write to. Or possibly + * used as argument to the write callback. + */ + data->set.out = va_arg(param, void *); + break; + +#ifdef CURL_LIST_ONLY_PROTOCOL + case CURLOPT_DIRLISTONLY: + /* + * An option that changes the command to one that asks for a list only, no + * file info details. Used for FTP, POP3 and SFTP. + */ + data->set.list_only = (0 != va_arg(param, long)); + break; +#endif + case CURLOPT_APPEND: + /* + * We want to upload and append to an existing file. Used for FTP and + * SFTP. + */ + data->set.remote_append = (0 != va_arg(param, long)); + break; + +#ifndef CURL_DISABLE_FTP + case CURLOPT_FTP_FILEMETHOD: + /* + * How do access files over FTP. + */ + arg = va_arg(param, long); + if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.ftp_filemethod = (unsigned char)arg; + break; + case CURLOPT_FTPPORT: + /* + * Use FTP PORT, this also specifies which IP address to use + */ + result = Curl_setstropt(&data->set.str[STRING_FTPPORT], + va_arg(param, char *)); + data->set.ftp_use_port = !!(data->set.str[STRING_FTPPORT]); + break; + + case CURLOPT_FTP_USE_EPRT: + data->set.ftp_use_eprt = (0 != va_arg(param, long)); + break; + + case CURLOPT_FTP_USE_EPSV: + data->set.ftp_use_epsv = (0 != va_arg(param, long)); + break; + + case CURLOPT_FTP_USE_PRET: + data->set.ftp_use_pret = (0 != va_arg(param, long)); + break; + + case CURLOPT_FTP_SSL_CCC: + arg = va_arg(param, long); + if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.ftp_ccc = (unsigned char)arg; + break; + + case CURLOPT_FTP_SKIP_PASV_IP: + /* + * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the + * bypass of the IP address in PASV responses. + */ + data->set.ftp_skip_ip = (0 != va_arg(param, long)); + break; + + case CURLOPT_FTP_ACCOUNT: + result = Curl_setstropt(&data->set.str[STRING_FTP_ACCOUNT], + va_arg(param, char *)); + break; + + case CURLOPT_FTP_ALTERNATIVE_TO_USER: + result = Curl_setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER], + va_arg(param, char *)); + break; + + case CURLOPT_FTPSSLAUTH: + /* + * Set a specific auth for FTP-SSL transfers. + */ + arg = va_arg(param, long); + if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.ftpsslauth = (unsigned char)(curl_ftpauth)arg; + break; +#ifdef HAVE_GSSAPI + case CURLOPT_KRBLEVEL: + /* + * A string that defines the kerberos security level. + */ + result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL], + va_arg(param, char *)); + data->set.krb = !!(data->set.str[STRING_KRB_LEVEL]); + break; +#endif +#endif +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) + case CURLOPT_FTP_CREATE_MISSING_DIRS: + /* + * An FTP/SFTP option that modifies an upload to create missing + * directories on the server. + */ + arg = va_arg(param, long); + /* reserve other values for future use */ + if((arg < CURLFTP_CREATE_DIR_NONE) || + (arg > CURLFTP_CREATE_DIR_RETRY)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + data->set.ftp_create_missing_dirs = (unsigned char)arg; + break; + + case CURLOPT_POSTQUOTE: + /* + * List of RAW FTP commands to use after a transfer + */ + data->set.postquote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_PREQUOTE: + /* + * List of RAW FTP commands to use prior to RETR (Wesley Laxton) + */ + data->set.prequote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_QUOTE: + /* + * List of RAW FTP commands to use before a transfer + */ + data->set.quote = va_arg(param, struct curl_slist *); + break; +#endif + case CURLOPT_READDATA: + /* + * FILE pointer to read the file to be uploaded from. Or possibly + * used as argument to the read callback. + */ + data->set.in_set = va_arg(param, void *); + break; + case CURLOPT_INFILESIZE: + /* + * If known, this should inform curl about the file size of the + * to-be-uploaded file. + */ + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.filesize = arg; + break; + case CURLOPT_INFILESIZE_LARGE: + /* + * If known, this should inform curl about the file size of the + * to-be-uploaded file. + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.filesize = bigsize; + break; + case CURLOPT_LOW_SPEED_LIMIT: + /* + * The low speed limit that if transfers are below this for + * CURLOPT_LOW_SPEED_TIME, the transfer is aborted. + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.low_speed_limit = arg; + break; + case CURLOPT_MAX_SEND_SPEED_LARGE: + /* + * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE + * bytes per second the transfer is throttled.. + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_send_speed = bigsize; + break; + case CURLOPT_MAX_RECV_SPEED_LARGE: + /* + * When receiving data faster than CURLOPT_MAX_RECV_SPEED_LARGE bytes per + * second the transfer is throttled.. + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_recv_speed = bigsize; + break; + case CURLOPT_LOW_SPEED_TIME: + /* + * The low speed time that if transfers are below the set + * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted. + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.low_speed_time = arg; + break; + case CURLOPT_CURLU: + /* + * pass CURLU to set URL + */ + data->set.uh = va_arg(param, CURLU *); + break; + case CURLOPT_URL: + /* + * The URL to fetch. + */ + if(data->state.url_alloc) { + /* the already set URL is allocated, free it first! */ + Curl_safefree(data->state.url); + data->state.url_alloc = FALSE; + } + result = Curl_setstropt(&data->set.str[STRING_SET_URL], + va_arg(param, char *)); + data->state.url = data->set.str[STRING_SET_URL]; + break; + case CURLOPT_PORT: + /* + * The port number to use when getting the URL. 0 disables it. + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.use_port = (unsigned short)arg; + break; + case CURLOPT_TIMEOUT: + /* + * The maximum time you allow curl to use for a single transfer + * operation. + */ + arg = va_arg(param, long); + if((arg >= 0) && (arg <= (INT_MAX/1000))) + data->set.timeout = (unsigned int)arg * 1000; + else + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + + case CURLOPT_TIMEOUT_MS: + uarg = va_arg(param, unsigned long); + if(uarg > UINT_MAX) + uarg = UINT_MAX; + data->set.timeout = (unsigned int)uarg; + break; + + case CURLOPT_CONNECTTIMEOUT: + /* + * The maximum time you allow curl to use to connect. + */ + arg = va_arg(param, long); + if((arg >= 0) && (arg <= (INT_MAX/1000))) + data->set.connecttimeout = (unsigned int)arg * 1000; + else + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + + case CURLOPT_CONNECTTIMEOUT_MS: + uarg = va_arg(param, unsigned long); + if(uarg > UINT_MAX) + uarg = UINT_MAX; + data->set.connecttimeout = (unsigned int)uarg; + break; + +#ifndef CURL_DISABLE_FTP + case CURLOPT_ACCEPTTIMEOUT_MS: + /* + * The maximum time for curl to wait for FTP server connect + */ + uarg = va_arg(param, unsigned long); + if(uarg > UINT_MAX) + uarg = UINT_MAX; + data->set.accepttimeout = (unsigned int)uarg; + break; +#endif + + case CURLOPT_USERPWD: + /* + * user:password to use in the operation + */ + result = setstropt_userpwd(va_arg(param, char *), + &data->set.str[STRING_USERNAME], + &data->set.str[STRING_PASSWORD]); + break; + + case CURLOPT_USERNAME: + /* + * authentication username to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_USERNAME], + va_arg(param, char *)); + break; + case CURLOPT_PASSWORD: + /* + * authentication password to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_PASSWORD], + va_arg(param, char *)); + break; + + case CURLOPT_LOGIN_OPTIONS: + /* + * authentication options to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_OPTIONS], + va_arg(param, char *)); + break; + + case CURLOPT_XOAUTH2_BEARER: + /* + * OAuth 2.0 bearer token to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_BEARER], + va_arg(param, char *)); + break; + + case CURLOPT_RESOLVE: + /* + * List of HOST:PORT:[addresses] strings to populate the DNS cache with + * Entries added this way will remain in the cache until explicitly + * removed or the handle is cleaned up. + * + * Prefix the HOST with plus sign (+) to have the entry expire just like + * automatically added entries. + * + * Prefix the HOST with dash (-) to _remove_ the entry from the cache. + * + * This API can remove any entry from the DNS cache, but only entries + * that are not actually in use right now will be pruned immediately. + */ + data->set.resolve = va_arg(param, struct curl_slist *); + data->state.resolve = data->set.resolve; + break; + case CURLOPT_PROGRESSFUNCTION: + /* + * Progress callback function + */ + data->set.fprogress = va_arg(param, curl_progress_callback); + if(data->set.fprogress) + data->progress.callback = TRUE; /* no longer internal */ + else + data->progress.callback = FALSE; /* NULL enforces internal */ + break; + + case CURLOPT_XFERINFOFUNCTION: + /* + * Transfer info callback function + */ + data->set.fxferinfo = va_arg(param, curl_xferinfo_callback); + if(data->set.fxferinfo) + data->progress.callback = TRUE; /* no longer internal */ + else + data->progress.callback = FALSE; /* NULL enforces internal */ + + break; + + case CURLOPT_PROGRESSDATA: + /* + * Custom client data to pass to the progress callback + */ + data->set.progress_client = va_arg(param, void *); + break; + +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXYUSERPWD: { + /* + * user:password needed to use the proxy + */ + char *u = NULL; + char *p = NULL; + result = setstropt_userpwd(va_arg(param, char *), &u, &p); + + /* URL decode the components */ + if(!result && u) + result = Curl_urldecode(u, 0, &data->set.str[STRING_PROXYUSERNAME], NULL, + REJECT_ZERO); + if(!result && p) + result = Curl_urldecode(p, 0, &data->set.str[STRING_PROXYPASSWORD], NULL, + REJECT_ZERO); + free(u); + free(p); + } + break; + case CURLOPT_PROXYUSERNAME: + /* + * authentication username to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_PROXYUSERNAME], + va_arg(param, char *)); + break; + case CURLOPT_PROXYPASSWORD: + /* + * authentication password to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_PROXYPASSWORD], + va_arg(param, char *)); + break; + case CURLOPT_NOPROXY: + /* + * proxy exception list + */ + result = Curl_setstropt(&data->set.str[STRING_NOPROXY], + va_arg(param, char *)); + break; +#endif + + case CURLOPT_RANGE: + /* + * What range of the file you want to transfer + */ + result = Curl_setstropt(&data->set.str[STRING_SET_RANGE], + va_arg(param, char *)); + break; + case CURLOPT_RESUME_FROM: + /* + * Resume transfer at the given file position + */ + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.set_resume_from = arg; + break; + case CURLOPT_RESUME_FROM_LARGE: + /* + * Resume transfer at the given file position + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.set_resume_from = bigsize; + break; + case CURLOPT_DEBUGFUNCTION: + /* + * stderr write callback. + */ + data->set.fdebug = va_arg(param, curl_debug_callback); + /* + * if the callback provided is NULL, it will use the default callback + */ + break; + case CURLOPT_DEBUGDATA: + /* + * Set to a void * that should receive all error writes. This + * defaults to CURLOPT_STDERR for normal operations. + */ + data->set.debugdata = va_arg(param, void *); + break; + case CURLOPT_STDERR: + /* + * Set to a FILE * that should receive all error writes. This + * defaults to stderr for normal operations. + */ + data->set.err = va_arg(param, FILE *); + if(!data->set.err) + data->set.err = stderr; + break; + case CURLOPT_HEADERFUNCTION: + /* + * Set header write callback + */ + data->set.fwrite_header = va_arg(param, curl_write_callback); + break; + case CURLOPT_WRITEFUNCTION: + /* + * Set data write callback + */ + data->set.fwrite_func = va_arg(param, curl_write_callback); + if(!data->set.fwrite_func) + /* When set to NULL, reset to our internal default function */ + data->set.fwrite_func = (curl_write_callback)fwrite; + break; + case CURLOPT_READFUNCTION: + /* + * Read data callback + */ + data->set.fread_func_set = va_arg(param, curl_read_callback); + if(!data->set.fread_func_set) { + data->set.is_fread_set = 0; + /* When set to NULL, reset to our internal default function */ + data->set.fread_func_set = (curl_read_callback)fread; + } + else + data->set.is_fread_set = 1; + break; + case CURLOPT_SEEKFUNCTION: + /* + * Seek callback. Might be NULL. + */ + data->set.seek_func = va_arg(param, curl_seek_callback); + break; + case CURLOPT_SEEKDATA: + /* + * Seek control callback. Might be NULL. + */ + data->set.seek_client = va_arg(param, void *); + break; + case CURLOPT_IOCTLFUNCTION: + /* + * I/O control callback. Might be NULL. + */ + data->set.ioctl_func = va_arg(param, curl_ioctl_callback); + break; + case CURLOPT_IOCTLDATA: + /* + * I/O control data pointer. Might be NULL. + */ + data->set.ioctl_client = va_arg(param, void *); + break; + case CURLOPT_SSLCERT: + /* + * String that holds filename of the SSL certificate to use + */ + result = Curl_setstropt(&data->set.str[STRING_CERT], + va_arg(param, char *)); + break; + case CURLOPT_SSLCERT_BLOB: + /* + * Blob that holds file content of the SSL certificate to use + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_CERT], + va_arg(param, struct curl_blob *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLCERT: + /* + * String that holds filename of the SSL certificate to use for proxy + */ + result = Curl_setstropt(&data->set.str[STRING_CERT_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_SSLCERT_BLOB: + /* + * Blob that holds file content of the SSL certificate to use for proxy + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_CERT_PROXY], + va_arg(param, struct curl_blob *)); + break; +#endif + case CURLOPT_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use + */ + result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE], + va_arg(param, char *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use for proxy + */ + result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE_PROXY], + va_arg(param, char *)); + break; +#endif + case CURLOPT_SSLKEY: + /* + * String that holds filename of the SSL key to use + */ + result = Curl_setstropt(&data->set.str[STRING_KEY], + va_arg(param, char *)); + break; + case CURLOPT_SSLKEY_BLOB: + /* + * Blob that holds file content of the SSL key to use + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_KEY], + va_arg(param, struct curl_blob *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLKEY: + /* + * String that holds filename of the SSL key to use for proxy + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_SSLKEY_BLOB: + /* + * Blob that holds file content of the SSL key to use for proxy + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_KEY_PROXY], + va_arg(param, struct curl_blob *)); + break; +#endif + case CURLOPT_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE], + va_arg(param, char *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use for proxy + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE_PROXY], + va_arg(param, char *)); + break; +#endif + case CURLOPT_KEYPASSWD: + /* + * String that holds the SSL or SSH private key password. + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD], + va_arg(param, char *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_KEYPASSWD: + /* + * String that holds the SSL private key password for proxy. + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_PROXY], + va_arg(param, char *)); + break; +#endif + case CURLOPT_SSLENGINE: + /* + * String that holds the SSL crypto engine. + */ + argptr = va_arg(param, char *); + if(argptr && argptr[0]) { + result = Curl_setstropt(&data->set.str[STRING_SSL_ENGINE], argptr); + if(!result) { + result = Curl_ssl_set_engine(data, argptr); + } + } + break; + + case CURLOPT_SSLENGINE_DEFAULT: + /* + * flag to set engine as default. + */ + Curl_safefree(data->set.str[STRING_SSL_ENGINE]); + result = Curl_ssl_set_engine_default(data); + break; + case CURLOPT_CRLF: + /* + * Kludgy option to enable CRLF conversions. Subject for removal. + */ + data->set.crlf = (0 != va_arg(param, long)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_HAPROXYPROTOCOL: + /* + * Set to send the HAProxy Proxy Protocol header + */ + data->set.haproxyprotocol = (0 != va_arg(param, long)); + break; + case CURLOPT_HAPROXY_CLIENT_IP: + /* + * Set the client IP to send through HAProxy PROXY protocol + */ + result = Curl_setstropt(&data->set.str[STRING_HAPROXY_CLIENT_IP], + va_arg(param, char *)); + /* We enable implicitly the HAProxy protocol if we use this flag. */ + data->set.haproxyprotocol = TRUE; + break; +#endif + case CURLOPT_INTERFACE: + /* + * Set what interface or address/hostname to bind the socket to when + * performing an operation and thus what from-IP your connection will use. + */ + result = setstropt_interface(va_arg(param, char *), + &data->set.str[STRING_DEVICE], + &data->set.str[STRING_INTERFACE], + &data->set.str[STRING_BINDHOST]); + break; +#ifndef CURL_DISABLE_BINDLOCAL + case CURLOPT_LOCALPORT: + /* + * Set what local port to bind the socket to when performing an operation. + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.localport = curlx_sltous(arg); + break; + case CURLOPT_LOCALPORTRANGE: + /* + * Set number of local ports to try, starting with CURLOPT_LOCALPORT. + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.localportrange = curlx_sltous(arg); + break; +#endif + case CURLOPT_GSSAPI_DELEGATION: + /* + * GSS-API credential delegation bitmask + */ + uarg = va_arg(param, unsigned long); + data->set.gssapi_delegation = (unsigned char)uarg& + (CURLGSSAPI_DELEGATION_POLICY_FLAG|CURLGSSAPI_DELEGATION_FLAG); + break; + case CURLOPT_SSL_VERIFYPEER: + /* + * Enable peer SSL verifying. + */ + data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)); + + /* Update the current connection ssl_config. */ + Curl_ssl_conn_config_update(data, FALSE); + break; +#ifndef CURL_DISABLE_DOH + case CURLOPT_DOH_SSL_VERIFYPEER: + /* + * Enable peer SSL verifying for DoH. + */ + data->set.doh_verifypeer = (0 != va_arg(param, long)); + break; +#endif +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSL_VERIFYPEER: + /* + * Enable peer SSL verifying for proxy. + */ + data->set.proxy_ssl.primary.verifypeer = + (0 != va_arg(param, long))?TRUE:FALSE; + + /* Update the current connection proxy_ssl_config. */ + Curl_ssl_conn_config_update(data, TRUE); + break; +#endif + case CURLOPT_SSL_VERIFYHOST: + /* + * Enable verification of the hostname in the peer certificate + */ + arg = va_arg(param, long); + + /* Obviously people are not reading documentation and too many thought + this argument took a boolean when it was not and misused it. + Treat 1 and 2 the same */ + data->set.ssl.primary.verifyhost = !!(arg & 3); + + /* Update the current connection ssl_config. */ + Curl_ssl_conn_config_update(data, FALSE); + break; +#ifndef CURL_DISABLE_DOH + case CURLOPT_DOH_SSL_VERIFYHOST: + /* + * Enable verification of the hostname in the peer certificate for DoH + */ + arg = va_arg(param, long); + + /* Treat both 1 and 2 as TRUE */ + data->set.doh_verifyhost = !!(arg & 3); + break; +#endif +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSL_VERIFYHOST: + /* + * Enable verification of the hostname in the peer certificate for proxy + */ + arg = va_arg(param, long); + + /* Treat both 1 and 2 as TRUE */ + data->set.proxy_ssl.primary.verifyhost = (bool)((arg & 3)?TRUE:FALSE); + /* Update the current connection proxy_ssl_config. */ + Curl_ssl_conn_config_update(data, TRUE); + break; +#endif + case CURLOPT_SSL_VERIFYSTATUS: + /* + * Enable certificate status verifying. + */ + if(!Curl_ssl_cert_status_request()) { + result = CURLE_NOT_BUILT_IN; + break; + } + + data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)); + + /* Update the current connection ssl_config. */ + Curl_ssl_conn_config_update(data, FALSE); + break; +#ifndef CURL_DISABLE_DOH + case CURLOPT_DOH_SSL_VERIFYSTATUS: + /* + * Enable certificate status verifying for DoH. + */ + if(!Curl_ssl_cert_status_request()) { + result = CURLE_NOT_BUILT_IN; + break; + } + + data->set.doh_verifystatus = (0 != va_arg(param, long)); + break; +#endif + case CURLOPT_SSL_CTX_FUNCTION: + /* + * Set a SSL_CTX callback + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) + data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_SSL_CTX_DATA: + /* + * Set a SSL_CTX callback parameter pointer + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) + data->set.ssl.fsslctxp = va_arg(param, void *); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_SSL_FALSESTART: + /* + * Enable TLS false start. + */ + if(!Curl_ssl_false_start(data)) { + result = CURLE_NOT_BUILT_IN; + break; + } + + data->set.ssl.falsestart = (0 != va_arg(param, long)); + break; + case CURLOPT_CERTINFO: +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CERTINFO)) + data->set.ssl.certinfo = (0 != va_arg(param, long)); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify filename of the public key in DER format. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY], + va_arg(param, char *)); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify filename of the public key in DER format. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY], + va_arg(param, char *)); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; +#endif + case CURLOPT_CAINFO: + /* + * Set CA info for SSL connection. Specify filename of the CA certificate + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE], + va_arg(param, char *)); + break; + case CURLOPT_CAINFO_BLOB: + /* + * Blob that holds CA info for SSL connection. + * Specify entire PEM of the CA certificate + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { + result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO], + va_arg(param, struct curl_blob *)); + break; + } + else +#endif + return CURLE_NOT_BUILT_IN; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_CAINFO: + /* + * Set CA info SSL connection for proxy. Specify filename of the + * CA certificate + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_CAINFO_BLOB: + /* + * Blob that holds CA info for SSL connection proxy. + * Specify entire PEM of the CA certificate + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { + result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY], + va_arg(param, struct curl_blob *)); + break; + } + else +#endif + return CURLE_NOT_BUILT_IN; +#endif + case CURLOPT_CAPATH: + /* + * Set CA path info for SSL connection. Specify directory name of the CA + * certificates which have been prepared using openssl c_rehash utility. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) + /* This does not work on windows. */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH], + va_arg(param, char *)); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_CAPATH: + /* + * Set CA path info for SSL connection proxy. Specify directory name of the + * CA certificates which have been prepared using openssl c_rehash utility. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) + /* This does not work on windows. */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY], + va_arg(param, char *)); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; +#endif + case CURLOPT_CRLFILE: + /* + * Set CRL file info for SSL connection. Specify filename of the CRL + * to check certificates revocation + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE], + va_arg(param, char *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_CRLFILE: + /* + * Set CRL file info for SSL connection for proxy. Specify filename of the + * CRL to check certificates revocation + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY], + va_arg(param, char *)); + break; +#endif + case CURLOPT_ISSUERCERT: + /* + * Set Issuer certificate file + * to check certificates issuer + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT], + va_arg(param, char *)); + break; + case CURLOPT_ISSUERCERT_BLOB: + /* + * Blob that holds Issuer certificate to check certificates issuer + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT], + va_arg(param, struct curl_blob *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_ISSUERCERT: + /* + * Set Issuer certificate file + * to check certificates issuer + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_ISSUERCERT_BLOB: + /* + * Blob that holds Issuer certificate to check certificates issuer + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY], + va_arg(param, struct curl_blob *)); + break; +#endif +#ifndef CURL_DISABLE_TELNET + case CURLOPT_TELNETOPTIONS: + /* + * Set a linked list of telnet options + */ + data->set.telnet_options = va_arg(param, struct curl_slist *); + break; +#endif + case CURLOPT_BUFFERSIZE: + /* + * The application kindly asks for a differently sized receive buffer. + * If it seems reasonable, we will use it. + */ + arg = va_arg(param, long); + + if(arg > READBUFFER_MAX) + arg = READBUFFER_MAX; + else if(arg < 1) + arg = READBUFFER_SIZE; + else if(arg < READBUFFER_MIN) + arg = READBUFFER_MIN; + + data->set.buffer_size = (unsigned int)arg; + break; + + case CURLOPT_UPLOAD_BUFFERSIZE: + /* + * The application kindly asks for a differently sized upload buffer. + * Cap it to sensible. + */ + arg = va_arg(param, long); + + if(arg > UPLOADBUFFER_MAX) + arg = UPLOADBUFFER_MAX; + else if(arg < UPLOADBUFFER_MIN) + arg = UPLOADBUFFER_MIN; + + data->set.upload_buffer_size = (unsigned int)arg; + break; + + case CURLOPT_NOSIGNAL: + /* + * The application asks not to set any signal() or alarm() handlers, + * even when using a timeout. + */ + data->set.no_signal = (0 != va_arg(param, long)); + break; + + case CURLOPT_SHARE: + { + struct Curl_share *set; + set = va_arg(param, struct Curl_share *); + + /* disconnect from old share, if any */ + if(data->share) { + Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + + if(data->dns.hostcachetype == HCACHE_SHARED) { + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; + } + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(data->share->cookies == data->cookies) + data->cookies = NULL; +#endif + +#ifndef CURL_DISABLE_HSTS + if(data->share->hsts == data->hsts) + data->hsts = NULL; +#endif +#ifdef USE_SSL + if(data->share->sslsession == data->state.session) + data->state.session = NULL; +#endif +#ifdef USE_LIBPSL + if(data->psl == &data->share->psl) + data->psl = data->multi? &data->multi->psl: NULL; +#endif + + data->share->dirty--; + + Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); + data->share = NULL; + } + + if(GOOD_SHARE_HANDLE(set)) + /* use new share if it set */ + data->share = set; + if(data->share) { + + Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + + data->share->dirty++; + + if(data->share->specifier & (1<< CURL_LOCK_DATA_DNS)) { + /* use shared host cache */ + data->dns.hostcache = &data->share->hostcache; + data->dns.hostcachetype = HCACHE_SHARED; + } +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(data->share->cookies) { + /* use shared cookie list, first free own one if any */ + Curl_cookie_cleanup(data->cookies); + /* enable cookies since we now use a share that uses cookies! */ + data->cookies = data->share->cookies; + } +#endif /* CURL_DISABLE_HTTP */ +#ifndef CURL_DISABLE_HSTS + if(data->share->hsts) { + /* first free the private one if any */ + Curl_hsts_cleanup(&data->hsts); + data->hsts = data->share->hsts; + } +#endif +#ifdef USE_SSL + if(data->share->sslsession) { + data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; + data->state.session = data->share->sslsession; + } +#endif +#ifdef USE_LIBPSL + if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) + data->psl = &data->share->psl; +#endif + + Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); + } + /* check for host cache not needed, + * it will be done by curl_easy_perform */ + } + break; + + case CURLOPT_PRIVATE: + /* + * Set private data pointer. + */ + data->set.private_data = va_arg(param, void *); + break; + + case CURLOPT_MAXFILESIZE: + /* + * Set the maximum size of a file to download. + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_filesize = arg; + break; + +#ifdef USE_SSL + case CURLOPT_USE_SSL: + /* + * Make transfers attempt to use SSL/TLS. + */ + arg = va_arg(param, long); + if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.use_ssl = (unsigned char)arg; + break; + + case CURLOPT_SSL_OPTIONS: + arg = va_arg(param, long); + data->set.ssl.primary.ssl_options = (unsigned char)(arg & 0xff); + data->set.ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); + data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); + data->set.ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); + data->set.ssl.revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); + data->set.ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); + data->set.ssl.auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); + /* If a setting is added here it should also be added in dohprobe() + which sets its own CURLOPT_SSL_OPTIONS based on these settings. */ + break; + +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSL_OPTIONS: + arg = va_arg(param, long); + data->set.proxy_ssl.primary.ssl_options = (unsigned char)(arg & 0xff); + data->set.proxy_ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); + data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); + data->set.proxy_ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); + data->set.proxy_ssl.revoke_best_effort = + !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); + data->set.proxy_ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); + data->set.proxy_ssl.auto_client_cert = + !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); + break; +#endif + + case CURLOPT_SSL_EC_CURVES: + /* + * Set accepted curves in SSL connection setup. + * Specify colon-delimited list of curve algorithm names. + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES], + va_arg(param, char *)); + break; +#endif + case CURLOPT_IPRESOLVE: + arg = va_arg(param, long); + if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.ipver = (unsigned char) arg; + break; + + case CURLOPT_MAXFILESIZE_LARGE: + /* + * Set the maximum size of a file to download. + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_filesize = bigsize; + break; + + case CURLOPT_TCP_NODELAY: + /* + * Enable or disable TCP_NODELAY, which will disable/enable the Nagle + * algorithm + */ + data->set.tcp_nodelay = (0 != va_arg(param, long)); + break; + + case CURLOPT_IGNORE_CONTENT_LENGTH: + data->set.ignorecl = (0 != va_arg(param, long)); + break; + + case CURLOPT_CONNECT_ONLY: + /* + * No data transfer. + * (1) - only do connection + * (2) - do first get request but get no content + */ + arg = va_arg(param, long); + if(arg > 2) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.connect_only = (unsigned char)arg; + break; + + case CURLOPT_SOCKOPTFUNCTION: + /* + * socket callback function: called after socket() but before connect() + */ + data->set.fsockopt = va_arg(param, curl_sockopt_callback); + break; + + case CURLOPT_SOCKOPTDATA: + /* + * socket callback data pointer. Might be NULL. + */ + data->set.sockopt_client = va_arg(param, void *); + break; + + case CURLOPT_OPENSOCKETFUNCTION: + /* + * open/create socket callback function: called instead of socket(), + * before connect() + */ + data->set.fopensocket = va_arg(param, curl_opensocket_callback); + break; + + case CURLOPT_OPENSOCKETDATA: + /* + * socket callback data pointer. Might be NULL. + */ + data->set.opensocket_client = va_arg(param, void *); + break; + + case CURLOPT_CLOSESOCKETFUNCTION: + /* + * close socket callback function: called instead of close() + * when shutting down a connection + */ + data->set.fclosesocket = va_arg(param, curl_closesocket_callback); + break; + + case CURLOPT_RESOLVER_START_FUNCTION: + /* + * resolver start callback function: called before a new resolver request + * is started + */ + data->set.resolver_start = va_arg(param, curl_resolver_start_callback); + break; + + case CURLOPT_RESOLVER_START_DATA: + /* + * resolver start callback data pointer. Might be NULL. + */ + data->set.resolver_start_client = va_arg(param, void *); + break; + + case CURLOPT_CLOSESOCKETDATA: + /* + * socket callback data pointer. Might be NULL. + */ + data->set.closesocket_client = va_arg(param, void *); + break; + + case CURLOPT_SSL_SESSIONID_CACHE: + data->set.ssl.primary.cache_session = (0 != va_arg(param, long)); +#ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl.primary.cache_session = + data->set.ssl.primary.cache_session; +#endif + break; + +#ifdef USE_SSH + /* we only include SSH options if explicitly built to support SSH */ + case CURLOPT_SSH_AUTH_TYPES: + data->set.ssh_auth_types = (int)va_arg(param, long); + break; + + case CURLOPT_SSH_PUBLIC_KEYFILE: + /* + * Use this file instead of the $HOME/.ssh/id_dsa.pub file + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY], + va_arg(param, char *)); + break; + + case CURLOPT_SSH_PRIVATE_KEYFILE: + /* + * Use this file instead of the $HOME/.ssh/id_dsa file + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY], + va_arg(param, char *)); + break; + case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: + /* + * Option to allow for the MD5 of the host public key to be checked + * for validation purposes. + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], + va_arg(param, char *)); + break; + + case CURLOPT_SSH_KNOWNHOSTS: + /* + * Store the filename to read known hosts from. + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], + va_arg(param, char *)); + break; +#ifdef USE_LIBSSH2 + case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: + /* + * Option to allow for the SHA256 of the host public key to be checked + * for validation purposes. + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], + va_arg(param, char *)); + break; + + case CURLOPT_SSH_HOSTKEYFUNCTION: + /* the callback to check the hostkey without the knownhost file */ + data->set.ssh_hostkeyfunc = va_arg(param, curl_sshhostkeycallback); + break; + + case CURLOPT_SSH_HOSTKEYDATA: + /* + * Custom client data to pass to the SSH keyfunc callback + */ + data->set.ssh_hostkeyfunc_userp = va_arg(param, void *); + break; +#endif + + case CURLOPT_SSH_KEYFUNCTION: + /* setting to NULL is fine since the ssh.c functions themselves will + then revert to use the internal default */ + data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback); + break; + + case CURLOPT_SSH_KEYDATA: + /* + * Custom client data to pass to the SSH keyfunc callback + */ + data->set.ssh_keyfunc_userp = va_arg(param, void *); + break; + + case CURLOPT_SSH_COMPRESSION: + data->set.ssh_compression = (0 != va_arg(param, long))?TRUE:FALSE; + break; +#endif /* USE_SSH */ + + case CURLOPT_HTTP_TRANSFER_DECODING: + /* + * disable libcurl transfer encoding is used + */ +#ifndef USE_HYPER + data->set.http_te_skip = (0 == va_arg(param, long)); + break; +#else + return CURLE_NOT_BUILT_IN; /* hyper does not support */ +#endif + + case CURLOPT_HTTP_CONTENT_DECODING: + /* + * raw data passed to the application when content encoding is used + */ + data->set.http_ce_skip = (0 == va_arg(param, long)); + break; + +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) + case CURLOPT_NEW_FILE_PERMS: + /* + * Uses these permissions instead of 0644 + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 0777)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.new_file_perms = (unsigned int)arg; + break; +#endif +#ifdef USE_SSH + case CURLOPT_NEW_DIRECTORY_PERMS: + /* + * Uses these permissions instead of 0755 + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 0777)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.new_directory_perms = (unsigned int)arg; + break; +#endif + +#ifdef USE_IPV6 + case CURLOPT_ADDRESS_SCOPE: + /* + * Use this scope id when using IPv6 + * We always get longs when passed plain numericals so we should check + * that the value fits into an unsigned 32-bit integer. + */ + uarg = va_arg(param, unsigned long); +#if SIZEOF_LONG > 4 + if(uarg > UINT_MAX) + return CURLE_BAD_FUNCTION_ARGUMENT; +#endif + data->set.scope_id = (unsigned int)uarg; + break; +#endif + + case CURLOPT_PROTOCOLS: + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal + with. Defaults to CURLPROTO_ALL. */ + data->set.allowed_protocols = (curl_prot_t)va_arg(param, long); + break; + + case CURLOPT_REDIR_PROTOCOLS: + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs + to be set in both bitmasks to be allowed to get redirected to. */ + data->set.redir_protocols = (curl_prot_t)va_arg(param, long); + break; + + case CURLOPT_PROTOCOLS_STR: { + argptr = va_arg(param, char *); + result = protocol2num(argptr, &data->set.allowed_protocols); + if(result) + return result; + break; + } + + case CURLOPT_REDIR_PROTOCOLS_STR: { + argptr = va_arg(param, char *); + result = protocol2num(argptr, &data->set.redir_protocols); + if(result) + return result; + break; + } + + case CURLOPT_DEFAULT_PROTOCOL: + /* Set the protocol to use when the URL does not include any protocol */ + result = Curl_setstropt(&data->set.str[STRING_DEFAULT_PROTOCOL], + va_arg(param, char *)); + break; +#ifndef CURL_DISABLE_SMTP + case CURLOPT_MAIL_FROM: + /* Set the SMTP mail originator */ + result = Curl_setstropt(&data->set.str[STRING_MAIL_FROM], + va_arg(param, char *)); + break; + + case CURLOPT_MAIL_AUTH: + /* Set the SMTP auth originator */ + result = Curl_setstropt(&data->set.str[STRING_MAIL_AUTH], + va_arg(param, char *)); + break; + + case CURLOPT_MAIL_RCPT: + /* Set the list of mail recipients */ + data->set.mail_rcpt = va_arg(param, struct curl_slist *); + break; + case CURLOPT_MAIL_RCPT_ALLOWFAILS: + /* allow RCPT TO command to fail for some recipients */ + data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)); + break; +#endif + + case CURLOPT_SASL_AUTHZID: + /* Authorization identity (identity to act as) */ + result = Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID], + va_arg(param, char *)); + break; + + case CURLOPT_SASL_IR: + /* Enable/disable SASL initial response */ + data->set.sasl_ir = (0 != va_arg(param, long)); + break; +#ifndef CURL_DISABLE_RTSP + case CURLOPT_RTSP_REQUEST: + { + /* + * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) + * Would this be better if the RTSPREQ_* were just moved into here? + */ + long in_rtspreq = va_arg(param, long); + Curl_RtspReq rtspreq = RTSPREQ_NONE; + switch(in_rtspreq) { + case CURL_RTSPREQ_OPTIONS: + rtspreq = RTSPREQ_OPTIONS; + break; + + case CURL_RTSPREQ_DESCRIBE: + rtspreq = RTSPREQ_DESCRIBE; + break; + + case CURL_RTSPREQ_ANNOUNCE: + rtspreq = RTSPREQ_ANNOUNCE; + break; + + case CURL_RTSPREQ_SETUP: + rtspreq = RTSPREQ_SETUP; + break; + + case CURL_RTSPREQ_PLAY: + rtspreq = RTSPREQ_PLAY; + break; + + case CURL_RTSPREQ_PAUSE: + rtspreq = RTSPREQ_PAUSE; + break; + + case CURL_RTSPREQ_TEARDOWN: + rtspreq = RTSPREQ_TEARDOWN; + break; + + case CURL_RTSPREQ_GET_PARAMETER: + rtspreq = RTSPREQ_GET_PARAMETER; + break; + + case CURL_RTSPREQ_SET_PARAMETER: + rtspreq = RTSPREQ_SET_PARAMETER; + break; + + case CURL_RTSPREQ_RECORD: + rtspreq = RTSPREQ_RECORD; + break; + + case CURL_RTSPREQ_RECEIVE: + rtspreq = RTSPREQ_RECEIVE; + break; + default: + rtspreq = RTSPREQ_NONE; + } + + data->set.rtspreq = rtspreq; + break; + } + + + case CURLOPT_RTSP_SESSION_ID: + /* + * Set the RTSP Session ID manually. Useful if the application is + * resuming a previously established RTSP session + */ + result = Curl_setstropt(&data->set.str[STRING_RTSP_SESSION_ID], + va_arg(param, char *)); + break; + + case CURLOPT_RTSP_STREAM_URI: + /* + * Set the Stream URI for the RTSP request. Unless the request is + * for generic server options, the application will need to set this. + */ + result = Curl_setstropt(&data->set.str[STRING_RTSP_STREAM_URI], + va_arg(param, char *)); + break; + + case CURLOPT_RTSP_TRANSPORT: + /* + * The content of the Transport: header for the RTSP request + */ + result = Curl_setstropt(&data->set.str[STRING_RTSP_TRANSPORT], + va_arg(param, char *)); + break; + + case CURLOPT_RTSP_CLIENT_CSEQ: + /* + * Set the CSEQ number to issue for the next RTSP request. Useful if the + * application is resuming a previously broken connection. The CSEQ + * will increment from this new number henceforth. + */ + data->state.rtsp_next_client_CSeq = va_arg(param, long); + break; + + case CURLOPT_RTSP_SERVER_CSEQ: + /* Same as the above, but for server-initiated requests */ + data->state.rtsp_next_server_CSeq = va_arg(param, long); + break; + + case CURLOPT_INTERLEAVEDATA: + data->set.rtp_out = va_arg(param, void *); + break; + case CURLOPT_INTERLEAVEFUNCTION: + /* Set the user defined RTP write function */ + data->set.fwrite_rtp = va_arg(param, curl_write_callback); + break; +#endif +#ifndef CURL_DISABLE_FTP + case CURLOPT_WILDCARDMATCH: + data->set.wildcard_enabled = (0 != va_arg(param, long)); + break; + case CURLOPT_CHUNK_BGN_FUNCTION: + data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback); + break; + case CURLOPT_CHUNK_END_FUNCTION: + data->set.chunk_end = va_arg(param, curl_chunk_end_callback); + break; + case CURLOPT_FNMATCH_FUNCTION: + data->set.fnmatch = va_arg(param, curl_fnmatch_callback); + break; + case CURLOPT_CHUNK_DATA: + data->set.wildcardptr = va_arg(param, void *); + break; + case CURLOPT_FNMATCH_DATA: + data->set.fnmatch_data = va_arg(param, void *); + break; +#endif +#ifdef USE_TLS_SRP + case CURLOPT_TLSAUTH_USERNAME: + result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME], + va_arg(param, char *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_TLSAUTH_USERNAME: + result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY], + va_arg(param, char *)); + break; +#endif + case CURLOPT_TLSAUTH_PASSWORD: + result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD], + va_arg(param, char *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_TLSAUTH_PASSWORD: + result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], + va_arg(param, char *)); + break; +#endif + case CURLOPT_TLSAUTH_TYPE: + argptr = va_arg(param, char *); + if(argptr && !strcasecompare(argptr, "SRP")) + return CURLE_BAD_FUNCTION_ARGUMENT; + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_TLSAUTH_TYPE: + argptr = va_arg(param, char *); + if(argptr && !strcasecompare(argptr, "SRP")) + return CURLE_BAD_FUNCTION_ARGUMENT; + break; +#endif +#endif +#ifdef USE_ARES + case CURLOPT_DNS_SERVERS: + result = Curl_setstropt(&data->set.str[STRING_DNS_SERVERS], + va_arg(param, char *)); + if(result) + return result; + result = Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]); + break; + case CURLOPT_DNS_INTERFACE: + result = Curl_setstropt(&data->set.str[STRING_DNS_INTERFACE], + va_arg(param, char *)); + if(result) + return result; + result = Curl_set_dns_interface(data, data->set.str[STRING_DNS_INTERFACE]); + break; + case CURLOPT_DNS_LOCAL_IP4: + result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP4], + va_arg(param, char *)); + if(result) + return result; + result = Curl_set_dns_local_ip4(data, data->set.str[STRING_DNS_LOCAL_IP4]); + break; + case CURLOPT_DNS_LOCAL_IP6: + result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP6], + va_arg(param, char *)); + if(result) + return result; + result = Curl_set_dns_local_ip6(data, data->set.str[STRING_DNS_LOCAL_IP6]); + break; +#endif + case CURLOPT_TCP_KEEPALIVE: + data->set.tcp_keepalive = (0 != va_arg(param, long)); + break; + case CURLOPT_TCP_KEEPIDLE: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; + data->set.tcp_keepidle = (int)arg; + break; + case CURLOPT_TCP_KEEPINTVL: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; + data->set.tcp_keepintvl = (int)arg; + break; + case CURLOPT_TCP_KEEPCNT: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; + data->set.tcp_keepcnt = (int)arg; + break; + case CURLOPT_TCP_FASTOPEN: +#if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \ + defined(TCP_FASTOPEN_CONNECT) + data->set.tcp_fastopen = (0 != va_arg(param, long))?TRUE:FALSE; +#else + result = CURLE_NOT_BUILT_IN; +#endif + break; + case CURLOPT_SSL_ENABLE_NPN: + break; + case CURLOPT_SSL_ENABLE_ALPN: + data->set.ssl_enable_alpn = (0 != va_arg(param, long)); + break; +#ifdef USE_UNIX_SOCKETS + case CURLOPT_UNIX_SOCKET_PATH: + data->set.abstract_unix_socket = FALSE; + result = Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], + va_arg(param, char *)); + break; + case CURLOPT_ABSTRACT_UNIX_SOCKET: + data->set.abstract_unix_socket = TRUE; + result = Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], + va_arg(param, char *)); + break; +#endif + + case CURLOPT_PATH_AS_IS: + data->set.path_as_is = (0 != va_arg(param, long)); + break; + case CURLOPT_PIPEWAIT: + data->set.pipewait = (0 != va_arg(param, long)); + break; + case CURLOPT_STREAM_WEIGHT: +#if defined(USE_HTTP2) || defined(USE_HTTP3) + arg = va_arg(param, long); + if((arg >= 1) && (arg <= 256)) + data->set.priority.weight = (int)arg; + break; +#else + return CURLE_NOT_BUILT_IN; +#endif + case CURLOPT_STREAM_DEPENDS: + case CURLOPT_STREAM_DEPENDS_E: + { + struct Curl_easy *dep = va_arg(param, struct Curl_easy *); + if(!dep || GOOD_EASY_HANDLE(dep)) { + return Curl_data_priority_add_child(dep, data, + option == CURLOPT_STREAM_DEPENDS_E); + } + break; + } + case CURLOPT_CONNECT_TO: + data->set.connect_to = va_arg(param, struct curl_slist *); + break; + case CURLOPT_SUPPRESS_CONNECT_HEADERS: + data->set.suppress_connect_headers = (0 != va_arg(param, long))?TRUE:FALSE; + break; + case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: + uarg = va_arg(param, unsigned long); + if(uarg > UINT_MAX) + uarg = UINT_MAX; + data->set.happy_eyeballs_timeout = (unsigned int)uarg; + break; +#ifndef CURL_DISABLE_SHUFFLE_DNS + case CURLOPT_DNS_SHUFFLE_ADDRESSES: + data->set.dns_shuffle_addresses = (0 != va_arg(param, long)); + break; +#endif + case CURLOPT_DISALLOW_USERNAME_IN_URL: + data->set.disallow_username_in_url = (0 != va_arg(param, long)); + break; +#ifndef CURL_DISABLE_DOH + case CURLOPT_DOH_URL: + result = Curl_setstropt(&data->set.str[STRING_DOH], + va_arg(param, char *)); + data->set.doh = data->set.str[STRING_DOH]?TRUE:FALSE; + break; +#endif + case CURLOPT_UPKEEP_INTERVAL_MS: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.upkeep_interval_ms = arg; + break; + case CURLOPT_MAXAGE_CONN: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.maxage_conn = arg; + break; + case CURLOPT_MAXLIFETIME_CONN: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.maxlifetime_conn = arg; + break; + case CURLOPT_TRAILERFUNCTION: +#ifndef CURL_DISABLE_HTTP + data->set.trailer_callback = va_arg(param, curl_trailer_callback); +#endif + break; + case CURLOPT_TRAILERDATA: +#ifndef CURL_DISABLE_HTTP + data->set.trailer_data = va_arg(param, void *); +#endif + break; +#ifndef CURL_DISABLE_HSTS + case CURLOPT_HSTSREADFUNCTION: + data->set.hsts_read = va_arg(param, curl_hstsread_callback); + break; + case CURLOPT_HSTSREADDATA: + data->set.hsts_read_userp = va_arg(param, void *); + break; + case CURLOPT_HSTSWRITEFUNCTION: + data->set.hsts_write = va_arg(param, curl_hstswrite_callback); + break; + case CURLOPT_HSTSWRITEDATA: + data->set.hsts_write_userp = va_arg(param, void *); + break; + case CURLOPT_HSTS: { + struct curl_slist *h; + if(!data->hsts) { + data->hsts = Curl_hsts_init(); + if(!data->hsts) + return CURLE_OUT_OF_MEMORY; + } + argptr = va_arg(param, char *); + if(argptr) { + result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr); + if(result) + return result; + /* this needs to build a list of filenames to read from, so that it can + read them later, as we might get a shared HSTS handle to load them + into */ + h = curl_slist_append(data->state.hstslist, argptr); + if(!h) { + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; + return CURLE_OUT_OF_MEMORY; + } + data->state.hstslist = h; /* store the list for later use */ + } + else { + /* clear the list of HSTS files */ + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; + if(!data->share || !data->share->hsts) + /* throw away the HSTS cache unless shared */ + Curl_hsts_cleanup(&data->hsts); + } + break; + } + case CURLOPT_HSTS_CTRL: + arg = va_arg(param, long); + if(arg & CURLHSTS_ENABLE) { + if(!data->hsts) { + data->hsts = Curl_hsts_init(); + if(!data->hsts) + return CURLE_OUT_OF_MEMORY; + } + } + else + Curl_hsts_cleanup(&data->hsts); + break; +#endif +#ifndef CURL_DISABLE_ALTSVC + case CURLOPT_ALTSVC: + if(!data->asi) { + data->asi = Curl_altsvc_init(); + if(!data->asi) + return CURLE_OUT_OF_MEMORY; + } + argptr = va_arg(param, char *); + result = Curl_setstropt(&data->set.str[STRING_ALTSVC], argptr); + if(result) + return result; + if(argptr) + (void)Curl_altsvc_load(data->asi, argptr); + break; + case CURLOPT_ALTSVC_CTRL: + if(!data->asi) { + data->asi = Curl_altsvc_init(); + if(!data->asi) + return CURLE_OUT_OF_MEMORY; + } + arg = va_arg(param, long); + if(!arg) { + DEBUGF(infof(data, "bad CURLOPT_ALTSVC_CTRL input")); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + result = Curl_altsvc_ctrl(data->asi, arg); + if(result) + return result; + break; +#endif + case CURLOPT_PREREQFUNCTION: + data->set.fprereq = va_arg(param, curl_prereq_callback); + break; + case CURLOPT_PREREQDATA: + data->set.prereq_userp = va_arg(param, void *); + break; +#ifdef USE_WEBSOCKETS + case CURLOPT_WS_OPTIONS: { + bool raw; + arg = va_arg(param, long); + raw = (arg & CURLWS_RAW_MODE); + data->set.ws_raw_mode = raw; + break; + } +#endif +#ifdef USE_ECH + case CURLOPT_ECH: { + size_t plen = 0; + + argptr = va_arg(param, char *); + if(!argptr) { + data->set.tls_ech = CURLECH_DISABLE; + result = CURLE_BAD_FUNCTION_ARGUMENT; + return result; + } + plen = strlen(argptr); + if(plen > CURL_MAX_INPUT_LENGTH) { + data->set.tls_ech = CURLECH_DISABLE; + result = CURLE_BAD_FUNCTION_ARGUMENT; + return result; + } + /* set tls_ech flag value, preserving CLA_CFG bit */ + if(plen == 5 && !strcmp(argptr, "false")) + data->set.tls_ech = CURLECH_DISABLE + | (data->set.tls_ech & CURLECH_CLA_CFG); + else if(plen == 6 && !strcmp(argptr, "grease")) + data->set.tls_ech = CURLECH_GREASE + | (data->set.tls_ech & CURLECH_CLA_CFG); + else if(plen == 4 && !strcmp(argptr, "true")) + data->set.tls_ech = CURLECH_ENABLE + | (data->set.tls_ech & CURLECH_CLA_CFG); + else if(plen == 4 && !strcmp(argptr, "hard")) + data->set.tls_ech = CURLECH_HARD + | (data->set.tls_ech & CURLECH_CLA_CFG); + else if(plen > 5 && !strncmp(argptr, "ecl:", 4)) { + result = Curl_setstropt(&data->set.str[STRING_ECH_CONFIG], argptr + 4); + if(result) + return result; + data->set.tls_ech |= CURLECH_CLA_CFG; + } + else if(plen > 4 && !strncmp(argptr, "pn:", 3)) { + result = Curl_setstropt(&data->set.str[STRING_ECH_PUBLIC], argptr + 3); + if(result) + return result; + } + break; + } +#endif + case CURLOPT_QUICK_EXIT: + data->set.quick_exit = (0 != va_arg(param, long)) ? 1L:0L; + break; + default: + /* unknown tag and its companion, just ignore: */ + result = CURLE_UNKNOWN_OPTION; + break; + } + + return result; +} + +/* + * curl_easy_setopt() is the external interface for setting options on an + * easy handle. + * + * NOTE: This is one of few API functions that are allowed to be called from + * within a callback. + */ + +#undef curl_easy_setopt +CURLcode curl_easy_setopt(struct Curl_easy *data, CURLoption tag, ...) +{ + va_list arg; + CURLcode result; + + if(!data) + return CURLE_BAD_FUNCTION_ARGUMENT; + + va_start(arg, tag); + + result = Curl_vsetopt(data, tag, arg); + + va_end(arg); +#ifdef DEBUGBUILD + if(result == CURLE_BAD_FUNCTION_ARGUMENT) + infof(data, "setopt arg 0x%x returned CURLE_BAD_FUNCTION_ARGUMENT", tag); +#endif + return result; +} diff --git a/third_party/curl/lib/setopt.h b/third_party/curl/lib/setopt.h new file mode 100644 index 0000000000..b0237467bd --- /dev/null +++ b/third_party/curl/lib/setopt.h @@ -0,0 +1,33 @@ +#ifndef HEADER_CURL_SETOPT_H +#define HEADER_CURL_SETOPT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +CURLcode Curl_setstropt(char **charp, const char *s) WARN_UNUSED_RESULT; +CURLcode Curl_setblobopt(struct curl_blob **blobp, + const struct curl_blob *blob) WARN_UNUSED_RESULT; +CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg) + WARN_UNUSED_RESULT; + +#endif /* HEADER_CURL_SETOPT_H */ diff --git a/third_party/curl/lib/setup-os400.h b/third_party/curl/lib/setup-os400.h new file mode 100644 index 0000000000..ef7baca67e --- /dev/null +++ b/third_party/curl/lib/setup-os400.h @@ -0,0 +1,157 @@ +#ifndef HEADER_CURL_SETUP_OS400_H +#define HEADER_CURL_SETUP_OS400_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + + +/* OS/400 netdb.h does not define NI_MAXHOST. */ +#define NI_MAXHOST 1025 + +/* OS/400 netdb.h does not define NI_MAXSERV. */ +#define NI_MAXSERV 32 + +/* No OS/400 header file defines u_int32_t. */ +typedef unsigned long u_int32_t; + +/* OS/400 has no idea of a tty! */ +#define isatty(fd) 0 + + +/* Workaround bug in IBM QADRT runtime library: + * function puts() does not output the implicit trailing newline. + */ + +#include /* Be sure it is loaded. */ +#undef puts +#define puts(s) (fputs((s), stdout) == EOF? EOF: putchar('\n')) + + +/* System API wrapper prototypes & definitions to support ASCII parameters. */ + +#include +#include +#include +#include +#include + +#ifdef BUILDING_LIBCURL + +extern int Curl_getaddrinfo_a(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct addrinfo **res); +#define getaddrinfo Curl_getaddrinfo_a + +/* Note socklen_t must be used as this is declared before curl_socklen_t */ +extern int Curl_getnameinfo_a(const struct sockaddr *sa, + socklen_t salen, + char *nodename, socklen_t nodenamelen, + char *servname, socklen_t servnamelen, + int flags); +#define getnameinfo Curl_getnameinfo_a + +/* GSSAPI wrappers. */ + +extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status, + gss_buffer_t in_name, + gss_OID in_name_type, + gss_name_t * out_name); +#define gss_import_name Curl_gss_import_name_a + + +extern OM_uint32 Curl_gss_display_status_a(OM_uint32 * minor_status, + OM_uint32 status_value, + int status_type, gss_OID mech_type, + gss_msg_ctx_t * message_context, + gss_buffer_t status_string); +#define gss_display_status Curl_gss_display_status_a + + +extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 * minor_status, + gss_cred_id_t cred_handle, + gss_ctx_id_t * context_handle, + gss_name_t target_name, + gss_OID mech_type, + gss_flags_t req_flags, + OM_uint32 time_req, + gss_channel_bindings_t + input_chan_bindings, + gss_buffer_t input_token, + gss_OID * actual_mech_type, + gss_buffer_t output_token, + gss_flags_t * ret_flags, + OM_uint32 * time_rec); +#define gss_init_sec_context Curl_gss_init_sec_context_a + + +extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status, + gss_ctx_id_t * context_handle, + gss_buffer_t output_token); +#define gss_delete_sec_context Curl_gss_delete_sec_context_a + + +/* LDAP wrappers. */ + +#define BerValue struct berval + +#define ldap_url_parse ldap_url_parse_utf8 +#define ldap_init Curl_ldap_init_a +#define ldap_simple_bind_s Curl_ldap_simple_bind_s_a +#define ldap_search_s Curl_ldap_search_s_a +#define ldap_get_values_len Curl_ldap_get_values_len_a +#define ldap_err2string Curl_ldap_err2string_a +#define ldap_get_dn Curl_ldap_get_dn_a +#define ldap_first_attribute Curl_ldap_first_attribute_a +#define ldap_next_attribute Curl_ldap_next_attribute_a + +/* Some socket functions must be wrapped to process textual addresses + like AF_UNIX. */ + +extern int Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen); +extern int Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen); +extern int Curl_os400_sendto(int sd, char *buffer, int buflen, int flags, + const struct sockaddr *dstaddr, int addrlen); +extern int Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags, + struct sockaddr *fromaddr, int *addrlen); +extern int Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen); +extern int Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen); + +#define connect Curl_os400_connect +#define bind Curl_os400_bind +#define sendto Curl_os400_sendto +#define recvfrom Curl_os400_recvfrom +#define getpeername Curl_os400_getpeername +#define getsockname Curl_os400_getsockname + +#ifdef HAVE_LIBZ +#define zlibVersion Curl_os400_zlibVersion +#define inflateInit_ Curl_os400_inflateInit_ +#define inflateInit2_ Curl_os400_inflateInit2_ +#define inflate Curl_os400_inflate +#define inflateEnd Curl_os400_inflateEnd +#endif + +#endif /* BUILDING_LIBCURL */ + +#endif /* HEADER_CURL_SETUP_OS400_H */ diff --git a/third_party/curl/lib/setup-vms.h b/third_party/curl/lib/setup-vms.h new file mode 100644 index 0000000000..fc21560284 --- /dev/null +++ b/third_party/curl/lib/setup-vms.h @@ -0,0 +1,444 @@ +#ifndef HEADER_CURL_SETUP_VMS_H +#define HEADER_CURL_SETUP_VMS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* */ +/* JEM, 12/30/12, VMS now generates config.h, so only define wrappers for */ +/* getenv(), getpwuid() and provide is_vms_shell() */ +/* Also need upper case symbols for system services, and */ +/* OpenSSL, and some Kerberos image */ + +#ifdef __DECC +#pragma message save +#pragma message disable dollarid +#endif + +/* Hide the stuff we are overriding */ +#define getenv decc_getenv +#ifdef __DECC +# if __INITIAL_POINTER_SIZE != 64 +# define getpwuid decc_getpwuid +# endif +#endif +#include +char *decc$getenv(const char *__name); +#include + +#include +#include + +#undef getenv +#undef getpwuid +#define getenv vms_getenv +#define getpwuid vms_getpwuid + +/* VAX needs these in upper case when compiling exact case */ +#define sys$assign SYS$ASSIGN +#define sys$dassgn SYS$DASSGN +#define sys$qiow SYS$QIOW + +#ifdef __DECC +# if __INITIAL_POINTER_SIZE +# pragma __pointer_size __save +# endif +#endif + +#if __USE_LONG_GID_T +# define decc_getpwuid DECC$__LONG_GID_GETPWUID +#else +# if __INITIAL_POINTER_SIZE +# define decc_getpwuid decc$__32_getpwuid +# else +# define decc_getpwuid decc$getpwuid +# endif +#endif + + struct passwd *decc_getpwuid(uid_t uid); + +#ifdef __DECC +# if __INITIAL_POINTER_SIZE == 32 +/* Translate the path, but only if the path is a VMS file specification */ +/* The translation is usually only needed for older versions of VMS */ +static char *vms_translate_path(const char *path) +{ + char *unix_path; + char *test_str; + + /* See if the result is in VMS format, if not, we are done */ + /* Assume that this is a PATH, not just some data */ + test_str = strpbrk(path, ":[<^"); + if(!test_str) { + return (char *)path; + } + + unix_path = decc$translate_vms(path); + + if((int)unix_path <= 0) { + /* We can not translate it, so return the original string */ + return (char *)path; + } +} +# else + /* VMS translate path is actually not needed on the current 64 bit */ + /* VMS platforms, so instead of figuring out the pointer settings */ + /* Change it to a noop */ +# define vms_translate_path(__path) __path +# endif +#endif + +#ifdef __DECC +# if __INITIAL_POINTER_SIZE +# pragma __pointer_size __restore +# endif +#endif + +static char *vms_getenv(const char *envvar) +{ + char *result; + char *vms_path; + + /* first use the DECC getenv() function */ + result = decc$getenv(envvar); + if(!result) { + return result; + } + + vms_path = result; + result = vms_translate_path(vms_path); + + /* note that if you backport this to use VAX C RTL, that the VAX C RTL */ + /* may do a malloc(2048) for each call to getenv(), so you will need */ + /* to add a free(vms_path) */ + /* Do not do a free() for DEC C RTL builds, which should be used for */ + /* VMS 5.5-2 and later, even if using GCC */ + + return result; +} + + +static struct passwd vms_passwd_cache; + +static struct passwd *vms_getpwuid(uid_t uid) +{ + struct passwd *my_passwd; + +/* Hack needed to support 64-bit builds, decc_getpwnam is 32-bit only */ +#ifdef __DECC +# if __INITIAL_POINTER_SIZE + __char_ptr32 unix_path; +# else + char *unix_path; +# endif +#else + char *unix_path; +#endif + + my_passwd = decc_getpwuid(uid); + if(!my_passwd) { + return my_passwd; + } + + unix_path = vms_translate_path(my_passwd->pw_dir); + + if((long)unix_path <= 0) { + /* We can not translate it, so return the original string */ + return my_passwd; + } + + /* If no changes needed just return it */ + if(unix_path == my_passwd->pw_dir) { + return my_passwd; + } + + /* Need to copy the structure returned */ + /* Since curl is only using pw_dir, no need to fix up */ + /* the pw_shell when running under Bash */ + vms_passwd_cache.pw_name = my_passwd->pw_name; + vms_passwd_cache.pw_uid = my_passwd->pw_uid; + vms_passwd_cache.pw_gid = my_passwd->pw_uid; + vms_passwd_cache.pw_dir = unix_path; + vms_passwd_cache.pw_shell = my_passwd->pw_shell; + + return &vms_passwd_cache; +} + +#ifdef __DECC +#pragma message restore +#endif + +/* Bug - VMS OpenSSL and Kerberos universal symbols are in uppercase only */ +/* VMS libraries should have universal symbols in exact and uppercase */ + +#define ASN1_INTEGER_get ASN1_INTEGER_GET +#define ASN1_STRING_data ASN1_STRING_DATA +#define ASN1_STRING_length ASN1_STRING_LENGTH +#define ASN1_STRING_print ASN1_STRING_PRINT +#define ASN1_STRING_to_UTF8 ASN1_STRING_TO_UTF8 +#define ASN1_STRING_type ASN1_STRING_TYPE +#define BIO_ctrl BIO_CTRL +#define BIO_free BIO_FREE +#define BIO_new BIO_NEW +#define BIO_s_mem BIO_S_MEM +#define BN_bn2bin BN_BN2BIN +#define BN_num_bits BN_NUM_BITS +#define CRYPTO_cleanup_all_ex_data CRYPTO_CLEANUP_ALL_EX_DATA +#define CRYPTO_free CRYPTO_FREE +#define CRYPTO_malloc CRYPTO_MALLOC +#define CONF_modules_load_file CONF_MODULES_LOAD_FILE +#ifdef __VAX +# ifdef VMS_OLD_SSL + /* Ancient OpenSSL on VAX/VMS missing this constant */ +# define CONF_MFLAGS_IGNORE_MISSING_FILE 0x10 +# undef CONF_modules_load_file + static int CONF_modules_load_file(const char *filename, + const char *appname, + unsigned long flags) { + return 1; + } +# endif +#endif +#define DES_ecb_encrypt DES_ECB_ENCRYPT +#define DES_set_key DES_SET_KEY +#define DES_set_odd_parity DES_SET_ODD_PARITY +#define ENGINE_ctrl ENGINE_CTRL +#define ENGINE_ctrl_cmd ENGINE_CTRL_CMD +#define ENGINE_finish ENGINE_FINISH +#define ENGINE_free ENGINE_FREE +#define ENGINE_get_first ENGINE_GET_FIRST +#define ENGINE_get_id ENGINE_GET_ID +#define ENGINE_get_next ENGINE_GET_NEXT +#define ENGINE_init ENGINE_INIT +#define ENGINE_load_builtin_engines ENGINE_LOAD_BUILTIN_ENGINES +#define ENGINE_load_private_key ENGINE_LOAD_PRIVATE_KEY +#define ENGINE_set_default ENGINE_SET_DEFAULT +#define ERR_clear_error ERR_CLEAR_ERROR +#define ERR_error_string ERR_ERROR_STRING +#define ERR_error_string_n ERR_ERROR_STRING_N +#define ERR_free_strings ERR_FREE_STRINGS +#define ERR_get_error ERR_GET_ERROR +#define ERR_peek_error ERR_PEEK_ERROR +#define ERR_remove_state ERR_REMOVE_STATE +#define EVP_PKEY_copy_parameters EVP_PKEY_COPY_PARAMETERS +#define EVP_PKEY_free EVP_PKEY_FREE +#define EVP_cleanup EVP_CLEANUP +#define GENERAL_NAMES_free GENERAL_NAMES_FREE +#define i2d_X509_PUBKEY I2D_X509_PUBKEY +#define MD4_Final MD4_FINAL +#define MD4_Init MD4_INIT +#define MD4_Update MD4_UPDATE +#define MD5_Final MD5_FINAL +#define MD5_Init MD5_INIT +#define MD5_Update MD5_UPDATE +#define OPENSSL_add_all_algo_noconf OPENSSL_ADD_ALL_ALGO_NOCONF +#ifndef __VAX +#define OPENSSL_load_builtin_modules OPENSSL_LOAD_BUILTIN_MODULES +#endif +#define PEM_read_X509 PEM_READ_X509 +#define PEM_write_bio_X509 PEM_WRITE_BIO_X509 +#define PKCS12_PBE_add PKCS12_PBE_ADD +#define PKCS12_free PKCS12_FREE +#define PKCS12_parse PKCS12_PARSE +#define RAND_add RAND_ADD +#define RAND_bytes RAND_BYTES +#define RAND_file_name RAND_FILE_NAME +#define RAND_load_file RAND_LOAD_FILE +#define RAND_status RAND_STATUS +#define SSL_CIPHER_get_name SSL_CIPHER_GET_NAME +#define SSL_CTX_add_client_CA SSL_CTX_ADD_CLIENT_CA +#define SSL_CTX_callback_ctrl SSL_CTX_CALLBACK_CTRL +#define SSL_CTX_check_private_key SSL_CTX_CHECK_PRIVATE_KEY +#define SSL_CTX_ctrl SSL_CTX_CTRL +#define SSL_CTX_free SSL_CTX_FREE +#define SSL_CTX_get_cert_store SSL_CTX_GET_CERT_STORE +#define SSL_CTX_load_verify_locations SSL_CTX_LOAD_VERIFY_LOCATIONS +#define SSL_CTX_new SSL_CTX_NEW +#define SSL_CTX_set_cipher_list SSL_CTX_SET_CIPHER_LIST +#define SSL_CTX_set_def_passwd_cb_ud SSL_CTX_SET_DEF_PASSWD_CB_UD +#define SSL_CTX_set_default_passwd_cb SSL_CTX_SET_DEFAULT_PASSWD_CB +#define SSL_CTX_set_msg_callback SSL_CTX_SET_MSG_CALLBACK +#define SSL_CTX_set_verify SSL_CTX_SET_VERIFY +#define SSL_CTX_use_PrivateKey SSL_CTX_USE_PRIVATEKEY +#define SSL_CTX_use_PrivateKey_file SSL_CTX_USE_PRIVATEKEY_FILE +#define SSL_CTX_use_cert_chain_file SSL_CTX_USE_CERT_CHAIN_FILE +#define SSL_CTX_use_certificate SSL_CTX_USE_CERTIFICATE +#define SSL_CTX_use_certificate_file SSL_CTX_USE_CERTIFICATE_FILE +#define SSL_SESSION_free SSL_SESSION_FREE +#define SSL_connect SSL_CONNECT +#define SSL_free SSL_FREE +#define SSL_get1_session SSL_GET1_SESSION +#define SSL_get_certificate SSL_GET_CERTIFICATE +#define SSL_get_current_cipher SSL_GET_CURRENT_CIPHER +#define SSL_get_error SSL_GET_ERROR +#define SSL_get_peer_cert_chain SSL_GET_PEER_CERT_CHAIN +#define SSL_get_peer_certificate SSL_GET_PEER_CERTIFICATE +#define SSL_get_privatekey SSL_GET_PRIVATEKEY +#define SSL_get_session SSL_GET_SESSION +#define SSL_get_shutdown SSL_GET_SHUTDOWN +#define SSL_get_verify_result SSL_GET_VERIFY_RESULT +#define SSL_library_init SSL_LIBRARY_INIT +#define SSL_load_error_strings SSL_LOAD_ERROR_STRINGS +#define SSL_new SSL_NEW +#define SSL_peek SSL_PEEK +#define SSL_pending SSL_PENDING +#define SSL_read SSL_READ +#define SSL_set_connect_state SSL_SET_CONNECT_STATE +#define SSL_set_fd SSL_SET_FD +#define SSL_set_session SSL_SET_SESSION +#define SSL_shutdown SSL_SHUTDOWN +#define SSL_version SSL_VERSION +#define SSL_write SSL_WRITE +#define SSLeay SSLEAY +#define SSLv23_client_method SSLV23_CLIENT_METHOD +#define SSLv3_client_method SSLV3_CLIENT_METHOD +#define TLSv1_client_method TLSV1_CLIENT_METHOD +#define UI_create_method UI_CREATE_METHOD +#define UI_destroy_method UI_DESTROY_METHOD +#define UI_get0_user_data UI_GET0_USER_DATA +#define UI_get_input_flags UI_GET_INPUT_FLAGS +#define UI_get_string_type UI_GET_STRING_TYPE +#define UI_create_method UI_CREATE_METHOD +#define UI_destroy_method UI_DESTROY_METHOD +#define UI_method_get_closer UI_METHOD_GET_CLOSER +#define UI_method_get_opener UI_METHOD_GET_OPENER +#define UI_method_get_reader UI_METHOD_GET_READER +#define UI_method_get_writer UI_METHOD_GET_WRITER +#define UI_method_set_closer UI_METHOD_SET_CLOSER +#define UI_method_set_opener UI_METHOD_SET_OPENER +#define UI_method_set_reader UI_METHOD_SET_READER +#define UI_method_set_writer UI_METHOD_SET_WRITER +#define UI_OpenSSL UI_OPENSSL +#define UI_set_result UI_SET_RESULT +#define X509V3_EXT_print X509V3_EXT_PRINT +#define X509_EXTENSION_get_critical X509_EXTENSION_GET_CRITICAL +#define X509_EXTENSION_get_data X509_EXTENSION_GET_DATA +#define X509_EXTENSION_get_object X509_EXTENSION_GET_OBJECT +#define X509_LOOKUP_file X509_LOOKUP_FILE +#define X509_NAME_ENTRY_get_data X509_NAME_ENTRY_GET_DATA +#define X509_NAME_get_entry X509_NAME_GET_ENTRY +#define X509_NAME_get_index_by_NID X509_NAME_GET_INDEX_BY_NID +#define X509_NAME_print_ex X509_NAME_PRINT_EX +#define X509_STORE_CTX_get_current_cert X509_STORE_CTX_GET_CURRENT_CERT +#define X509_STORE_add_lookup X509_STORE_ADD_LOOKUP +#define X509_STORE_set_flags X509_STORE_SET_FLAGS +#define X509_check_issued X509_CHECK_ISSUED +#define X509_free X509_FREE +#define X509_get_ext_d2i X509_GET_EXT_D2I +#define X509_get_issuer_name X509_GET_ISSUER_NAME +#define X509_get_pubkey X509_GET_PUBKEY +#define X509_get_serialNumber X509_GET_SERIALNUMBER +#define X509_get_subject_name X509_GET_SUBJECT_NAME +#define X509_load_crl_file X509_LOAD_CRL_FILE +#define X509_verify_cert_error_string X509_VERIFY_CERT_ERROR_STRING +#define d2i_PKCS12_fp D2I_PKCS12_FP +#define i2t_ASN1_OBJECT I2T_ASN1_OBJECT +#define sk_num SK_NUM +#define sk_pop SK_POP +#define sk_pop_free SK_POP_FREE +#define sk_value SK_VALUE +#ifdef __VAX +#define OPENSSL_NO_SHA256 +#endif +#define SHA256_Final SHA256_FINAL +#define SHA256_Init SHA256_INIT +#define SHA256_Update SHA256_UPDATE + +#define USE_UPPERCASE_GSSAPI 1 +#define gss_seal GSS_SEAL +#define gss_unseal GSS_UNSEAL + +#define USE_UPPERCASE_KRBAPI 1 + +/* AI_NUMERICHOST needed for IP V6 support in Curl */ +#ifdef HAVE_NETDB_H +#include +#ifndef AI_NUMERICHOST +#ifdef USE_IPV6 +#undef USE_IPV6 +#endif +#endif +#endif + +/* VAX symbols are always in uppercase */ +#ifdef __VAX +#define inflate INFLATE +#define inflateEnd INFLATEEND +#define inflateInit2_ INFLATEINIT2_ +#define inflateInit_ INFLATEINIT_ +#define zlibVersion ZLIBVERSION +#endif + +/* Older VAX OpenSSL port defines these as Macros */ +/* Need to include the headers first and then redefine */ +/* that way a newer port will also work if some one has one */ +#ifdef __VAX + +# if (OPENSSL_VERSION_NUMBER < 0x00907001L) +# define des_set_odd_parity DES_SET_ODD_PARITY +# define des_set_key DES_SET_KEY +# define des_ecb_encrypt DES_ECB_ENCRYPT + +# endif +# include +# ifndef OpenSSL_add_all_algorithms +# define OpenSSL_add_all_algorithms OPENSSL_ADD_ALL_ALGORITHMS + void OPENSSL_ADD_ALL_ALGORITHMS(void); +# endif + + /* Curl defines these to lower case and VAX needs them in upper case */ + /* So we need static routines */ +# if (OPENSSL_VERSION_NUMBER < 0x00907001L) + +# undef des_set_odd_parity +# undef DES_set_odd_parity +# undef des_set_key +# undef DES_set_key +# undef des_ecb_encrypt +# undef DES_ecb_encrypt + + static void des_set_odd_parity(des_cblock *key) { + DES_SET_ODD_PARITY(key); + } + + static int des_set_key(const_des_cblock *key, + des_key_schedule schedule) { + return DES_SET_KEY(key, schedule); + } + + static void des_ecb_encrypt(const_des_cblock *input, + des_cblock *output, + des_key_schedule ks, int enc) { + DES_ECB_ENCRYPT(input, output, ks, enc); + } +#endif +/* Need this to stop a macro redefinition error */ +#if OPENSSL_VERSION_NUMBER < 0x00907000L +# ifdef X509_STORE_set_flags +# undef X509_STORE_set_flags +# define X509_STORE_set_flags(x,y) Curl_nop_stmt +# endif +#endif +#endif + +#endif /* HEADER_CURL_SETUP_VMS_H */ diff --git a/third_party/curl/lib/setup-win32.h b/third_party/curl/lib/setup-win32.h new file mode 100644 index 0000000000..80efc25f13 --- /dev/null +++ b/third_party/curl/lib/setup-win32.h @@ -0,0 +1,138 @@ +#ifndef HEADER_CURL_SETUP_WIN32_H +#define HEADER_CURL_SETUP_WIN32_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#undef USE_WINSOCK +/* ---------------------------------------------------------------- */ +/* Watt-32 TCP/IP SPECIFIC */ +/* ---------------------------------------------------------------- */ +#ifdef USE_WATT32 +# include +# undef byte +# undef word +# define HAVE_SYS_IOCTL_H +# define HAVE_SYS_SOCKET_H +# define HAVE_NETINET_IN_H +# define HAVE_NETDB_H +# define HAVE_ARPA_INET_H +# define SOCKET int +/* ---------------------------------------------------------------- */ +/* BSD-style lwIP TCP/IP stack SPECIFIC */ +/* ---------------------------------------------------------------- */ +#elif defined(USE_LWIPSOCK) + /* Define to use BSD-style lwIP TCP/IP stack. */ + /* #define USE_LWIPSOCK 1 */ +# undef HAVE_GETHOSTNAME +# undef LWIP_POSIX_SOCKETS_IO_NAMES +# undef RECV_TYPE_ARG1 +# undef RECV_TYPE_ARG3 +# undef SEND_TYPE_ARG1 +# undef SEND_TYPE_ARG3 +# define HAVE_GETHOSTBYNAME_R +# define HAVE_GETHOSTBYNAME_R_6 +# define LWIP_POSIX_SOCKETS_IO_NAMES 0 +# define RECV_TYPE_ARG1 int +# define RECV_TYPE_ARG3 size_t +# define SEND_TYPE_ARG1 int +# define SEND_TYPE_ARG3 size_t +#elif defined(_WIN32) +# define USE_WINSOCK 2 +#endif + +/* + * Include header files for windows builds before redefining anything. + * Use this preprocessor block only to include or exclude windows.h, + * winsock2.h or ws2tcpip.h. Any other windows thing belongs + * to any other further and independent block. Under Cygwin things work + * just as under linux (e.g. ) and the winsock headers should + * never be included when __CYGWIN__ is defined. + */ + +#ifdef _WIN32 +# if defined(UNICODE) && !defined(_UNICODE) +# error "UNICODE is defined but _UNICODE is not defined" +# endif +# if defined(_UNICODE) && !defined(UNICODE) +# error "_UNICODE is defined but UNICODE is not defined" +# endif +/* + * Do not include unneeded stuff in Windows headers to avoid compiler + * warnings and macro clashes. + * Make sure to define this macro before including any Windows headers. + */ +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# ifndef NOGDI +# define NOGDI +# endif +# include +# include +# include +# include +# include +# ifdef UNICODE + typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str); +# endif +#endif + +/* + * Define _WIN32_WINNT_[OS] symbols because not all Windows build systems have + * those symbols to compare against, and even those that do may be missing + * newer symbols. + */ + +#ifndef _WIN32_WINNT_NT4 +#define _WIN32_WINNT_NT4 0x0400 /* Windows NT 4.0 */ +#endif +#ifndef _WIN32_WINNT_WIN2K +#define _WIN32_WINNT_WIN2K 0x0500 /* Windows 2000 */ +#endif +#ifndef _WIN32_WINNT_WINXP +#define _WIN32_WINNT_WINXP 0x0501 /* Windows XP */ +#endif +#ifndef _WIN32_WINNT_WS03 +#define _WIN32_WINNT_WS03 0x0502 /* Windows Server 2003 */ +#endif +#ifndef _WIN32_WINNT_VISTA +#define _WIN32_WINNT_VISTA 0x0600 /* Windows Vista */ +#endif +#ifndef _WIN32_WINNT_WS08 +#define _WIN32_WINNT_WS08 0x0600 /* Windows Server 2008 */ +#endif +#ifndef _WIN32_WINNT_WIN7 +#define _WIN32_WINNT_WIN7 0x0601 /* Windows 7 */ +#endif +#ifndef _WIN32_WINNT_WIN8 +#define _WIN32_WINNT_WIN8 0x0602 /* Windows 8 */ +#endif +#ifndef _WIN32_WINNT_WINBLUE +#define _WIN32_WINNT_WINBLUE 0x0603 /* Windows 8.1 */ +#endif +#ifndef _WIN32_WINNT_WIN10 +#define _WIN32_WINNT_WIN10 0x0A00 /* Windows 10 */ +#endif + +#endif /* HEADER_CURL_SETUP_WIN32_H */ diff --git a/third_party/curl/lib/sha256.c b/third_party/curl/lib/sha256.c new file mode 100644 index 0000000000..6ff66f7418 --- /dev/null +++ b/third_party/curl/lib/sha256.c @@ -0,0 +1,545 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Florin Petriuc, + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ + || defined(USE_LIBSSH2) + +#include "warnless.h" +#include "curl_sha256.h" +#include "curl_hmac.h" + +#ifdef USE_WOLFSSL +#include +#ifndef NO_SHA256 +#define USE_OPENSSL_SHA256 +#endif +#endif + +#if defined(USE_OPENSSL) + +#include + +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) +#define USE_OPENSSL_SHA256 +#endif + +#endif /* USE_OPENSSL */ + +#ifdef USE_MBEDTLS +#include + +#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \ + (MBEDTLS_VERSION_NUMBER < 0x03000000) + #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS +#endif +#endif /* USE_MBEDTLS */ + +#if defined(USE_OPENSSL_SHA256) + +/* When OpenSSL or wolfSSL is available we use their SHA256-functions. */ +#if defined(USE_OPENSSL) +#include +#elif defined(USE_WOLFSSL) +#include +#endif + +#elif defined(USE_GNUTLS) +#include +#elif defined(USE_MBEDTLS) +#include +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) +#include +#define AN_APPLE_OS +#elif defined(USE_WIN32_CRYPTO) +#include +#endif + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* Please keep the SSL backend-specific #if branches in this order: + * + * 1. USE_OPENSSL + * 2. USE_GNUTLS + * 3. USE_MBEDTLS + * 4. USE_COMMON_CRYPTO + * 5. USE_WIN32_CRYPTO + * + * This ensures that the same SSL branch gets activated throughout this source + * file even if multiple backends are enabled at the same time. + */ + +#if defined(USE_OPENSSL_SHA256) + +struct sha256_ctx { + EVP_MD_CTX *openssl_ctx; +}; +typedef struct sha256_ctx my_sha256_ctx; + +static CURLcode my_sha256_init(my_sha256_ctx *ctx) +{ + ctx->openssl_ctx = EVP_MD_CTX_create(); + if(!ctx->openssl_ctx) + return CURLE_OUT_OF_MEMORY; + + if(!EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL)) { + EVP_MD_CTX_destroy(ctx->openssl_ctx); + return CURLE_FAILED_INIT; + } + return CURLE_OK; +} + +static void my_sha256_update(my_sha256_ctx *ctx, + const unsigned char *data, + unsigned int length) +{ + EVP_DigestUpdate(ctx->openssl_ctx, data, length); +} + +static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +{ + EVP_DigestFinal_ex(ctx->openssl_ctx, digest, NULL); + EVP_MD_CTX_destroy(ctx->openssl_ctx); +} + +#elif defined(USE_GNUTLS) + +typedef struct sha256_ctx my_sha256_ctx; + +static CURLcode my_sha256_init(my_sha256_ctx *ctx) +{ + sha256_init(ctx); + return CURLE_OK; +} + +static void my_sha256_update(my_sha256_ctx *ctx, + const unsigned char *data, + unsigned int length) +{ + sha256_update(ctx, length, data); +} + +static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +{ + sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); +} + +#elif defined(USE_MBEDTLS) + +typedef mbedtls_sha256_context my_sha256_ctx; + +static CURLcode my_sha256_init(my_sha256_ctx *ctx) +{ +#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + (void) mbedtls_sha256_starts(ctx, 0); +#else + (void) mbedtls_sha256_starts_ret(ctx, 0); +#endif + return CURLE_OK; +} + +static void my_sha256_update(my_sha256_ctx *ctx, + const unsigned char *data, + unsigned int length) +{ +#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + (void) mbedtls_sha256_update(ctx, data, length); +#else + (void) mbedtls_sha256_update_ret(ctx, data, length); +#endif +} + +static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +{ +#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + (void) mbedtls_sha256_finish(ctx, digest); +#else + (void) mbedtls_sha256_finish_ret(ctx, digest); +#endif +} + +#elif defined(AN_APPLE_OS) +typedef CC_SHA256_CTX my_sha256_ctx; + +static CURLcode my_sha256_init(my_sha256_ctx *ctx) +{ + (void) CC_SHA256_Init(ctx); + return CURLE_OK; +} + +static void my_sha256_update(my_sha256_ctx *ctx, + const unsigned char *data, + unsigned int length) +{ + (void) CC_SHA256_Update(ctx, data, length); +} + +static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +{ + (void) CC_SHA256_Final(digest, ctx); +} + +#elif defined(USE_WIN32_CRYPTO) + +struct sha256_ctx { + HCRYPTPROV hCryptProv; + HCRYPTHASH hHash; +}; +typedef struct sha256_ctx my_sha256_ctx; + +#if !defined(CALG_SHA_256) +#define CALG_SHA_256 0x0000800c +#endif + +static CURLcode my_sha256_init(my_sha256_ctx *ctx) +{ + if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return CURLE_OUT_OF_MEMORY; + + if(!CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash)) { + CryptReleaseContext(ctx->hCryptProv, 0); + ctx->hCryptProv = 0; + return CURLE_FAILED_INIT; + } + + return CURLE_OK; +} + +static void my_sha256_update(my_sha256_ctx *ctx, + const unsigned char *data, + unsigned int length) +{ + CryptHashData(ctx->hHash, (unsigned char *) data, length, 0); +} + +static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +{ + unsigned long length = 0; + + CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); + if(length == SHA256_DIGEST_LENGTH) + CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0); + + if(ctx->hHash) + CryptDestroyHash(ctx->hHash); + + if(ctx->hCryptProv) + CryptReleaseContext(ctx->hCryptProv, 0); +} + +#else + +/* When no other crypto library is available we use this code segment */ + +/* This is based on SHA256 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +#define WPA_GET_BE32(a) ((((unsigned long)(a)[0]) << 24) | \ + (((unsigned long)(a)[1]) << 16) | \ + (((unsigned long)(a)[2]) << 8) | \ + ((unsigned long)(a)[3])) +#define WPA_PUT_BE32(a, val) \ +do { \ + (a)[0] = (unsigned char)((((unsigned long) (val)) >> 24) & 0xff); \ + (a)[1] = (unsigned char)((((unsigned long) (val)) >> 16) & 0xff); \ + (a)[2] = (unsigned char)((((unsigned long) (val)) >> 8) & 0xff); \ + (a)[3] = (unsigned char)(((unsigned long) (val)) & 0xff); \ +} while(0) + +#ifdef HAVE_LONGLONG +#define WPA_PUT_BE64(a, val) \ +do { \ + (a)[0] = (unsigned char)(((unsigned long long)(val)) >> 56); \ + (a)[1] = (unsigned char)(((unsigned long long)(val)) >> 48); \ + (a)[2] = (unsigned char)(((unsigned long long)(val)) >> 40); \ + (a)[3] = (unsigned char)(((unsigned long long)(val)) >> 32); \ + (a)[4] = (unsigned char)(((unsigned long long)(val)) >> 24); \ + (a)[5] = (unsigned char)(((unsigned long long)(val)) >> 16); \ + (a)[6] = (unsigned char)(((unsigned long long)(val)) >> 8); \ + (a)[7] = (unsigned char)(((unsigned long long)(val)) & 0xff); \ +} while(0) +#else +#define WPA_PUT_BE64(a, val) \ +do { \ + (a)[0] = (unsigned char)(((unsigned __int64)(val)) >> 56); \ + (a)[1] = (unsigned char)(((unsigned __int64)(val)) >> 48); \ + (a)[2] = (unsigned char)(((unsigned __int64)(val)) >> 40); \ + (a)[3] = (unsigned char)(((unsigned __int64)(val)) >> 32); \ + (a)[4] = (unsigned char)(((unsigned __int64)(val)) >> 24); \ + (a)[5] = (unsigned char)(((unsigned __int64)(val)) >> 16); \ + (a)[6] = (unsigned char)(((unsigned __int64)(val)) >> 8); \ + (a)[7] = (unsigned char)(((unsigned __int64)(val)) & 0xff); \ +} while(0) +#endif + +struct sha256_state { +#ifdef HAVE_LONGLONG + unsigned long long length; +#else + unsigned __int64 length; +#endif + unsigned long state[8], curlen; + unsigned char buf[64]; +}; +typedef struct sha256_state my_sha256_ctx; + +/* The K array */ +static const unsigned long K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Various logical functions */ +#define RORc(x, y) \ +(((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | \ + ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define Sha256_Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Sha256_Maj(x,y,z) (((x | y) & z) | (x & y)) +#define Sha256_S(x, n) RORc((x), (n)) +#define Sha256_R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (Sha256_S(x, 2) ^ Sha256_S(x, 13) ^ Sha256_S(x, 22)) +#define Sigma1(x) (Sha256_S(x, 6) ^ Sha256_S(x, 11) ^ Sha256_S(x, 25)) +#define Gamma0(x) (Sha256_S(x, 7) ^ Sha256_S(x, 18) ^ Sha256_R(x, 3)) +#define Gamma1(x) (Sha256_S(x, 17) ^ Sha256_S(x, 19) ^ Sha256_R(x, 10)) + +/* Compress 512-bits */ +static int sha256_compress(struct sha256_state *md, + unsigned char *buf) +{ + unsigned long S[8], W[64]; + int i; + + /* Copy state into S */ + for(i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + /* copy the state into 512-bits into W[0..15] */ + for(i = 0; i < 16; i++) + W[i] = WPA_GET_BE32(buf + (4 * i)); + /* fill W[16..63] */ + for(i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i) \ + do { \ + unsigned long t0 = h + Sigma1(e) + Sha256_Ch(e, f, g) + K[i] + W[i]; \ + unsigned long t1 = Sigma0(a) + Sha256_Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; \ + } while(0) + + for(i = 0; i < 64; ++i) { + unsigned long t; + RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } + + /* Feedback */ + for(i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + + return 0; +} + +/* Initialize the hash state */ +static CURLcode my_sha256_init(struct sha256_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; + + return CURLE_OK; +} + +/* + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return 0 if successful +*/ +static int my_sha256_update(struct sha256_state *md, + const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; + +#define block_size 64 + if(md->curlen > sizeof(md->buf)) + return -1; + while(inlen > 0) { + if(md->curlen == 0 && inlen >= block_size) { + if(sha256_compress(md, (unsigned char *)in) < 0) + return -1; + md->length += block_size * 8; + in += block_size; + inlen -= block_size; + } + else { + n = CURLMIN(inlen, (block_size - md->curlen)); + memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if(md->curlen == block_size) { + if(sha256_compress(md, md->buf) < 0) + return -1; + md->length += 8 * block_size; + md->curlen = 0; + } + } + } + + return 0; +} + +/* + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return 0 if successful +*/ +static int my_sha256_final(unsigned char *out, + struct sha256_state *md) +{ + int i; + + if(md->curlen >= sizeof(md->buf)) + return -1; + + /* Increase the length of the message */ + md->length += md->curlen * 8; + + /* Append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + + /* If the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if(md->curlen > 56) { + while(md->curlen < 64) { + md->buf[md->curlen++] = (unsigned char)0; + } + sha256_compress(md, md->buf); + md->curlen = 0; + } + + /* Pad up to 56 bytes of zeroes */ + while(md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char)0; + } + + /* Store length */ + WPA_PUT_BE64(md->buf + 56, md->length); + sha256_compress(md, md->buf); + + /* Copy output */ + for(i = 0; i < 8; i++) + WPA_PUT_BE32(out + (4 * i), md->state[i]); + + return 0; +} + +#endif /* CRYPTO LIBS */ + +/* + * Curl_sha256it() + * + * Generates a SHA256 hash for the given input data. + * + * Parameters: + * + * output [in/out] - The output buffer. + * input [in] - The input data. + * length [in] - The input length. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, + const size_t length) +{ + CURLcode result; + my_sha256_ctx ctx; + + result = my_sha256_init(&ctx); + if(!result) { + my_sha256_update(&ctx, input, curlx_uztoui(length)); + my_sha256_final(output, &ctx); + } + return result; +} + + +const struct HMAC_params Curl_HMAC_SHA256[] = { + { + /* Hash initialization function. */ + CURLX_FUNCTION_CAST(HMAC_hinit_func, my_sha256_init), + /* Hash update function. */ + CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_sha256_update), + /* Hash computation end function. */ + CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_sha256_final), + /* Size of hash context structure. */ + sizeof(my_sha256_ctx), + /* Maximum key length. */ + 64, + /* Result size. */ + 32 + } +}; + + +#endif /* AWS, DIGEST, or libssh2 */ diff --git a/third_party/curl/lib/share.c b/third_party/curl/lib/share.c new file mode 100644 index 0000000000..164aceb874 --- /dev/null +++ b/third_party/curl/lib/share.c @@ -0,0 +1,291 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include +#include "urldata.h" +#include "connect.h" +#include "share.h" +#include "psl.h" +#include "vtls/vtls.h" +#include "hsts.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +struct Curl_share * +curl_share_init(void) +{ + struct Curl_share *share = calloc(1, sizeof(struct Curl_share)); + if(share) { + share->magic = CURL_GOOD_SHARE; + share->specifier |= (1<hostcache, 23); + } + + return share; +} + +#undef curl_share_setopt +CURLSHcode +curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) +{ + va_list param; + int type; + curl_lock_function lockfunc; + curl_unlock_function unlockfunc; + void *ptr; + CURLSHcode res = CURLSHE_OK; + + if(!GOOD_SHARE_HANDLE(share)) + return CURLSHE_INVALID; + + if(share->dirty) + /* do not allow setting options while one or more handles are already + using this share */ + return CURLSHE_IN_USE; + + va_start(param, option); + + switch(option) { + case CURLSHOPT_SHARE: + /* this is a type this share will share */ + type = va_arg(param, int); + + switch(type) { + case CURL_LOCK_DATA_DNS: + break; + + case CURL_LOCK_DATA_COOKIE: +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(!share->cookies) { + share->cookies = Curl_cookie_init(NULL, NULL, NULL, TRUE); + if(!share->cookies) + res = CURLSHE_NOMEM; + } +#else /* CURL_DISABLE_HTTP */ + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + + case CURL_LOCK_DATA_HSTS: +#ifndef CURL_DISABLE_HSTS + if(!share->hsts) { + share->hsts = Curl_hsts_init(); + if(!share->hsts) + res = CURLSHE_NOMEM; + } +#else /* CURL_DISABLE_HSTS */ + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + + case CURL_LOCK_DATA_SSL_SESSION: +#ifdef USE_SSL + if(!share->sslsession) { + share->max_ssl_sessions = 8; + share->sslsession = calloc(share->max_ssl_sessions, + sizeof(struct Curl_ssl_session)); + share->sessionage = 0; + if(!share->sslsession) + res = CURLSHE_NOMEM; + } +#else + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + + case CURL_LOCK_DATA_CONNECT: + if(Curl_conncache_init(&share->conn_cache, NULL, 103)) + res = CURLSHE_NOMEM; + break; + + case CURL_LOCK_DATA_PSL: +#ifndef USE_LIBPSL + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + + default: + res = CURLSHE_BAD_OPTION; + } + if(!res) + share->specifier |= (unsigned int)(1<specifier &= ~(unsigned int)(1<cookies) { + Curl_cookie_cleanup(share->cookies); + share->cookies = NULL; + } +#else /* CURL_DISABLE_HTTP */ + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + + case CURL_LOCK_DATA_HSTS: +#ifndef CURL_DISABLE_HSTS + if(share->hsts) { + Curl_hsts_cleanup(&share->hsts); + } +#else /* CURL_DISABLE_HSTS */ + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + + case CURL_LOCK_DATA_SSL_SESSION: +#ifdef USE_SSL + Curl_safefree(share->sslsession); +#else + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + + case CURL_LOCK_DATA_CONNECT: + break; + + default: + res = CURLSHE_BAD_OPTION; + break; + } + break; + + case CURLSHOPT_LOCKFUNC: + lockfunc = va_arg(param, curl_lock_function); + share->lockfunc = lockfunc; + break; + + case CURLSHOPT_UNLOCKFUNC: + unlockfunc = va_arg(param, curl_unlock_function); + share->unlockfunc = unlockfunc; + break; + + case CURLSHOPT_USERDATA: + ptr = va_arg(param, void *); + share->clientdata = ptr; + break; + + default: + res = CURLSHE_BAD_OPTION; + break; + } + + va_end(param); + + return res; +} + +CURLSHcode +curl_share_cleanup(struct Curl_share *share) +{ + if(!GOOD_SHARE_HANDLE(share)) + return CURLSHE_INVALID; + + if(share->lockfunc) + share->lockfunc(NULL, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE, + share->clientdata); + + if(share->dirty) { + if(share->unlockfunc) + share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata); + return CURLSHE_IN_USE; + } + + Curl_conncache_close_all_connections(&share->conn_cache); + Curl_conncache_destroy(&share->conn_cache); + Curl_hash_destroy(&share->hostcache); + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + Curl_cookie_cleanup(share->cookies); +#endif + +#ifndef CURL_DISABLE_HSTS + Curl_hsts_cleanup(&share->hsts); +#endif + +#ifdef USE_SSL + if(share->sslsession) { + size_t i; + for(i = 0; i < share->max_ssl_sessions; i++) + Curl_ssl_kill_session(&(share->sslsession[i])); + free(share->sslsession); + } +#endif + + Curl_psl_destroy(&share->psl); + + if(share->unlockfunc) + share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata); + share->magic = 0; + free(share); + + return CURLSHE_OK; +} + + +CURLSHcode +Curl_share_lock(struct Curl_easy *data, curl_lock_data type, + curl_lock_access accesstype) +{ + struct Curl_share *share = data->share; + + if(!share) + return CURLSHE_INVALID; + + if(share->specifier & (unsigned int)(1<lockfunc) /* only call this if set! */ + share->lockfunc(data, type, accesstype, share->clientdata); + } + /* else if we do not share this, pretend successful lock */ + + return CURLSHE_OK; +} + +CURLSHcode +Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) +{ + struct Curl_share *share = data->share; + + if(!share) + return CURLSHE_INVALID; + + if(share->specifier & (unsigned int)(1<unlockfunc) /* only call this if set! */ + share->unlockfunc (data, type, share->clientdata); + } + + return CURLSHE_OK; +} diff --git a/third_party/curl/lib/share.h b/third_party/curl/lib/share.h new file mode 100644 index 0000000000..f63a6f8fe5 --- /dev/null +++ b/third_party/curl/lib/share.h @@ -0,0 +1,68 @@ +#ifndef HEADER_CURL_SHARE_H +#define HEADER_CURL_SHARE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include +#include "cookie.h" +#include "psl.h" +#include "urldata.h" +#include "conncache.h" + +#define CURL_GOOD_SHARE 0x7e117a1e +#define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE) + +/* this struct is libcurl-private, do not export details */ +struct Curl_share { + unsigned int magic; /* CURL_GOOD_SHARE */ + unsigned int specifier; + volatile unsigned int dirty; + + curl_lock_function lockfunc; + curl_unlock_function unlockfunc; + void *clientdata; + struct conncache conn_cache; + struct Curl_hash hostcache; +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + struct CookieInfo *cookies; +#endif +#ifdef USE_LIBPSL + struct PslCache psl; +#endif +#ifndef CURL_DISABLE_HSTS + struct hsts *hsts; +#endif +#ifdef USE_SSL + struct Curl_ssl_session *sslsession; + size_t max_ssl_sessions; + long sessionage; +#endif +}; + +CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data, + curl_lock_access); +CURLSHcode Curl_share_unlock(struct Curl_easy *, curl_lock_data); + +#endif /* HEADER_CURL_SHARE_H */ diff --git a/third_party/curl/lib/sigpipe.h b/third_party/curl/lib/sigpipe.h new file mode 100644 index 0000000000..b91a2f5133 --- /dev/null +++ b/third_party/curl/lib/sigpipe.h @@ -0,0 +1,96 @@ +#ifndef HEADER_CURL_SIGPIPE_H +#define HEADER_CURL_SIGPIPE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if defined(HAVE_SIGACTION) && \ + (defined(USE_OPENSSL) || defined(USE_MBEDTLS) || defined(USE_WOLFSSL)) +#include + +struct sigpipe_ignore { + struct sigaction old_pipe_act; + bool no_signal; +}; + +#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x + +static void sigpipe_init(struct sigpipe_ignore *ig) +{ + memset(ig, 0, sizeof(*ig)); +} + +/* + * sigpipe_ignore() makes sure we ignore SIGPIPE while running libcurl + * internals, and then sigpipe_restore() will restore the situation when we + * return from libcurl again. + */ +static void sigpipe_ignore(struct Curl_easy *data, + struct sigpipe_ignore *ig) +{ + /* get a local copy of no_signal because the Curl_easy might not be + around when we restore */ + ig->no_signal = data->set.no_signal; + if(!data->set.no_signal) { + struct sigaction action; + /* first, extract the existing situation */ + sigaction(SIGPIPE, NULL, &ig->old_pipe_act); + action = ig->old_pipe_act; + /* ignore this signal */ + action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &action, NULL); + } +} + +/* + * sigpipe_restore() puts back the outside world's opinion of signal handler + * and SIGPIPE handling. It MUST only be called after a corresponding + * sigpipe_ignore() was used. + */ +static void sigpipe_restore(struct sigpipe_ignore *ig) +{ + if(!ig->no_signal) + /* restore the outside state */ + sigaction(SIGPIPE, &ig->old_pipe_act, NULL); +} + +static void sigpipe_apply(struct Curl_easy *data, + struct sigpipe_ignore *ig) +{ + if(data->set.no_signal != ig->no_signal) { + sigpipe_restore(ig); + sigpipe_ignore(data, ig); + } +} + +#else +/* for systems without sigaction */ +#define sigpipe_ignore(x,y) Curl_nop_stmt +#define sigpipe_apply(x,y) Curl_nop_stmt +#define sigpipe_init(x) Curl_nop_stmt +#define sigpipe_restore(x) Curl_nop_stmt +#define SIGPIPE_VARIABLE(x) +#endif + +#endif /* HEADER_CURL_SIGPIPE_H */ diff --git a/third_party/curl/lib/slist.c b/third_party/curl/lib/slist.c new file mode 100644 index 0000000000..366b247609 --- /dev/null +++ b/third_party/curl/lib/slist.c @@ -0,0 +1,146 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "slist.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* returns last node in linked list */ +static struct curl_slist *slist_get_last(struct curl_slist *list) +{ + struct curl_slist *item; + + /* if caller passed us a NULL, return now */ + if(!list) + return NULL; + + /* loop through to find the last item */ + item = list; + while(item->next) { + item = item->next; + } + return item; +} + +/* + * Curl_slist_append_nodup() appends a string to the linked list. Rather than + * copying the string in dynamic storage, it takes its ownership. The string + * should have been malloc()ated. Curl_slist_append_nodup always returns + * the address of the first record, so that you can use this function as an + * initialization function as well as an append function. + * If an error occurs, NULL is returned and the string argument is NOT + * released. + */ +struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, char *data) +{ + struct curl_slist *last; + struct curl_slist *new_item; + + DEBUGASSERT(data); + + new_item = malloc(sizeof(struct curl_slist)); + if(!new_item) + return NULL; + + new_item->next = NULL; + new_item->data = data; + + /* if this is the first item, then new_item *is* the list */ + if(!list) + return new_item; + + last = slist_get_last(list); + last->next = new_item; + return list; +} + +/* + * curl_slist_append() appends a string to the linked list. It always returns + * the address of the first record, so that you can use this function as an + * initialization function as well as an append function. If you find this + * bothersome, then simply create a separate _init function and call it + * appropriately from within the program. + */ +struct curl_slist *curl_slist_append(struct curl_slist *list, + const char *data) +{ + char *dupdata = strdup(data); + + if(!dupdata) + return NULL; + + list = Curl_slist_append_nodup(list, dupdata); + if(!list) + free(dupdata); + + return list; +} + +/* + * Curl_slist_duplicate() duplicates a linked list. It always returns the + * address of the first record of the cloned list or NULL in case of an + * error (or if the input list was NULL). + */ +struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist) +{ + struct curl_slist *outlist = NULL; + struct curl_slist *tmp; + + while(inlist) { + tmp = curl_slist_append(outlist, inlist->data); + + if(!tmp) { + curl_slist_free_all(outlist); + return NULL; + } + + outlist = tmp; + inlist = inlist->next; + } + return outlist; +} + +/* be nice and clean up resources */ +void curl_slist_free_all(struct curl_slist *list) +{ + struct curl_slist *next; + struct curl_slist *item; + + if(!list) + return; + + item = list; + do { + next = item->next; + Curl_safefree(item->data); + free(item); + item = next; + } while(next); +} diff --git a/third_party/curl/lib/slist.h b/third_party/curl/lib/slist.h new file mode 100644 index 0000000000..9561fd0226 --- /dev/null +++ b/third_party/curl/lib/slist.h @@ -0,0 +1,41 @@ +#ifndef HEADER_CURL_SLIST_H +#define HEADER_CURL_SLIST_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Curl_slist_duplicate() duplicates a linked list. It always returns the + * address of the first record of the cloned list or NULL in case of an + * error (or if the input list was NULL). + */ +struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist); + +/* + * Curl_slist_append_nodup() takes ownership of the given string and appends + * it to the list. + */ +struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, + char *data); + +#endif /* HEADER_CURL_SLIST_H */ diff --git a/third_party/curl/lib/smb.c b/third_party/curl/lib/smb.c new file mode 100644 index 0000000000..102662ace6 --- /dev/null +++ b/third_party/curl/lib/smb.c @@ -0,0 +1,1206 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Bill Nagel , Exacq Technologies + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) + +#ifdef _WIN32 +#define getpid GetCurrentProcessId +#endif + +#include "smb.h" +#include "urldata.h" +#include "sendf.h" +#include "multiif.h" +#include "cfilters.h" +#include "connect.h" +#include "progress.h" +#include "transfer.h" +#include "vtls/vtls.h" +#include "curl_ntlm_core.h" +#include "escape.h" +#include "curl_endian.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Definitions for SMB protocol data structures + */ +#if defined(_MSC_VER) || defined(__ILEC400__) +# define PACK +# pragma pack(push) +# pragma pack(1) +#elif defined(__GNUC__) +# define PACK __attribute__((packed)) +#else +# define PACK +#endif + +#define SMB_COM_CLOSE 0x04 +#define SMB_COM_READ_ANDX 0x2e +#define SMB_COM_WRITE_ANDX 0x2f +#define SMB_COM_TREE_DISCONNECT 0x71 +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SETUP_ANDX 0x73 +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_NT_CREATE_ANDX 0xa2 +#define SMB_COM_NO_ANDX_COMMAND 0xff + +#define SMB_WC_CLOSE 0x03 +#define SMB_WC_READ_ANDX 0x0c +#define SMB_WC_WRITE_ANDX 0x0e +#define SMB_WC_SETUP_ANDX 0x0d +#define SMB_WC_TREE_CONNECT_ANDX 0x04 +#define SMB_WC_NT_CREATE_ANDX 0x18 + +#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 +#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 +#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 +#define SMB_FLAGS2_IS_LONG_NAME 0x0040 +#define SMB_FLAGS2_KNOWS_LONG_NAME 0x0001 + +#define SMB_CAP_LARGE_FILES 0x08 +#define SMB_GENERIC_WRITE 0x40000000 +#define SMB_GENERIC_READ 0x80000000 +#define SMB_FILE_SHARE_ALL 0x07 +#define SMB_FILE_OPEN 0x01 +#define SMB_FILE_OVERWRITE_IF 0x05 + +#define SMB_ERR_NOACCESS 0x00050001 + +struct smb_header { + unsigned char nbt_type; + unsigned char nbt_flags; + unsigned short nbt_length; + unsigned char magic[4]; + unsigned char command; + unsigned int status; + unsigned char flags; + unsigned short flags2; + unsigned short pid_high; + unsigned char signature[8]; + unsigned short pad; + unsigned short tid; + unsigned short pid; + unsigned short uid; + unsigned short mid; +} PACK; + +struct smb_negotiate_response { + struct smb_header h; + unsigned char word_count; + unsigned short dialect_index; + unsigned char security_mode; + unsigned short max_mpx_count; + unsigned short max_number_vcs; + unsigned int max_buffer_size; + unsigned int max_raw_size; + unsigned int session_key; + unsigned int capabilities; + unsigned int system_time_low; + unsigned int system_time_high; + unsigned short server_time_zone; + unsigned char encryption_key_length; + unsigned short byte_count; + char bytes[1]; +} PACK; + +struct andx { + unsigned char command; + unsigned char pad; + unsigned short offset; +} PACK; + +struct smb_setup { + unsigned char word_count; + struct andx andx; + unsigned short max_buffer_size; + unsigned short max_mpx_count; + unsigned short vc_number; + unsigned int session_key; + unsigned short lengths[2]; + unsigned int pad; + unsigned int capabilities; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_tree_connect { + unsigned char word_count; + struct andx andx; + unsigned short flags; + unsigned short pw_len; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_nt_create { + unsigned char word_count; + struct andx andx; + unsigned char pad; + unsigned short name_length; + unsigned int flags; + unsigned int root_fid; + unsigned int access; + curl_off_t allocation_size; + unsigned int ext_file_attributes; + unsigned int share_access; + unsigned int create_disposition; + unsigned int create_options; + unsigned int impersonation_level; + unsigned char security_flags; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_nt_create_response { + struct smb_header h; + unsigned char word_count; + struct andx andx; + unsigned char op_lock_level; + unsigned short fid; + unsigned int create_disposition; + + curl_off_t create_time; + curl_off_t last_access_time; + curl_off_t last_write_time; + curl_off_t last_change_time; + unsigned int ext_file_attributes; + curl_off_t allocation_size; + curl_off_t end_of_file; +} PACK; + +struct smb_read { + unsigned char word_count; + struct andx andx; + unsigned short fid; + unsigned int offset; + unsigned short max_bytes; + unsigned short min_bytes; + unsigned int timeout; + unsigned short remaining; + unsigned int offset_high; + unsigned short byte_count; +} PACK; + +struct smb_write { + struct smb_header h; + unsigned char word_count; + struct andx andx; + unsigned short fid; + unsigned int offset; + unsigned int timeout; + unsigned short write_mode; + unsigned short remaining; + unsigned short pad; + unsigned short data_length; + unsigned short data_offset; + unsigned int offset_high; + unsigned short byte_count; + unsigned char pad2; +} PACK; + +struct smb_close { + unsigned char word_count; + unsigned short fid; + unsigned int last_mtime; + unsigned short byte_count; +} PACK; + +struct smb_tree_disconnect { + unsigned char word_count; + unsigned short byte_count; +} PACK; + +#if defined(_MSC_VER) || defined(__ILEC400__) +# pragma pack(pop) +#endif + +/* Local API functions */ +static CURLcode smb_setup_connection(struct Curl_easy *data, + struct connectdata *conn); +static CURLcode smb_connect(struct Curl_easy *data, bool *done); +static CURLcode smb_connection_state(struct Curl_easy *data, bool *done); +static CURLcode smb_do(struct Curl_easy *data, bool *done); +static CURLcode smb_request_state(struct Curl_easy *data, bool *done); +static CURLcode smb_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead); +static int smb_getsock(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t *socks); +static CURLcode smb_parse_url_path(struct Curl_easy *data, + struct connectdata *conn); + +/* + * SMB handler interface + */ +const struct Curl_handler Curl_handler_smb = { + "smb", /* scheme */ + smb_setup_connection, /* setup_connection */ + smb_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + smb_connect, /* connect_it */ + smb_connection_state, /* connecting */ + smb_request_state, /* doing */ + smb_getsock, /* proto_getsock */ + smb_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + smb_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_SMB, /* defport */ + CURLPROTO_SMB, /* protocol */ + CURLPROTO_SMB, /* family */ + PROTOPT_NONE /* flags */ +}; + +#ifdef USE_SSL +/* + * SMBS handler interface + */ +const struct Curl_handler Curl_handler_smbs = { + "smbs", /* scheme */ + smb_setup_connection, /* setup_connection */ + smb_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + smb_connect, /* connect_it */ + smb_connection_state, /* connecting */ + smb_request_state, /* doing */ + smb_getsock, /* proto_getsock */ + smb_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + smb_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_SMBS, /* defport */ + CURLPROTO_SMBS, /* protocol */ + CURLPROTO_SMB, /* family */ + PROTOPT_SSL /* flags */ +}; +#endif + +#define MAX_PAYLOAD_SIZE 0x8000 +#define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000) +#define CLIENTNAME "curl" +#define SERVICENAME "?????" + +/* Append a string to an SMB message */ +#define MSGCAT(str) \ + do { \ + strcpy(p, (str)); \ + p += strlen(str); \ + } while(0) + +/* Append a null-terminated string to an SMB message */ +#define MSGCATNULL(str) \ + do { \ + strcpy(p, (str)); \ + p += strlen(str) + 1; \ + } while(0) + +/* SMB is mostly little endian */ +#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ + defined(__OS400__) +static unsigned short smb_swap16(unsigned short x) +{ + return (unsigned short) ((x << 8) | ((x >> 8) & 0xff)); +} + +static unsigned int smb_swap32(unsigned int x) +{ + return (x << 24) | ((x << 8) & 0xff0000) | ((x >> 8) & 0xff00) | + ((x >> 24) & 0xff); +} + +static curl_off_t smb_swap64(curl_off_t x) +{ + return ((curl_off_t) smb_swap32((unsigned int) x) << 32) | + smb_swap32((unsigned int) (x >> 32)); +} + +#else +# define smb_swap16(x) (x) +# define smb_swap32(x) (x) +# define smb_swap64(x) (x) +#endif + +/* SMB request state */ +enum smb_req_state { + SMB_REQUESTING, + SMB_TREE_CONNECT, + SMB_OPEN, + SMB_DOWNLOAD, + SMB_UPLOAD, + SMB_CLOSE, + SMB_TREE_DISCONNECT, + SMB_DONE +}; + +/* SMB request data */ +struct smb_request { + enum smb_req_state state; + char *path; + unsigned short tid; /* Even if we connect to the same tree as another */ + unsigned short fid; /* request, the tid will be different */ + CURLcode result; +}; + +static void conn_state(struct Curl_easy *data, enum smb_conn_state newstate) +{ + struct smb_conn *smbc = &data->conn->proto.smbc; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* For debug purposes */ + static const char * const names[] = { + "SMB_NOT_CONNECTED", + "SMB_CONNECTING", + "SMB_NEGOTIATE", + "SMB_SETUP", + "SMB_CONNECTED", + /* LAST */ + }; + + if(smbc->state != newstate) + infof(data, "SMB conn %p state change from %s to %s", + (void *)smbc, names[smbc->state], names[newstate]); +#endif + + smbc->state = newstate; +} + +static void request_state(struct Curl_easy *data, + enum smb_req_state newstate) +{ + struct smb_request *req = data->req.p.smb; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* For debug purposes */ + static const char * const names[] = { + "SMB_REQUESTING", + "SMB_TREE_CONNECT", + "SMB_OPEN", + "SMB_DOWNLOAD", + "SMB_UPLOAD", + "SMB_CLOSE", + "SMB_TREE_DISCONNECT", + "SMB_DONE", + /* LAST */ + }; + + if(req->state != newstate) + infof(data, "SMB request %p state change from %s to %s", + (void *)req, names[req->state], names[newstate]); +#endif + + req->state = newstate; +} + +/* this should setup things in the connection, not in the easy + handle */ +static CURLcode smb_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + struct smb_request *req; + + /* Initialize the request state */ + data->req.p.smb = req = calloc(1, sizeof(struct smb_request)); + if(!req) + return CURLE_OUT_OF_MEMORY; + + /* Parse the URL path */ + return smb_parse_url_path(data, conn); +} + +static CURLcode smb_connect(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + char *slash; + + (void) done; + + /* Check we have a username and password to authenticate with */ + if(!data->state.aptr.user) + return CURLE_LOGIN_DENIED; + + /* Initialize the connection state */ + smbc->state = SMB_CONNECTING; + smbc->recv_buf = malloc(MAX_MESSAGE_SIZE); + if(!smbc->recv_buf) + return CURLE_OUT_OF_MEMORY; + smbc->send_buf = malloc(MAX_MESSAGE_SIZE); + if(!smbc->send_buf) + return CURLE_OUT_OF_MEMORY; + + /* Multiple requests are allowed with this connection */ + connkeep(conn, "SMB default"); + + /* Parse the username, domain, and password */ + slash = strchr(conn->user, '/'); + if(!slash) + slash = strchr(conn->user, '\\'); + + if(slash) { + smbc->user = slash + 1; + smbc->domain = strdup(conn->user); + if(!smbc->domain) + return CURLE_OUT_OF_MEMORY; + smbc->domain[slash - conn->user] = 0; + } + else { + smbc->user = conn->user; + smbc->domain = strdup(conn->host.name); + if(!smbc->domain) + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +static CURLcode smb_recv_message(struct Curl_easy *data, void **msg) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + char *buf = smbc->recv_buf; + ssize_t bytes_read; + size_t nbt_size; + size_t msg_size; + size_t len = MAX_MESSAGE_SIZE - smbc->got; + CURLcode result; + + result = Curl_xfer_recv(data, buf + smbc->got, len, &bytes_read); + if(result) + return result; + + if(!bytes_read) + return CURLE_OK; + + smbc->got += bytes_read; + + /* Check for a 32-bit nbt header */ + if(smbc->got < sizeof(unsigned int)) + return CURLE_OK; + + nbt_size = Curl_read16_be((const unsigned char *) + (buf + sizeof(unsigned short))) + + sizeof(unsigned int); + if(smbc->got < nbt_size) + return CURLE_OK; + + msg_size = sizeof(struct smb_header); + if(nbt_size >= msg_size + 1) { + /* Add the word count */ + msg_size += 1 + ((unsigned char) buf[msg_size]) * sizeof(unsigned short); + if(nbt_size >= msg_size + sizeof(unsigned short)) { + /* Add the byte count */ + msg_size += sizeof(unsigned short) + + Curl_read16_le((const unsigned char *)&buf[msg_size]); + if(nbt_size < msg_size) + return CURLE_READ_ERROR; + } + } + + *msg = buf; + + return CURLE_OK; +} + +static void smb_pop_message(struct connectdata *conn) +{ + struct smb_conn *smbc = &conn->proto.smbc; + + smbc->got = 0; +} + +static void smb_format_message(struct Curl_easy *data, struct smb_header *h, + unsigned char cmd, size_t len) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + struct smb_request *req = data->req.p.smb; + unsigned int pid; + + memset(h, 0, sizeof(*h)); + h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) + + len)); + memcpy((char *)h->magic, "\xffSMB", 4); + h->command = cmd; + h->flags = SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES; + h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME); + h->uid = smb_swap16(smbc->uid); + h->tid = smb_swap16(req->tid); + pid = (unsigned int)getpid(); + h->pid_high = smb_swap16((unsigned short)(pid >> 16)); + h->pid = smb_swap16((unsigned short) pid); +} + +static CURLcode smb_send(struct Curl_easy *data, size_t len, + size_t upload_size) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + size_t bytes_written; + CURLcode result; + + result = Curl_xfer_send(data, smbc->send_buf, len, &bytes_written); + if(result) + return result; + + if(bytes_written != len) { + smbc->send_size = len; + smbc->sent = bytes_written; + } + + smbc->upload_size = upload_size; + + return CURLE_OK; +} + +static CURLcode smb_flush(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + size_t bytes_written; + size_t len = smbc->send_size - smbc->sent; + CURLcode result; + + if(!smbc->send_size) + return CURLE_OK; + + result = Curl_xfer_send(data, smbc->send_buf + smbc->sent, len, + &bytes_written); + if(result) + return result; + + if(bytes_written != len) + smbc->sent += bytes_written; + else + smbc->send_size = 0; + + return CURLE_OK; +} + +static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd, + const void *msg, size_t msg_len) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + + smb_format_message(data, (struct smb_header *)smbc->send_buf, + cmd, msg_len); + DEBUGASSERT((sizeof(struct smb_header) + msg_len) <= MAX_MESSAGE_SIZE); + memcpy(smbc->send_buf + sizeof(struct smb_header), msg, msg_len); + + return smb_send(data, sizeof(struct smb_header) + msg_len, 0); +} + +static CURLcode smb_send_negotiate(struct Curl_easy *data) +{ + const char *msg = "\x00\x0c\x00\x02NT LM 0.12"; + + return smb_send_message(data, SMB_COM_NEGOTIATE, msg, 15); +} + +static CURLcode smb_send_setup(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + struct smb_setup msg; + char *p = msg.bytes; + unsigned char lm_hash[21]; + unsigned char lm[24]; + unsigned char nt_hash[21]; + unsigned char nt[24]; + + size_t byte_count = sizeof(lm) + sizeof(nt); + byte_count += strlen(smbc->user) + strlen(smbc->domain); + byte_count += strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */ + if(byte_count > sizeof(msg.bytes)) + return CURLE_FILESIZE_EXCEEDED; + + Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash); + Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm); + Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash); + Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt); + + memset(&msg, 0, sizeof(msg)); + msg.word_count = SMB_WC_SETUP_ANDX; + msg.andx.command = SMB_COM_NO_ANDX_COMMAND; + msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE); + msg.max_mpx_count = smb_swap16(1); + msg.vc_number = smb_swap16(1); + msg.session_key = smb_swap32(smbc->session_key); + msg.capabilities = smb_swap32(SMB_CAP_LARGE_FILES); + msg.lengths[0] = smb_swap16(sizeof(lm)); + msg.lengths[1] = smb_swap16(sizeof(nt)); + memcpy(p, lm, sizeof(lm)); + p += sizeof(lm); + memcpy(p, nt, sizeof(nt)); + p += sizeof(nt); + MSGCATNULL(smbc->user); + MSGCATNULL(smbc->domain); + MSGCATNULL(OS); + MSGCATNULL(CLIENTNAME); + byte_count = p - msg.bytes; + msg.byte_count = smb_swap16((unsigned short)byte_count); + + return smb_send_message(data, SMB_COM_SETUP_ANDX, &msg, + sizeof(msg) - sizeof(msg.bytes) + byte_count); +} + +static CURLcode smb_send_tree_connect(struct Curl_easy *data) +{ + struct smb_tree_connect msg; + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + char *p = msg.bytes; + + size_t byte_count = strlen(conn->host.name) + strlen(smbc->share); + byte_count += strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ + if(byte_count > sizeof(msg.bytes)) + return CURLE_FILESIZE_EXCEEDED; + + memset(&msg, 0, sizeof(msg)); + msg.word_count = SMB_WC_TREE_CONNECT_ANDX; + msg.andx.command = SMB_COM_NO_ANDX_COMMAND; + msg.pw_len = 0; + MSGCAT("\\\\"); + MSGCAT(conn->host.name); + MSGCAT("\\"); + MSGCATNULL(smbc->share); + MSGCATNULL(SERVICENAME); /* Match any type of service */ + byte_count = p - msg.bytes; + msg.byte_count = smb_swap16((unsigned short)byte_count); + + return smb_send_message(data, SMB_COM_TREE_CONNECT_ANDX, &msg, + sizeof(msg) - sizeof(msg.bytes) + byte_count); +} + +static CURLcode smb_send_open(struct Curl_easy *data) +{ + struct smb_request *req = data->req.p.smb; + struct smb_nt_create msg; + size_t byte_count; + + if((strlen(req->path) + 1) > sizeof(msg.bytes)) + return CURLE_FILESIZE_EXCEEDED; + + memset(&msg, 0, sizeof(msg)); + msg.word_count = SMB_WC_NT_CREATE_ANDX; + msg.andx.command = SMB_COM_NO_ANDX_COMMAND; + byte_count = strlen(req->path); + msg.name_length = smb_swap16((unsigned short)byte_count); + msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL); + if(data->state.upload) { + msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE); + msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF); + } + else { + msg.access = smb_swap32(SMB_GENERIC_READ); + msg.create_disposition = smb_swap32(SMB_FILE_OPEN); + } + msg.byte_count = smb_swap16((unsigned short) ++byte_count); + strcpy(msg.bytes, req->path); + + return smb_send_message(data, SMB_COM_NT_CREATE_ANDX, &msg, + sizeof(msg) - sizeof(msg.bytes) + byte_count); +} + +static CURLcode smb_send_close(struct Curl_easy *data) +{ + struct smb_request *req = data->req.p.smb; + struct smb_close msg; + + memset(&msg, 0, sizeof(msg)); + msg.word_count = SMB_WC_CLOSE; + msg.fid = smb_swap16(req->fid); + + return smb_send_message(data, SMB_COM_CLOSE, &msg, sizeof(msg)); +} + +static CURLcode smb_send_tree_disconnect(struct Curl_easy *data) +{ + struct smb_tree_disconnect msg; + + memset(&msg, 0, sizeof(msg)); + + return smb_send_message(data, SMB_COM_TREE_DISCONNECT, &msg, sizeof(msg)); +} + +static CURLcode smb_send_read(struct Curl_easy *data) +{ + struct smb_request *req = data->req.p.smb; + curl_off_t offset = data->req.offset; + struct smb_read msg; + + memset(&msg, 0, sizeof(msg)); + msg.word_count = SMB_WC_READ_ANDX; + msg.andx.command = SMB_COM_NO_ANDX_COMMAND; + msg.fid = smb_swap16(req->fid); + msg.offset = smb_swap32((unsigned int) offset); + msg.offset_high = smb_swap32((unsigned int) (offset >> 32)); + msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE); + msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE); + + return smb_send_message(data, SMB_COM_READ_ANDX, &msg, sizeof(msg)); +} + +static CURLcode smb_send_write(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + struct smb_write *msg; + struct smb_request *req = data->req.p.smb; + curl_off_t offset = data->req.offset; + curl_off_t upload_size = data->req.size - data->req.bytecount; + + msg = (struct smb_write *)smbc->send_buf; + if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */ + upload_size = MAX_PAYLOAD_SIZE - 1; + + memset(msg, 0, sizeof(*msg)); + msg->word_count = SMB_WC_WRITE_ANDX; + msg->andx.command = SMB_COM_NO_ANDX_COMMAND; + msg->fid = smb_swap16(req->fid); + msg->offset = smb_swap32((unsigned int) offset); + msg->offset_high = smb_swap32((unsigned int) (offset >> 32)); + msg->data_length = smb_swap16((unsigned short) upload_size); + msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int)); + msg->byte_count = smb_swap16((unsigned short) (upload_size + 1)); + + smb_format_message(data, &msg->h, SMB_COM_WRITE_ANDX, + sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size); + + return smb_send(data, sizeof(*msg), (size_t) upload_size); +} + +static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + CURLcode result; + *msg = NULL; /* if it returns early */ + + /* Check if there is data in the transfer buffer */ + if(!smbc->send_size && smbc->upload_size) { + size_t nread = smbc->upload_size > (size_t)MAX_MESSAGE_SIZE ? + (size_t)MAX_MESSAGE_SIZE : smbc->upload_size; + bool eos; + + result = Curl_client_read(data, smbc->send_buf, nread, &nread, &eos); + if(result && result != CURLE_AGAIN) + return result; + if(!nread) + return CURLE_OK; + + smbc->upload_size -= nread; + smbc->send_size = nread; + smbc->sent = 0; + } + + /* Check if there is data to send */ + if(smbc->send_size) { + result = smb_flush(data); + if(result) + return result; + } + + /* Check if there is still data to be sent */ + if(smbc->send_size || smbc->upload_size) + return CURLE_AGAIN; + + return smb_recv_message(data, msg); +} + +static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + struct smb_negotiate_response *nrsp; + struct smb_header *h; + CURLcode result; + void *msg = NULL; + + if(smbc->state == SMB_CONNECTING) { +#ifdef USE_SSL + if((conn->handler->flags & PROTOPT_SSL)) { + bool ssl_done = FALSE; + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssl_done); + if(result && result != CURLE_AGAIN) + return result; + if(!ssl_done) + return CURLE_OK; + } +#endif + + result = smb_send_negotiate(data); + if(result) { + connclose(conn, "SMB: failed to send negotiate message"); + return result; + } + + conn_state(data, SMB_NEGOTIATE); + } + + /* Send the previous message and check for a response */ + result = smb_send_and_recv(data, &msg); + if(result && result != CURLE_AGAIN) { + connclose(conn, "SMB: failed to communicate"); + return result; + } + + if(!msg) + return CURLE_OK; + + h = msg; + + switch(smbc->state) { + case SMB_NEGOTIATE: + if((smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) || + h->status) { + connclose(conn, "SMB: negotiation failed"); + return CURLE_COULDNT_CONNECT; + } + nrsp = msg; + memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge)); + smbc->session_key = smb_swap32(nrsp->session_key); + result = smb_send_setup(data); + if(result) { + connclose(conn, "SMB: failed to send setup message"); + return result; + } + conn_state(data, SMB_SETUP); + break; + + case SMB_SETUP: + if(h->status) { + connclose(conn, "SMB: authentication failed"); + return CURLE_LOGIN_DENIED; + } + smbc->uid = smb_swap16(h->uid); + conn_state(data, SMB_CONNECTED); + *done = true; + break; + + default: + smb_pop_message(conn); + return CURLE_OK; /* ignore */ + } + + smb_pop_message(conn); + + return CURLE_OK; +} + +/* + * Convert a timestamp from the Windows world (100 nsec units from 1 Jan 1601) + * to Posix time. Cap the output to fit within a time_t. + */ +static void get_posix_time(time_t *out, curl_off_t timestamp) +{ + timestamp -= 116444736000000000; + timestamp /= 10000000; +#if SIZEOF_TIME_T < SIZEOF_CURL_OFF_T + if(timestamp > TIME_T_MAX) + *out = TIME_T_MAX; + else if(timestamp < TIME_T_MIN) + *out = TIME_T_MIN; + else +#endif + *out = (time_t) timestamp; +} + +static CURLcode smb_request_state(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct smb_request *req = data->req.p.smb; + struct smb_header *h; + struct smb_conn *smbc = &conn->proto.smbc; + enum smb_req_state next_state = SMB_DONE; + unsigned short len; + unsigned short off; + CURLcode result; + void *msg = NULL; + const struct smb_nt_create_response *smb_m; + + if(data->state.upload && (data->state.infilesize < 0)) { + failf(data, "SMB upload needs to know the size up front"); + return CURLE_SEND_ERROR; + } + + /* Start the request */ + if(req->state == SMB_REQUESTING) { + result = smb_send_tree_connect(data); + if(result) { + connclose(conn, "SMB: failed to send tree connect message"); + return result; + } + + request_state(data, SMB_TREE_CONNECT); + } + + /* Send the previous message and check for a response */ + result = smb_send_and_recv(data, &msg); + if(result && result != CURLE_AGAIN) { + connclose(conn, "SMB: failed to communicate"); + return result; + } + + if(!msg) + return CURLE_OK; + + h = msg; + + switch(req->state) { + case SMB_TREE_CONNECT: + if(h->status) { + req->result = CURLE_REMOTE_FILE_NOT_FOUND; + if(h->status == smb_swap32(SMB_ERR_NOACCESS)) + req->result = CURLE_REMOTE_ACCESS_DENIED; + break; + } + req->tid = smb_swap16(h->tid); + next_state = SMB_OPEN; + break; + + case SMB_OPEN: + if(h->status || smbc->got < sizeof(struct smb_nt_create_response)) { + req->result = CURLE_REMOTE_FILE_NOT_FOUND; + if(h->status == smb_swap32(SMB_ERR_NOACCESS)) + req->result = CURLE_REMOTE_ACCESS_DENIED; + next_state = SMB_TREE_DISCONNECT; + break; + } + smb_m = (const struct smb_nt_create_response*) msg; + req->fid = smb_swap16(smb_m->fid); + data->req.offset = 0; + if(data->state.upload) { + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->req.size); + next_state = SMB_UPLOAD; + } + else { + data->req.size = smb_swap64(smb_m->end_of_file); + if(data->req.size < 0) { + req->result = CURLE_WEIRD_SERVER_REPLY; + next_state = SMB_CLOSE; + } + else { + Curl_pgrsSetDownloadSize(data, data->req.size); + if(data->set.get_filetime) + get_posix_time(&data->info.filetime, smb_m->last_change_time); + next_state = SMB_DOWNLOAD; + } + } + break; + + case SMB_DOWNLOAD: + if(h->status || smbc->got < sizeof(struct smb_header) + 14) { + req->result = CURLE_RECV_ERROR; + next_state = SMB_CLOSE; + break; + } + len = Curl_read16_le(((const unsigned char *) msg) + + sizeof(struct smb_header) + 11); + off = Curl_read16_le(((const unsigned char *) msg) + + sizeof(struct smb_header) + 13); + if(len > 0) { + if(off + sizeof(unsigned int) + len > smbc->got) { + failf(data, "Invalid input packet"); + result = CURLE_RECV_ERROR; + } + else + result = Curl_client_write(data, CLIENTWRITE_BODY, + (char *)msg + off + sizeof(unsigned int), + len); + if(result) { + req->result = result; + next_state = SMB_CLOSE; + break; + } + } + data->req.offset += len; + next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD; + break; + + case SMB_UPLOAD: + if(h->status || smbc->got < sizeof(struct smb_header) + 6) { + req->result = CURLE_UPLOAD_FAILED; + next_state = SMB_CLOSE; + break; + } + len = Curl_read16_le(((const unsigned char *) msg) + + sizeof(struct smb_header) + 5); + data->req.bytecount += len; + data->req.offset += len; + Curl_pgrsSetUploadCounter(data, data->req.bytecount); + if(data->req.bytecount >= data->req.size) + next_state = SMB_CLOSE; + else + next_state = SMB_UPLOAD; + break; + + case SMB_CLOSE: + /* We do not care if the close failed, proceed to tree disconnect anyway */ + next_state = SMB_TREE_DISCONNECT; + break; + + case SMB_TREE_DISCONNECT: + next_state = SMB_DONE; + break; + + default: + smb_pop_message(conn); + return CURLE_OK; /* ignore */ + } + + smb_pop_message(conn); + + switch(next_state) { + case SMB_OPEN: + result = smb_send_open(data); + break; + + case SMB_DOWNLOAD: + result = smb_send_read(data); + break; + + case SMB_UPLOAD: + result = smb_send_write(data); + break; + + case SMB_CLOSE: + result = smb_send_close(data); + break; + + case SMB_TREE_DISCONNECT: + result = smb_send_tree_disconnect(data); + break; + + case SMB_DONE: + result = req->result; + *done = true; + break; + + default: + break; + } + + if(result) { + connclose(conn, "SMB: failed to send message"); + return result; + } + + request_state(data, next_state); + + return CURLE_OK; +} + +static CURLcode smb_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead) +{ + struct smb_conn *smbc = &conn->proto.smbc; + (void) dead; + (void) data; + Curl_safefree(smbc->share); + Curl_safefree(smbc->domain); + Curl_safefree(smbc->recv_buf); + Curl_safefree(smbc->send_buf); + return CURLE_OK; +} + +static int smb_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks) +{ + (void)data; + socks[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); +} + +static CURLcode smb_do(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + + *done = FALSE; + if(smbc->share) { + return CURLE_OK; + } + return CURLE_URL_MALFORMAT; +} + +static CURLcode smb_parse_url_path(struct Curl_easy *data, + struct connectdata *conn) +{ + struct smb_request *req = data->req.p.smb; + struct smb_conn *smbc = &conn->proto.smbc; + char *path; + char *slash; + + /* URL decode the path */ + CURLcode result = Curl_urldecode(data->state.up.path, 0, &path, NULL, + REJECT_CTRL); + if(result) + return result; + + /* Parse the path for the share */ + smbc->share = strdup((*path == '/' || *path == '\\') ? path + 1 : path); + free(path); + if(!smbc->share) + return CURLE_OUT_OF_MEMORY; + + slash = strchr(smbc->share, '/'); + if(!slash) + slash = strchr(smbc->share, '\\'); + + /* The share must be present */ + if(!slash) { + Curl_safefree(smbc->share); + failf(data, "missing share in URL path for SMB"); + return CURLE_URL_MALFORMAT; + } + + /* Parse the path for the file path converting any forward slashes into + backslashes */ + *slash++ = 0; + req->path = slash; + + for(; *slash; slash++) { + if(*slash == '/') + *slash = '\\'; + } + return CURLE_OK; +} + +#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && + SIZEOF_CURL_OFF_T > 4 */ diff --git a/third_party/curl/lib/smb.h b/third_party/curl/lib/smb.h new file mode 100644 index 0000000000..9ea2a8cc31 --- /dev/null +++ b/third_party/curl/lib/smb.h @@ -0,0 +1,61 @@ +#ifndef HEADER_CURL_SMB_H +#define HEADER_CURL_SMB_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Bill Nagel , Exacq Technologies + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +enum smb_conn_state { + SMB_NOT_CONNECTED = 0, + SMB_CONNECTING, + SMB_NEGOTIATE, + SMB_SETUP, + SMB_CONNECTED +}; + +struct smb_conn { + enum smb_conn_state state; + char *user; + char *domain; + char *share; + unsigned char challenge[8]; + unsigned int session_key; + unsigned short uid; + char *recv_buf; + char *send_buf; + size_t upload_size; + size_t send_size; + size_t sent; + size_t got; +}; + +#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ + (SIZEOF_CURL_OFF_T > 4) + +extern const struct Curl_handler Curl_handler_smb; +extern const struct Curl_handler Curl_handler_smbs; + +#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && + SIZEOF_CURL_OFF_T > 4 */ + +#endif /* HEADER_CURL_SMB_H */ diff --git a/third_party/curl/lib/smtp.c b/third_party/curl/lib/smtp.c new file mode 100644 index 0000000000..5ee1b5ea37 --- /dev/null +++ b/third_party/curl/lib/smtp.c @@ -0,0 +1,1953 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC1870 SMTP Service Extension for Message Size + * RFC2195 CRAM-MD5 authentication + * RFC2831 DIGEST-MD5 authentication + * RFC3207 SMTP over TLS + * RFC4422 Simple Authentication and Security Layer (SASL) + * RFC4616 PLAIN authentication + * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism + * RFC4954 SMTP Authentication + * RFC5321 SMTP protocol + * RFC5890 Internationalized Domain Names for Applications (IDNA) + * RFC6531 SMTP Extension for Internationalized Email + * RFC6532 Internationalized Email Headers + * RFC6749 OAuth 2.0 Authorization Framework + * RFC8314 Use of TLS for Email Submission and Access + * Draft SMTP URL Interface + * Draft LOGIN SASL Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_SMTP + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "escape.h" +#include "http.h" /* for HTTP proxy tunnel stuff */ +#include "mime.h" +#include "socks.h" +#include "smtp.h" +#include "strtoofft.h" +#include "strcase.h" +#include "vtls/vtls.h" +#include "cfilters.h" +#include "connect.h" +#include "select.h" +#include "multiif.h" +#include "url.h" +#include "curl_gethostname.h" +#include "bufref.h" +#include "curl_sasl.h" +#include "warnless.h" +#include "idn.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* Local API functions */ +static CURLcode smtp_regular_transfer(struct Curl_easy *data, bool *done); +static CURLcode smtp_do(struct Curl_easy *data, bool *done); +static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, + bool premature); +static CURLcode smtp_connect(struct Curl_easy *data, bool *done); +static CURLcode smtp_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead); +static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done); +static int smtp_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); +static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done); +static CURLcode smtp_setup_connection(struct Curl_easy *data, + struct connectdata *conn); +static CURLcode smtp_parse_url_options(struct connectdata *conn); +static CURLcode smtp_parse_url_path(struct Curl_easy *data); +static CURLcode smtp_parse_custom_request(struct Curl_easy *data); +static CURLcode smtp_parse_address(const char *fqma, + char **address, struct hostname *host); +static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech, + const struct bufref *initresp); +static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech, + const struct bufref *resp); +static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech); +static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out); +static CURLcode cr_eob_add(struct Curl_easy *data); + +/* + * SMTP protocol handler. + */ + +const struct Curl_handler Curl_handler_smtp = { + "smtp", /* scheme */ + smtp_setup_connection, /* setup_connection */ + smtp_do, /* do_it */ + smtp_done, /* done */ + ZERO_NULL, /* do_more */ + smtp_connect, /* connect_it */ + smtp_multi_statemach, /* connecting */ + smtp_doing, /* doing */ + smtp_getsock, /* proto_getsock */ + smtp_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + smtp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_SMTP, /* defport */ + CURLPROTO_SMTP, /* protocol */ + CURLPROTO_SMTP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ + PROTOPT_URLOPTIONS +}; + +#ifdef USE_SSL +/* + * SMTPS protocol handler. + */ + +const struct Curl_handler Curl_handler_smtps = { + "smtps", /* scheme */ + smtp_setup_connection, /* setup_connection */ + smtp_do, /* do_it */ + smtp_done, /* done */ + ZERO_NULL, /* do_more */ + smtp_connect, /* connect_it */ + smtp_multi_statemach, /* connecting */ + smtp_doing, /* doing */ + smtp_getsock, /* proto_getsock */ + smtp_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + smtp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_SMTPS, /* defport */ + CURLPROTO_SMTPS, /* protocol */ + CURLPROTO_SMTP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL + | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */ +}; +#endif + +/* SASL parameters for the smtp protocol */ +static const struct SASLproto saslsmtp = { + "smtp", /* The service name */ + smtp_perform_auth, /* Send authentication command */ + smtp_continue_auth, /* Send authentication continuation */ + smtp_cancel_auth, /* Cancel authentication */ + smtp_get_message, /* Get SASL response message */ + 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ + 334, /* Code received when continuation is expected */ + 235, /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ +}; + +#ifdef USE_SSL +static void smtp_to_smtps(struct connectdata *conn) +{ + /* Change the connection handler */ + conn->handler = &Curl_handler_smtps; + + /* Set the connection's upgraded to TLS flag */ + conn->bits.tls_upgraded = TRUE; +} +#else +#define smtp_to_smtps(x) Curl_nop_stmt +#endif + +/*********************************************************************** + * + * smtp_endofresp() + * + * Checks for an ending SMTP status code at the start of the given string, but + * also detects various capabilities from the EHLO response including the + * supported authentication mechanisms. + */ +static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn, + char *line, size_t len, int *resp) +{ + struct smtp_conn *smtpc = &conn->proto.smtpc; + bool result = FALSE; + (void)data; + + /* Nothing for us */ + if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) + return FALSE; + + /* Do we have a command response? This should be the response code followed + by a space and optionally some text as per RFC-5321 and as outlined in + Section 4. Examples of RFC-4954 but some email servers ignore this and + only send the response code instead as per Section 4.2. */ + if(line[3] == ' ' || len == 5) { + char tmpline[6]; + + result = TRUE; + memset(tmpline, '\0', sizeof(tmpline)); + memcpy(tmpline, line, (len == 5 ? 5 : 3)); + *resp = curlx_sltosi(strtol(tmpline, NULL, 10)); + + /* Make sure real server never sends internal value */ + if(*resp == 1) + *resp = 0; + } + /* Do we have a multiline (continuation) response? */ + else if(line[3] == '-' && + (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { + result = TRUE; + *resp = 1; /* Internal response code */ + } + + return result; +} + +/*********************************************************************** + * + * smtp_get_message() + * + * Gets the authentication message from the response buffer. + */ +static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out) +{ + char *message = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf); + size_t len = data->conn->proto.smtpc.pp.nfinal; + + if(len > 4) { + /* Find the start of the message */ + len -= 4; + for(message += 4; *message == ' ' || *message == '\t'; message++, len--) + ; + + /* Find the end of the message */ + while(len--) + if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && + message[len] != '\t') + break; + + /* Terminate the message */ + message[++len] = '\0'; + Curl_bufref_set(out, message, len, NULL); + } + else + /* junk input => zero length output */ + Curl_bufref_set(out, "", 0, NULL); + + return CURLE_OK; +} + +/*********************************************************************** + * + * smtp_state() + * + * This is the ONLY way to change SMTP state! + */ +static void smtp_state(struct Curl_easy *data, smtpstate newstate) +{ + struct smtp_conn *smtpc = &data->conn->proto.smtpc; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char * const names[] = { + "STOP", + "SERVERGREET", + "EHLO", + "HELO", + "STARTTLS", + "UPGRADETLS", + "AUTH", + "COMMAND", + "MAIL", + "RCPT", + "DATA", + "POSTDATA", + "QUIT", + /* LAST */ + }; + + if(smtpc->state != newstate) + infof(data, "SMTP %p state change from %s to %s", + (void *)smtpc, names[smtpc->state], names[newstate]); +#endif + + smtpc->state = newstate; +} + +/*********************************************************************** + * + * smtp_perform_ehlo() + * + * Sends the EHLO command to not only initialise communication with the ESMTP + * server but to also obtain a list of server side supported capabilities. + */ +static CURLcode smtp_perform_ehlo(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct smtp_conn *smtpc = &conn->proto.smtpc; + + smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */ + smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism + used for esmtp connections */ + smtpc->tls_supported = FALSE; /* Clear the TLS capability */ + smtpc->auth_supported = FALSE; /* Clear the AUTH capability */ + + /* Send the EHLO command */ + result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain); + + if(!result) + smtp_state(data, SMTP_EHLO); + + return result; +} + +/*********************************************************************** + * + * smtp_perform_helo() + * + * Sends the HELO command to initialise communication with the SMTP server. + */ +static CURLcode smtp_perform_helo(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct smtp_conn *smtpc = &conn->proto.smtpc; + + smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used + in smtp connections */ + + /* Send the HELO command */ + result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain); + + if(!result) + smtp_state(data, SMTP_HELO); + + return result; +} + +/*********************************************************************** + * + * smtp_perform_starttls() + * + * Sends the STLS command to start the upgrade to TLS. + */ +static CURLcode smtp_perform_starttls(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Send the STARTTLS command */ + CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, + "%s", "STARTTLS"); + + if(!result) + smtp_state(data, SMTP_STARTTLS); + + return result; +} + +/*********************************************************************** + * + * smtp_perform_upgrade_tls() + * + * Performs the upgrade to TLS. + */ +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; + bool ssldone = FALSE; + + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + if(result) + goto out; + } + + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + if(!result) { + smtpc->ssldone = ssldone; + if(smtpc->state != SMTP_UPGRADETLS) + smtp_state(data, SMTP_UPGRADETLS); + + if(smtpc->ssldone) { + smtp_to_smtps(conn); + result = smtp_perform_ehlo(data); + } + } +out: + return result; +} + +/*********************************************************************** + * + * smtp_perform_auth() + * + * Sends an AUTH command allowing the client to login with the given SASL + * authentication mechanism. + */ +static CURLcode smtp_perform_auth(struct Curl_easy *data, + const char *mech, + const struct bufref *initresp) +{ + CURLcode result = CURLE_OK; + struct smtp_conn *smtpc = &data->conn->proto.smtpc; + const char *ir = (const char *) Curl_bufref_ptr(initresp); + + if(ir) { /* AUTH ... */ + /* Send the AUTH command with the initial response */ + result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir); + } + else { + /* Send the AUTH command */ + result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech); + } + + return result; +} + +/*********************************************************************** + * + * smtp_continue_auth() + * + * Sends SASL continuation data. + */ +static CURLcode smtp_continue_auth(struct Curl_easy *data, + const char *mech, + const struct bufref *resp) +{ + struct smtp_conn *smtpc = &data->conn->proto.smtpc; + + (void)mech; + + return Curl_pp_sendf(data, &smtpc->pp, + "%s", (const char *) Curl_bufref_ptr(resp)); +} + +/*********************************************************************** + * + * smtp_cancel_auth() + * + * Sends SASL cancellation. + */ +static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech) +{ + struct smtp_conn *smtpc = &data->conn->proto.smtpc; + + (void)mech; + + return Curl_pp_sendf(data, &smtpc->pp, "*"); +} + +/*********************************************************************** + * + * smtp_perform_authentication() + * + * Initiates the authentication sequence, with the appropriate SASL + * authentication mechanism. + */ +static CURLcode smtp_perform_authentication(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct smtp_conn *smtpc = &conn->proto.smtpc; + saslprogress progress; + + /* Check we have enough data to authenticate with, and the + server supports authentication, and end the connect phase if not */ + if(!smtpc->auth_supported || + !Curl_sasl_can_authenticate(&smtpc->sasl, data)) { + smtp_state(data, SMTP_STOP); + return result; + } + + /* Calculate the SASL login details */ + result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress); + + if(!result) { + if(progress == SASL_INPROGRESS) + smtp_state(data, SMTP_AUTH); + else { + /* Other mechanisms not supported */ + infof(data, "No known authentication mechanisms supported"); + result = CURLE_LOGIN_DENIED; + } + } + + return result; +} + +/*********************************************************************** + * + * smtp_perform_command() + * + * Sends a SMTP based command. + */ +static CURLcode smtp_perform_command(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct SMTP *smtp = data->req.p.smtp; + + if(smtp->rcpt) { + /* We notify the server we are sending UTF-8 data if a) it supports the + SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in + either the local address or hostname parts. This is regardless of + whether the hostname is encoded using IDN ACE */ + bool utf8 = FALSE; + + if((!smtp->custom) || (!smtp->custom[0])) { + char *address = NULL; + struct hostname host = { NULL, NULL, NULL, NULL }; + + /* Parse the mailbox to verify into the local address and hostname + parts, converting the hostname to an IDN A-label if necessary */ + result = smtp_parse_address(smtp->rcpt->data, + &address, &host); + if(result) + return result; + + /* Establish whether we should report SMTPUTF8 to the server for this + mailbox as per RFC-6531 sect. 3.1 point 6 */ + utf8 = (conn->proto.smtpc.utf8_supported) && + ((host.encalloc) || (!Curl_is_ASCII_name(address)) || + (!Curl_is_ASCII_name(host.name))); + + /* Send the VRFY command (Note: The hostname part may be absent when the + host is a local system) */ + result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s", + address, + host.name ? "@" : "", + host.name ? host.name : "", + utf8 ? " SMTPUTF8" : ""); + + Curl_free_idnconverted_hostname(&host); + free(address); + } + else { + /* Establish whether we should report that we support SMTPUTF8 for EXPN + commands to the server as per RFC-6531 sect. 3.1 point 6 */ + utf8 = (conn->proto.smtpc.utf8_supported) && + (!strcmp(smtp->custom, "EXPN")); + + /* Send the custom recipient based command such as the EXPN command */ + result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, + "%s %s%s", smtp->custom, + smtp->rcpt->data, + utf8 ? " SMTPUTF8" : ""); + } + } + else + /* Send the non-recipient based command such as HELP */ + result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", + smtp->custom && smtp->custom[0] != '\0' ? + smtp->custom : "HELP"); + + if(!result) + smtp_state(data, SMTP_COMMAND); + + return result; +} + +/*********************************************************************** + * + * smtp_perform_mail() + * + * Sends an MAIL command to initiate the upload of a message. + */ +static CURLcode smtp_perform_mail(struct Curl_easy *data) +{ + char *from = NULL; + char *auth = NULL; + char *size = NULL; + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + /* We notify the server we are sending UTF-8 data if a) it supports the + SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in + either the local address or hostname parts. This is regardless of + whether the hostname is encoded using IDN ACE */ + bool utf8 = FALSE; + + /* Calculate the FROM parameter */ + if(data->set.str[STRING_MAIL_FROM]) { + char *address = NULL; + struct hostname host = { NULL, NULL, NULL, NULL }; + + /* Parse the FROM mailbox into the local address and hostname parts, + converting the hostname to an IDN A-label if necessary */ + result = smtp_parse_address(data->set.str[STRING_MAIL_FROM], + &address, &host); + if(result) + goto out; + + /* Establish whether we should report SMTPUTF8 to the server for this + mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ + utf8 = (conn->proto.smtpc.utf8_supported) && + ((host.encalloc) || (!Curl_is_ASCII_name(address)) || + (!Curl_is_ASCII_name(host.name))); + + if(host.name) { + from = aprintf("<%s@%s>", address, host.name); + + Curl_free_idnconverted_hostname(&host); + } + else + /* An invalid mailbox was provided but we will simply let the server + worry about that and reply with a 501 error */ + from = aprintf("<%s>", address); + + free(address); + } + else + /* Null reverse-path, RFC-5321, sect. 3.6.3 */ + from = strdup("<>"); + + if(!from) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + /* Calculate the optional AUTH parameter */ + if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) { + if(data->set.str[STRING_MAIL_AUTH][0] != '\0') { + char *address = NULL; + struct hostname host = { NULL, NULL, NULL, NULL }; + + /* Parse the AUTH mailbox into the local address and hostname parts, + converting the hostname to an IDN A-label if necessary */ + result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH], + &address, &host); + if(result) + goto out; + + /* Establish whether we should report SMTPUTF8 to the server for this + mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ + if((!utf8) && (conn->proto.smtpc.utf8_supported) && + ((host.encalloc) || (!Curl_is_ASCII_name(address)) || + (!Curl_is_ASCII_name(host.name)))) + utf8 = TRUE; + + if(host.name) { + auth = aprintf("<%s@%s>", address, host.name); + + Curl_free_idnconverted_hostname(&host); + } + else + /* An invalid mailbox was provided but we will simply let the server + worry about it */ + auth = aprintf("<%s>", address); + free(address); + } + else + /* Empty AUTH, RFC-2554, sect. 5 */ + auth = strdup("<>"); + + if(!auth) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + +#ifndef CURL_DISABLE_MIME + /* Prepare the mime data if some. */ + if(data->set.mimepost.kind != MIMEKIND_NONE) { + /* Use the whole structure as data. */ + data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY; + + /* Add external headers and mime version. */ + curl_mime_headers(&data->set.mimepost, data->set.headers, 0); + result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL, + NULL, MIMESTRATEGY_MAIL); + + if(!result) + if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) + result = Curl_mime_add_header(&data->set.mimepost.curlheaders, + "Mime-Version: 1.0"); + + if(!result) + result = Curl_creader_set_mime(data, &data->set.mimepost); + if(result) + goto out; + data->state.infilesize = Curl_creader_total_length(data); + } + else +#endif + { + result = Curl_creader_set_fread(data, data->state.infilesize); + if(result) + goto out; + } + + /* Calculate the optional SIZE parameter */ + if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) { + size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize); + + if(!size) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + + /* If the mailboxes in the FROM and AUTH parameters do not include a UTF-8 + based address then quickly scan through the recipient list and check if + any there do, as we need to correctly identify our support for SMTPUTF8 + in the envelope, as per RFC-6531 sect. 3.4 */ + if(conn->proto.smtpc.utf8_supported && !utf8) { + struct SMTP *smtp = data->req.p.smtp; + struct curl_slist *rcpt = smtp->rcpt; + + while(rcpt && !utf8) { + /* Does the hostname contain non-ASCII characters? */ + if(!Curl_is_ASCII_name(rcpt->data)) + utf8 = TRUE; + + rcpt = rcpt->next; + } + } + + /* Add the client reader doing STMP EOB escaping */ + result = cr_eob_add(data); + if(result) + goto out; + + /* Send the MAIL command */ + result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, + "MAIL FROM:%s%s%s%s%s%s", + from, /* Mandatory */ + auth ? " AUTH=" : "", /* Optional on AUTH support */ + auth ? auth : "", /* */ + size ? " SIZE=" : "", /* Optional on SIZE support */ + size ? size : "", /* */ + utf8 ? " SMTPUTF8" /* Internationalised mailbox */ + : ""); /* included in our envelope */ + +out: + free(from); + free(auth); + free(size); + + if(!result) + smtp_state(data, SMTP_MAIL); + + return result; +} + +/*********************************************************************** + * + * smtp_perform_rcpt_to() + * + * Sends a RCPT TO command for a given recipient as part of the message upload + * process. + */ +static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct SMTP *smtp = data->req.p.smtp; + char *address = NULL; + struct hostname host = { NULL, NULL, NULL, NULL }; + + /* Parse the recipient mailbox into the local address and hostname parts, + converting the hostname to an IDN A-label if necessary */ + result = smtp_parse_address(smtp->rcpt->data, + &address, &host); + if(result) + return result; + + /* Send the RCPT TO command */ + if(host.name) + result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>", + address, host.name); + else + /* An invalid mailbox was provided but we will simply let the server worry + about that and reply with a 501 error */ + result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>", + address); + + Curl_free_idnconverted_hostname(&host); + free(address); + + if(!result) + smtp_state(data, SMTP_RCPT); + + return result; +} + +/*********************************************************************** + * + * smtp_perform_quit() + * + * Performs the quit action prior to sclose() being called. + */ +static CURLcode smtp_perform_quit(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Send the QUIT command */ + CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT"); + + if(!result) + smtp_state(data, SMTP_QUIT); + + return result; +} + +/* For the initial server greeting */ +static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data, + int smtpcode, + smtpstate instate) +{ + CURLcode result = CURLE_OK; + (void)instate; /* no use for this yet */ + + if(smtpcode/100 != 2) { + failf(data, "Got unexpected smtp-server response: %d", smtpcode); + result = CURLE_WEIRD_SERVER_REPLY; + } + else + result = smtp_perform_ehlo(data); + + return result; +} + +/* For STARTTLS responses */ +static CURLcode smtp_state_starttls_resp(struct Curl_easy *data, + int smtpcode, + smtpstate instate) +{ + CURLcode result = CURLE_OK; + (void)instate; /* no use for this yet */ + + /* Pipelining in response is forbidden. */ + if(data->conn->proto.smtpc.pp.overflow) + return CURLE_WEIRD_SERVER_REPLY; + + if(smtpcode != 220) { + if(data->set.use_ssl != CURLUSESSL_TRY) { + failf(data, "STARTTLS denied, code %d", smtpcode); + result = CURLE_USE_SSL_FAILED; + } + else + result = smtp_perform_authentication(data); + } + else + result = smtp_perform_upgrade_tls(data); + + return result; +} + +/* For EHLO responses */ +static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, + struct connectdata *conn, int smtpcode, + smtpstate instate) +{ + CURLcode result = CURLE_OK; + struct smtp_conn *smtpc = &conn->proto.smtpc; + const char *line = Curl_dyn_ptr(&smtpc->pp.recvbuf); + size_t len = smtpc->pp.nfinal; + + (void)instate; /* no use for this yet */ + + if(smtpcode/100 != 2 && smtpcode != 1) { + if(data->set.use_ssl <= CURLUSESSL_TRY + || Curl_conn_is_ssl(conn, FIRSTSOCKET)) + result = smtp_perform_helo(data, conn); + else { + failf(data, "Remote access denied: %d", smtpcode); + result = CURLE_REMOTE_ACCESS_DENIED; + } + } + else if(len >= 4) { + line += 4; + len -= 4; + + /* Does the server support the STARTTLS capability? */ + if(len >= 8 && !memcmp(line, "STARTTLS", 8)) + smtpc->tls_supported = TRUE; + + /* Does the server support the SIZE capability? */ + else if(len >= 4 && !memcmp(line, "SIZE", 4)) + smtpc->size_supported = TRUE; + + /* Does the server support the UTF-8 capability? */ + else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8)) + smtpc->utf8_supported = TRUE; + + /* Does the server support authentication? */ + else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { + smtpc->auth_supported = TRUE; + + /* Advance past the AUTH keyword */ + line += 5; + len -= 5; + + /* Loop through the data line */ + for(;;) { + size_t llen; + size_t wordlen; + unsigned short mechbit; + + while(len && + (*line == ' ' || *line == '\t' || + *line == '\r' || *line == '\n')) { + + line++; + len--; + } + + if(!len) + break; + + /* Extract the word */ + for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && + line[wordlen] != '\t' && line[wordlen] != '\r' && + line[wordlen] != '\n';) + wordlen++; + + /* Test the word for a matching authentication mechanism */ + mechbit = Curl_sasl_decode_mech(line, wordlen, &llen); + if(mechbit && llen == wordlen) + smtpc->sasl.authmechs |= mechbit; + + line += wordlen; + len -= wordlen; + } + } + + if(smtpcode != 1) { + if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + /* We do not have a SSL/TLS connection yet, but SSL is requested */ + if(smtpc->tls_supported) + /* Switch to TLS connection now */ + result = smtp_perform_starttls(data, conn); + else if(data->set.use_ssl == CURLUSESSL_TRY) + /* Fallback and carry on with authentication */ + result = smtp_perform_authentication(data); + else { + failf(data, "STARTTLS not supported."); + result = CURLE_USE_SSL_FAILED; + } + } + else + result = smtp_perform_authentication(data); + } + } + else { + failf(data, "Unexpectedly short EHLO response"); + result = CURLE_WEIRD_SERVER_REPLY; + } + + return result; +} + +/* For HELO responses */ +static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode, + smtpstate instate) +{ + CURLcode result = CURLE_OK; + (void)instate; /* no use for this yet */ + + if(smtpcode/100 != 2) { + failf(data, "Remote access denied: %d", smtpcode); + result = CURLE_REMOTE_ACCESS_DENIED; + } + else + /* End of connect phase */ + smtp_state(data, SMTP_STOP); + + return result; +} + +/* For SASL authentication responses */ +static CURLcode smtp_state_auth_resp(struct Curl_easy *data, + int smtpcode, + smtpstate instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct smtp_conn *smtpc = &conn->proto.smtpc; + saslprogress progress; + + (void)instate; /* no use for this yet */ + + result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress); + if(!result) + switch(progress) { + case SASL_DONE: + smtp_state(data, SMTP_STOP); /* Authenticated */ + break; + case SASL_IDLE: /* No mechanism left after cancellation */ + failf(data, "Authentication cancelled"); + result = CURLE_LOGIN_DENIED; + break; + default: + break; + } + + return result; +} + +/* For command responses */ +static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode, + smtpstate instate) +{ + CURLcode result = CURLE_OK; + struct SMTP *smtp = data->req.p.smtp; + char *line = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf); + size_t len = data->conn->proto.smtpc.pp.nfinal; + + (void)instate; /* no use for this yet */ + + if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) || + (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) { + failf(data, "Command failed: %d", smtpcode); + result = CURLE_WEIRD_SERVER_REPLY; + } + else { + if(!data->req.no_body) + result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); + + if(smtpcode != 1) { + if(smtp->rcpt) { + smtp->rcpt = smtp->rcpt->next; + + if(smtp->rcpt) { + /* Send the next command */ + result = smtp_perform_command(data); + } + else + /* End of DO phase */ + smtp_state(data, SMTP_STOP); + } + else + /* End of DO phase */ + smtp_state(data, SMTP_STOP); + } + } + + return result; +} + +/* For MAIL responses */ +static CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode, + smtpstate instate) +{ + CURLcode result = CURLE_OK; + (void)instate; /* no use for this yet */ + + if(smtpcode/100 != 2) { + failf(data, "MAIL failed: %d", smtpcode); + result = CURLE_SEND_ERROR; + } + else + /* Start the RCPT TO command */ + result = smtp_perform_rcpt_to(data); + + return result; +} + +/* For RCPT responses */ +static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data, + struct connectdata *conn, int smtpcode, + smtpstate instate) +{ + CURLcode result = CURLE_OK; + struct SMTP *smtp = data->req.p.smtp; + bool is_smtp_err = FALSE; + bool is_smtp_blocking_err = FALSE; + + (void)instate; /* no use for this yet */ + + is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE; + + /* If there is multiple RCPT TO to be issued, it is possible to ignore errors + and proceed with only the valid addresses. */ + is_smtp_blocking_err = + (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE; + + if(is_smtp_err) { + /* Remembering the last failure which we can report if all "RCPT TO" have + failed and we cannot proceed. */ + smtp->rcpt_last_error = smtpcode; + + if(is_smtp_blocking_err) { + failf(data, "RCPT failed: %d", smtpcode); + result = CURLE_SEND_ERROR; + } + } + else { + /* Some RCPT TO commands have succeeded. */ + smtp->rcpt_had_ok = TRUE; + } + + if(!is_smtp_blocking_err) { + smtp->rcpt = smtp->rcpt->next; + + if(smtp->rcpt) + /* Send the next RCPT TO command */ + result = smtp_perform_rcpt_to(data); + else { + /* We were not able to issue a successful RCPT TO command while going + over recipients (potentially multiple). Sending back last error. */ + if(!smtp->rcpt_had_ok) { + failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error); + result = CURLE_SEND_ERROR; + } + else { + /* Send the DATA command */ + result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA"); + + if(!result) + smtp_state(data, SMTP_DATA); + } + } + } + + return result; +} + +/* For DATA response */ +static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode, + smtpstate instate) +{ + CURLcode result = CURLE_OK; + (void)instate; /* no use for this yet */ + + if(smtpcode != 354) { + failf(data, "DATA failed: %d", smtpcode); + result = CURLE_SEND_ERROR; + } + else { + /* Set the progress upload size */ + Curl_pgrsSetUploadSize(data, data->state.infilesize); + + /* SMTP upload */ + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); + + /* End of DO phase */ + smtp_state(data, SMTP_STOP); + } + + return result; +} + +/* For POSTDATA responses, which are received after the entire DATA + part has been sent to the server */ +static CURLcode smtp_state_postdata_resp(struct Curl_easy *data, + int smtpcode, + smtpstate instate) +{ + CURLcode result = CURLE_OK; + + (void)instate; /* no use for this yet */ + + if(smtpcode != 250) + result = CURLE_WEIRD_SERVER_REPLY; + + /* End of DONE phase */ + smtp_state(data, SMTP_STOP); + + return result; +} + +static CURLcode smtp_statemachine(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + int smtpcode; + struct smtp_conn *smtpc = &conn->proto.smtpc; + struct pingpong *pp = &smtpc->pp; + size_t nread = 0; + + /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */ +upgrade_tls: + if(smtpc->state == SMTP_UPGRADETLS) + return smtp_perform_upgrade_tls(data); + + /* Flush any data that needs to be sent */ + if(pp->sendleft) + return Curl_pp_flushsend(data, pp); + + do { + /* Read the response from the server */ + result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &smtpcode, &nread); + if(result) + return result; + + /* Store the latest response for later retrieval if necessary */ + if(smtpc->state != SMTP_QUIT && smtpcode != 1) + data->info.httpcode = smtpcode; + + if(!smtpcode) + break; + + /* We have now received a full SMTP server response */ + switch(smtpc->state) { + case SMTP_SERVERGREET: + result = smtp_state_servergreet_resp(data, smtpcode, smtpc->state); + break; + + case SMTP_EHLO: + result = smtp_state_ehlo_resp(data, conn, smtpcode, smtpc->state); + break; + + case SMTP_HELO: + result = smtp_state_helo_resp(data, smtpcode, smtpc->state); + break; + + case SMTP_STARTTLS: + result = smtp_state_starttls_resp(data, smtpcode, smtpc->state); + /* During UPGRADETLS, leave the read loop as we need to connect + * (e.g. TLS handshake) before we continue sending/receiving. */ + if(!result && (smtpc->state == SMTP_UPGRADETLS)) + goto upgrade_tls; + break; + + case SMTP_AUTH: + result = smtp_state_auth_resp(data, smtpcode, smtpc->state); + break; + + case SMTP_COMMAND: + result = smtp_state_command_resp(data, smtpcode, smtpc->state); + break; + + case SMTP_MAIL: + result = smtp_state_mail_resp(data, smtpcode, smtpc->state); + break; + + case SMTP_RCPT: + result = smtp_state_rcpt_resp(data, conn, smtpcode, smtpc->state); + break; + + case SMTP_DATA: + result = smtp_state_data_resp(data, smtpcode, smtpc->state); + break; + + case SMTP_POSTDATA: + result = smtp_state_postdata_resp(data, smtpcode, smtpc->state); + break; + + case SMTP_QUIT: + default: + /* internal error */ + smtp_state(data, SMTP_STOP); + break; + } + } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp)); + + return result; +} + +/* Called repeatedly until done from multi.c */ +static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct smtp_conn *smtpc = &conn->proto.smtpc; + + if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { + bool ssldone = FALSE; + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + smtpc->ssldone = ssldone; + if(result || !smtpc->ssldone) + return result; + } + + result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE); + *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE; + + return result; +} + +static CURLcode smtp_block_statemach(struct Curl_easy *data, + struct connectdata *conn, + bool disconnecting) +{ + CURLcode result = CURLE_OK; + struct smtp_conn *smtpc = &conn->proto.smtpc; + + while(smtpc->state != SMTP_STOP && !result) + result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting); + + return result; +} + +/* Allocate and initialize the SMTP struct for the current Curl_easy if + required */ +static CURLcode smtp_init(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct SMTP *smtp; + + smtp = data->req.p.smtp = calloc(1, sizeof(struct SMTP)); + if(!smtp) + result = CURLE_OUT_OF_MEMORY; + + return result; +} + +/* For the SMTP "protocol connect" and "doing" phases only */ +static int smtp_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks) +{ + return Curl_pp_getsock(data, &conn->proto.smtpc.pp, socks); +} + +/*********************************************************************** + * + * smtp_connect() + * + * This function should do everything that is to be considered a part of + * the connection phase. + * + * The variable pointed to by 'done' will be TRUE if the protocol-layer + * connect phase is done when this function returns, or FALSE if not. + */ +static CURLcode smtp_connect(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct smtp_conn *smtpc = &conn->proto.smtpc; + struct pingpong *pp = &smtpc->pp; + + *done = FALSE; /* default to not done yet */ + + /* We always support persistent connections in SMTP */ + connkeep(conn, "SMTP default"); + + PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp); + + /* Initialize the SASL storage */ + Curl_sasl_init(&smtpc->sasl, data, &saslsmtp); + + /* Initialise the pingpong layer */ + Curl_pp_init(pp); + + /* Parse the URL options */ + result = smtp_parse_url_options(conn); + if(result) + return result; + + /* Parse the URL path */ + result = smtp_parse_url_path(data); + if(result) + return result; + + /* Start off waiting for the server greeting response */ + smtp_state(data, SMTP_SERVERGREET); + + result = smtp_multi_statemach(data, done); + + return result; +} + +/*********************************************************************** + * + * smtp_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct SMTP *smtp = data->req.p.smtp; + + (void)premature; + + if(!smtp) + return CURLE_OK; + + /* Cleanup our per-request based variables */ + Curl_safefree(smtp->custom); + + if(status) { + connclose(conn, "SMTP done with bad status"); /* marked for closure */ + result = status; /* use the already set error code */ + } + else if(!data->set.connect_only && data->set.mail_rcpt && + (data->state.upload || IS_MIME_POST(data))) { + + smtp_state(data, SMTP_POSTDATA); + + /* Run the state-machine */ + result = smtp_block_statemach(data, conn, FALSE); + } + + /* Clear the transfer mode for the next request */ + smtp->transfer = PPTRANSFER_BODY; + + return result; +} + +/*********************************************************************** + * + * smtp_perform() + * + * This is the actual DO function for SMTP. Transfer a mail, send a command + * or get some data according to the options previously setup. + */ +static CURLcode smtp_perform(struct Curl_easy *data, bool *connected, + bool *dophase_done) +{ + /* This is SMTP and no proxy */ + CURLcode result = CURLE_OK; + struct SMTP *smtp = data->req.p.smtp; + + DEBUGF(infof(data, "DO phase starts")); + + if(data->req.no_body) { + /* Requested no body means no transfer */ + smtp->transfer = PPTRANSFER_INFO; + } + + *dophase_done = FALSE; /* not done yet */ + + /* Store the first recipient (or NULL if not specified) */ + smtp->rcpt = data->set.mail_rcpt; + + /* Track of whether we have successfully sent at least one RCPT TO command */ + smtp->rcpt_had_ok = FALSE; + + /* Track of the last error we have received by sending RCPT TO command */ + smtp->rcpt_last_error = 0; + + /* Initial data character is the first character in line: it is implicitly + preceded by a virtual CRLF. */ + smtp->trailing_crlf = TRUE; + smtp->eob = 2; + + /* Start the first command in the DO phase */ + if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt) + /* MAIL transfer */ + result = smtp_perform_mail(data); + else + /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */ + result = smtp_perform_command(data); + + if(result) + return result; + + /* Run the state-machine */ + result = smtp_multi_statemach(data, dophase_done); + + *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + + if(*dophase_done) + DEBUGF(infof(data, "DO phase is complete")); + + return result; +} + +/*********************************************************************** + * + * smtp_do() + * + * This function is registered as 'curl_do' function. It decodes the path + * parts etc as a wrapper to the actual DO function (smtp_perform). + * + * The input argument is already checked for validity. + */ +static CURLcode smtp_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + *done = FALSE; /* default to false */ + + /* Parse the custom request */ + result = smtp_parse_custom_request(data); + if(result) + return result; + + result = smtp_regular_transfer(data, done); + + return result; +} + +/*********************************************************************** + * + * smtp_disconnect() + * + * Disconnect from an SMTP server. Cleanup protocol-specific per-connection + * resources. BLOCKING. + */ +static CURLcode smtp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + struct smtp_conn *smtpc = &conn->proto.smtpc; + (void)data; + + /* We cannot send quit unconditionally. If this connection is stale or + bad in any way, sending quit and waiting around here will make the + disconnect wait in vain and cause more problems than we need to. */ + + if(!dead_connection && conn->bits.protoconnstart) { + if(!smtp_perform_quit(data, conn)) + (void)smtp_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */ + } + + /* Disconnect from the server */ + Curl_pp_disconnect(&smtpc->pp); + + /* Cleanup the SASL module */ + Curl_sasl_cleanup(conn, smtpc->sasl.authused); + + /* Cleanup our connection based variables */ + Curl_safefree(smtpc->domain); + + return CURLE_OK; +} + +/* Call this when the DO phase has completed */ +static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected) +{ + struct SMTP *smtp = data->req.p.smtp; + + (void)connected; + + if(smtp->transfer != PPTRANSFER_BODY) + /* no data to transfer */ + Curl_xfer_setup_nop(data); + + return CURLE_OK; +} + +/* Called from multi.c while DOing */ +static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) +{ + CURLcode result = smtp_multi_statemach(data, dophase_done); + + if(result) + DEBUGF(infof(data, "DO phase failed")); + else if(*dophase_done) { + result = smtp_dophase_done(data, FALSE /* not connected */); + + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +/*********************************************************************** + * + * smtp_regular_transfer() + * + * The input argument is already checked for validity. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. + */ +static CURLcode smtp_regular_transfer(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + + /* Make sure size is unknown at this point */ + data->req.size = -1; + + /* Set the progress data */ + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); + + /* Carry out the perform */ + result = smtp_perform(data, &connected, dophase_done); + + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) + result = smtp_dophase_done(data, connected); + + return result; +} + +static CURLcode smtp_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result; + + /* Clear the TLS upgraded flag */ + conn->bits.tls_upgraded = FALSE; + + /* Initialise the SMTP layer */ + result = smtp_init(data); + if(result) + return result; + + return CURLE_OK; +} + +/*********************************************************************** + * + * smtp_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode smtp_parse_url_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct smtp_conn *smtpc = &conn->proto.smtpc; + const char *ptr = conn->options; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(strncasecompare(key, "AUTH=", 5)) + result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, + value, ptr - value); + else + result = CURLE_URL_MALFORMAT; + + if(*ptr == ';') + ptr++; + } + + return result; +} + +/*********************************************************************** + * + * smtp_parse_url_path() + * + * Parse the URL path into separate path components. + */ +static CURLcode smtp_parse_url_path(struct Curl_easy *data) +{ + /* The SMTP struct is already initialised in smtp_connect() */ + struct connectdata *conn = data->conn; + struct smtp_conn *smtpc = &conn->proto.smtpc; + const char *path = &data->state.up.path[1]; /* skip leading path */ + char localhost[HOSTNAME_MAX + 1]; + + /* Calculate the path if necessary */ + if(!*path) { + if(!Curl_gethostname(localhost, sizeof(localhost))) + path = localhost; + else + path = "localhost"; + } + + /* URL decode the path and use it as the domain in our EHLO */ + return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL); +} + +/*********************************************************************** + * + * smtp_parse_custom_request() + * + * Parse the custom request. + */ +static CURLcode smtp_parse_custom_request(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct SMTP *smtp = data->req.p.smtp; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + + /* URL decode the custom request */ + if(custom) + result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL); + + return result; +} + +/*********************************************************************** + * + * smtp_parse_address() + * + * Parse the fully qualified mailbox address into a local address part and the + * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if + * necessary. + * + * Parameters: + * + * conn [in] - The connection handle. + * fqma [in] - The fully qualified mailbox address (which may or + * may not contain UTF-8 characters). + * address [in/out] - A new allocated buffer which holds the local + * address part of the mailbox. This buffer must be + * free'ed by the caller. + * host [in/out] - The hostname structure that holds the original, + * and optionally encoded, hostname. + * Curl_free_idnconverted_hostname() must be called + * once the caller has finished with the structure. + * + * Returns CURLE_OK on success. + * + * Notes: + * + * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor + * that conversion then we shall return success. This allow the caller to send + * the data to the server as a U-label (as per RFC-6531 sect. 3.2). + * + * If an mailbox '@' separator cannot be located then the mailbox is considered + * to be either a local mailbox or an invalid mailbox (depending on what the + * calling function deems it to be) then the input will simply be returned in + * the address part with the hostname being NULL. + */ +static CURLcode smtp_parse_address(const char *fqma, char **address, + struct hostname *host) +{ + CURLcode result = CURLE_OK; + size_t length; + + /* Duplicate the fully qualified email address so we can manipulate it, + ensuring it does not contain the delimiters if specified */ + char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma); + if(!dup) + return CURLE_OUT_OF_MEMORY; + + length = strlen(dup); + if(length) { + if(dup[length - 1] == '>') + dup[length - 1] = '\0'; + } + + /* Extract the hostname from the address (if we can) */ + host->name = strpbrk(dup, "@"); + if(host->name) { + *host->name = '\0'; + host->name = host->name + 1; + + /* Attempt to convert the hostname to IDN ACE */ + (void) Curl_idnconvert_hostname(host); + + /* If Curl_idnconvert_hostname() fails then we shall attempt to continue + and send the hostname using UTF-8 rather than as 7-bit ACE (which is + our preference) */ + } + + /* Extract the local address from the mailbox */ + *address = dup; + + return result; +} + +struct cr_eob_ctx { + struct Curl_creader super; + struct bufq buf; + size_t n_eob; /* how many EOB bytes we matched so far */ + size_t eob; /* Number of bytes of the EOB (End Of Body) that + have been received so far */ + BIT(read_eos); /* we read an EOS from the next reader */ + BIT(eos); /* we have returned an EOS */ +}; + +static CURLcode cr_eob_init(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_eob_ctx *ctx = reader->ctx; + (void)data; + /* The first char we read is the first on a line, as if we had + * read CRLF just before */ + ctx->n_eob = 2; + Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT); + return CURLE_OK; +} + +static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader) +{ + struct cr_eob_ctx *ctx = reader->ctx; + (void)data; + Curl_bufq_free(&ctx->buf); +} + +/* this is the 5-bytes End-Of-Body marker for SMTP */ +#define SMTP_EOB "\r\n.\r\n" +#define SMTP_EOB_FIND_LEN 3 + +/* client reader doing SMTP End-Of-Body escaping. */ +static CURLcode cr_eob_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_eob_ctx *ctx = reader->ctx; + CURLcode result = CURLE_OK; + size_t nread, i, start, n; + bool eos; + + if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { + /* Get more and convert it when needed */ + result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); + if(result) + return result; + + ctx->read_eos = eos; + if(nread) { + if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) { + /* not in the middle of a match, no EOB start found, just pass */ + *pnread = nread; + *peos = FALSE; + return CURLE_OK; + } + /* scan for EOB (continuation) and convert */ + for(i = start = 0; i < nread; ++i) { + if(ctx->n_eob >= SMTP_EOB_FIND_LEN) { + /* matched the EOB prefix and seeing additional char, add '.' */ + result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); + if(result) + return result; + result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n); + if(result) + return result; + ctx->n_eob = 0; + start = i; + if(data->state.infilesize > 0) + data->state.infilesize++; + } + + if(buf[i] != SMTP_EOB[ctx->n_eob]) + ctx->n_eob = 0; + + if(buf[i] == SMTP_EOB[ctx->n_eob]) { + /* matching another char of the EOB */ + ++ctx->n_eob; + } + } + + /* add any remainder to buf */ + if(start < nread) { + result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n); + if(result) + return result; + } + } + + if(ctx->read_eos) { + /* if we last matched a CRLF or if the data was empty, add ".\r\n" + * to end the body. If we sent something and it did not end with "\r\n", + * add "\r\n.\r\n" to end the body */ + const char *eob = SMTP_EOB; + switch(ctx->n_eob) { + case 2: + /* seen a CRLF at the end, just add the remainder */ + eob = &SMTP_EOB[2]; + break; + case 3: + /* ended with '\r\n.', we should escpe the last '.' */ + eob = "." SMTP_EOB; + break; + default: + break; + } + result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n); + if(result) + return result; + } + } + + *peos = FALSE; + if(!Curl_bufq_is_empty(&ctx->buf)) { + result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread); + } + else + *pnread = 0; + + if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { + /* no more data, read all, done. */ + ctx->eos = TRUE; + } + *peos = ctx->eos; + DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d", + blen, result, *pnread, *peos)); + return result; +} + +static curl_off_t cr_eob_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + /* this reader changes length depending on input */ + (void)data; + (void)reader; + return -1; +} + +static const struct Curl_crtype cr_eob = { + "cr-smtp-eob", + cr_eob_init, + cr_eob_read, + cr_eob_close, + Curl_creader_def_needs_rewind, + cr_eob_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_rewind, + Curl_creader_def_unpause, + Curl_creader_def_is_paused, + Curl_creader_def_done, + sizeof(struct cr_eob_ctx) +}; + +static CURLcode cr_eob_add(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &cr_eob, + CURL_CR_CONTENT_ENCODE); + if(!result) + result = Curl_creader_add(data, reader); + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + +#endif /* CURL_DISABLE_SMTP */ diff --git a/third_party/curl/lib/smtp.h b/third_party/curl/lib/smtp.h new file mode 100644 index 0000000000..7c2af68073 --- /dev/null +++ b/third_party/curl/lib/smtp.h @@ -0,0 +1,87 @@ +#ifndef HEADER_CURL_SMTP_H +#define HEADER_CURL_SMTP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "pingpong.h" +#include "curl_sasl.h" + +/**************************************************************************** + * SMTP unique setup + ***************************************************************************/ +typedef enum { + SMTP_STOP, /* do nothing state, stops the state machine */ + SMTP_SERVERGREET, /* waiting for the initial greeting immediately after + a connect */ + SMTP_EHLO, + SMTP_HELO, + SMTP_STARTTLS, + SMTP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS + (multi mode only) */ + SMTP_AUTH, + SMTP_COMMAND, /* VRFY, EXPN, NOOP, RSET and HELP */ + SMTP_MAIL, /* MAIL FROM */ + SMTP_RCPT, /* RCPT TO */ + SMTP_DATA, + SMTP_POSTDATA, + SMTP_QUIT, + SMTP_LAST /* never used */ +} smtpstate; + +/* This SMTP struct is used in the Curl_easy. All SMTP data that is + connection-oriented must be in smtp_conn to properly deal with the fact that + perhaps the Curl_easy is changed between the times the connection is + used. */ +struct SMTP { + curl_pp_transfer transfer; + char *custom; /* Custom Request */ + struct curl_slist *rcpt; /* Recipient list */ + int rcpt_last_error; /* The last error received for RCPT TO command */ + size_t eob; /* Number of bytes of the EOB (End Of Body) that + have been received so far */ + BIT(rcpt_had_ok); /* Whether any of RCPT TO commands (depends on + total number of recipients) succeeded so far */ + BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */ +}; + +/* smtp_conn is used for struct connection-oriented data in the connectdata + struct */ +struct smtp_conn { + struct pingpong pp; + struct SASL sasl; /* SASL-related storage */ + smtpstate state; /* Always use smtp.c:state() to change state! */ + char *domain; /* Client address/name to send in the EHLO */ + BIT(ssldone); /* Is connect() over SSL done? */ + BIT(tls_supported); /* StartTLS capability supported by server */ + BIT(size_supported); /* If server supports SIZE extension according to + RFC 1870 */ + BIT(utf8_supported); /* If server supports SMTPUTF8 extension according + to RFC 6531 */ + BIT(auth_supported); /* AUTH capability supported by server */ +}; + +extern const struct Curl_handler Curl_handler_smtp; +extern const struct Curl_handler Curl_handler_smtps; + +#endif /* HEADER_CURL_SMTP_H */ diff --git a/third_party/curl/lib/sockaddr.h b/third_party/curl/lib/sockaddr.h new file mode 100644 index 0000000000..2e2d375e06 --- /dev/null +++ b/third_party/curl/lib/sockaddr.h @@ -0,0 +1,44 @@ +#ifndef HEADER_CURL_SOCKADDR_H +#define HEADER_CURL_SOCKADDR_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +struct Curl_sockaddr_storage { + union { + struct sockaddr sa; + struct sockaddr_in sa_in; +#ifdef USE_IPV6 + struct sockaddr_in6 sa_in6; +#endif +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE + struct sockaddr_storage sa_stor; +#else + char cbuf[256]; /* this should be big enough to fit a lot */ +#endif + } buffer; +}; + +#endif /* HEADER_CURL_SOCKADDR_H */ diff --git a/third_party/curl/lib/socketpair.c b/third_party/curl/lib/socketpair.c new file mode 100644 index 0000000000..b14f5a5f14 --- /dev/null +++ b/third_party/curl/lib/socketpair.c @@ -0,0 +1,264 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "socketpair.h" +#include "urldata.h" +#include "rand.h" + +#if defined(USE_EVENTFD) +#ifdef HAVE_SYS_EVENTFD_H +#include +#endif + +int Curl_eventfd(curl_socket_t socks[2], bool nonblocking) +{ + int efd = eventfd(0, nonblocking ? EFD_CLOEXEC | EFD_NONBLOCK : EFD_CLOEXEC); + if(efd == -1) { + socks[0] = socks[1] = CURL_SOCKET_BAD; + return -1; + } + socks[0] = socks[1] = efd; + return 0; +} +#elif defined(HAVE_PIPE) +#ifdef HAVE_FCNTL +#include +#endif + +int Curl_pipe(curl_socket_t socks[2], bool nonblocking) +{ + if(pipe(socks)) + return -1; +#ifdef HAVE_FCNTL + if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) || + fcntl(socks[1], F_SETFD, FD_CLOEXEC) ) { + close(socks[0]); + close(socks[1]); + socks[0] = socks[1] = CURL_SOCKET_BAD; + return -1; + } +#endif + if(nonblocking) { + if(curlx_nonblock(socks[0], TRUE) < 0 || + curlx_nonblock(socks[1], TRUE) < 0) { + close(socks[0]); + close(socks[1]); + socks[0] = socks[1] = CURL_SOCKET_BAD; + return -1; + } + } + + return 0; +} +#endif + + +#ifndef CURL_DISABLE_SOCKETPAIR +#ifdef HAVE_SOCKETPAIR +int Curl_socketpair(int domain, int type, int protocol, + curl_socket_t socks[2], bool nonblocking) +{ +#ifdef SOCK_NONBLOCK + type = nonblocking ? type | SOCK_NONBLOCK : type; +#endif + if(socketpair(domain, type, protocol, socks)) + return -1; +#ifndef SOCK_NONBLOCK + if(nonblocking) { + if(curlx_nonblock(socks[0], TRUE) < 0 || + curlx_nonblock(socks[1], TRUE) < 0) { + close(socks[0]); + close(socks[1]); + return -1; + } + } +#endif + return 0; +} +#else /* !HAVE_SOCKETPAIR */ +#ifdef _WIN32 +/* + * This is a socketpair() implementation for Windows. + */ +#include +#include +#else +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include /* IPPROTO_TCP */ +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001 +#endif /* !INADDR_LOOPBACK */ +#endif /* !_WIN32 */ + +#include "nonblock.h" /* for curlx_nonblock */ +#include "timeval.h" /* needed before select.h */ +#include "select.h" /* for Curl_poll */ + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +int Curl_socketpair(int domain, int type, int protocol, + curl_socket_t socks[2], bool nonblocking) +{ + union { + struct sockaddr_in inaddr; + struct sockaddr addr; + } a; + curl_socket_t listener; + curl_socklen_t addrlen = sizeof(a.inaddr); + int reuse = 1; + struct pollfd pfd[1]; + (void)domain; + (void)type; + (void)protocol; + + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(listener == CURL_SOCKET_BAD) + return -1; + + memset(&a, 0, sizeof(a)); + a.inaddr.sin_family = AF_INET; + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_port = 0; + + socks[0] = socks[1] = CURL_SOCKET_BAD; + +#if defined(_WIN32) || defined(__CYGWIN__) + /* do not set SO_REUSEADDR on Windows */ + (void)reuse; +#ifdef SO_EXCLUSIVEADDRUSE + { + int exclusive = 1; + if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1) + goto error; + } +#endif +#else + if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, + (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1) + goto error; +#endif + if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1) + goto error; + if(getsockname(listener, &a.addr, &addrlen) == -1 || + addrlen < (int)sizeof(a.inaddr)) + goto error; + if(listen(listener, 1) == -1) + goto error; + socks[0] = socket(AF_INET, SOCK_STREAM, 0); + if(socks[0] == CURL_SOCKET_BAD) + goto error; + if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1) + goto error; + + /* use non-blocking accept to make sure we do not block forever */ + if(curlx_nonblock(listener, TRUE) < 0) + goto error; + pfd[0].fd = listener; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + (void)Curl_poll(pfd, 1, 1000); /* one second */ + socks[1] = accept(listener, NULL, NULL); + if(socks[1] == CURL_SOCKET_BAD) + goto error; + else { + struct curltime start = Curl_now(); + char rnd[9]; + char check[sizeof(rnd)]; + char *p = &check[0]; + size_t s = sizeof(check); + + if(Curl_rand(NULL, (unsigned char *)rnd, sizeof(rnd))) + goto error; + + /* write data to the socket */ + swrite(socks[0], rnd, sizeof(rnd)); + /* verify that we read the correct data */ + do { + ssize_t nread; + + pfd[0].fd = socks[1]; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + (void)Curl_poll(pfd, 1, 1000); /* one second */ + + nread = sread(socks[1], p, s); + if(nread == -1) { + int sockerr = SOCKERRNO; + /* Do not block forever */ + if(Curl_timediff(Curl_now(), start) > (60 * 1000)) + goto error; + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == sockerr) +#else + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it + returned due to its inability to send off data without + blocking. We therefore treat both error codes the same here */ + (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || + (EINTR == sockerr) || (EINPROGRESS == sockerr) +#endif + ) { + continue; + } + goto error; + } + s -= nread; + if(s) { + p += nread; + continue; + } + if(memcmp(rnd, check, sizeof(check))) + goto error; + break; + } while(1); + } + + if(nonblocking) + if(curlx_nonblock(socks[0], TRUE) < 0 || + curlx_nonblock(socks[1], TRUE) < 0) + goto error; + sclose(listener); + return 0; + +error: + sclose(listener); + sclose(socks[0]); + sclose(socks[1]); + return -1; +} +#endif +#endif /* !CURL_DISABLE_SOCKETPAIR */ diff --git a/third_party/curl/lib/socketpair.h b/third_party/curl/lib/socketpair.h new file mode 100644 index 0000000000..42f4034fc5 --- /dev/null +++ b/third_party/curl/lib/socketpair.h @@ -0,0 +1,98 @@ +#ifndef HEADER_CURL_SOCKETPAIR_H +#define HEADER_CURL_SOCKETPAIR_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(HAVE_EVENTFD) && \ + defined(__x86_64__) && \ + defined(__aarch64__) && \ + defined(__ia64__) && \ + defined(__ppc64__) && \ + defined(__mips64) && \ + defined(__sparc64__) && \ + defined(__riscv_64e) && \ + defined(__s390x__) + +/* Use eventfd only with 64-bit CPU architectures because eventfd has a + * stringent rule of requiring the 8-byte buffer when calling read(2) and + * write(2) on it. In some rare cases, the C standard library implementation + * on a 32-bit system might choose to define uint64_t as a 32-bit type for + * various reasons (memory limitations, compatibility with older code), + * which makes eventfd broken. + */ +#define USE_EVENTFD 1 + +#define wakeup_write write +#define wakeup_read read +#define wakeup_close close +#define wakeup_create(p,nb) Curl_eventfd(p,nb) + +#include +int Curl_eventfd(curl_socket_t socks[2], bool nonblocking); + +#elif defined(HAVE_PIPE) + +#define wakeup_write write +#define wakeup_read read +#define wakeup_close close +#define wakeup_create(p,nb) Curl_pipe(p,nb) + +#include +int Curl_pipe(curl_socket_t socks[2], bool nonblocking); + +#else /* !USE_EVENTFD && !HAVE_PIPE */ + +#define wakeup_write swrite +#define wakeup_read sread +#define wakeup_close sclose + +#if defined(USE_UNIX_SOCKETS) && defined(HAVE_SOCKETPAIR) +#define SOCKETPAIR_FAMILY AF_UNIX +#elif !defined(HAVE_SOCKETPAIR) +#define SOCKETPAIR_FAMILY 0 /* not used */ +#else +#error "unsupported unix domain and socketpair build combo" +#endif + +#ifdef SOCK_CLOEXEC +#define SOCKETPAIR_TYPE (SOCK_STREAM | SOCK_CLOEXEC) +#else +#define SOCKETPAIR_TYPE SOCK_STREAM +#endif + +#define wakeup_create(p,nb)\ +Curl_socketpair(SOCKETPAIR_FAMILY, SOCKETPAIR_TYPE, 0, p, nb) + +#endif /* USE_EVENTFD */ + +#ifndef CURL_DISABLE_SOCKETPAIR +#include + +int Curl_socketpair(int domain, int type, int protocol, + curl_socket_t socks[2], bool nonblocking); +#endif + +#endif /* HEADER_CURL_SOCKETPAIR_H */ diff --git a/third_party/curl/lib/socks.c b/third_party/curl/lib/socks.c new file mode 100644 index 0000000000..094ff42cd7 --- /dev/null +++ b/third_party/curl/lib/socks.c @@ -0,0 +1,1277 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#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 */ +}; + +#define CURL_SOCKS_BUF_SIZE 600 + +/* make sure we configure it not too low */ +#if CURL_SOCKS_BUF_SIZE < 600 +#error CURL_SOCKS_BUF_SIZE must be at least 600 +#endif + + +struct socks_state { + enum connect_t state; + ssize_t outstanding; /* send this many bytes more */ + unsigned char buffer[CURL_SOCKS_BUF_SIZE]; + unsigned char *outp; /* send from this pointer */ + + const char *hostname; + 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 + * blocks until all bytes amount of buffersize will be read. No more, no less. + * + * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions. + */ +int Curl_blockread_all(struct Curl_cfilter *cf, + struct Curl_easy *data, /* transfer */ + char *buf, /* store read data here */ + ssize_t buffersize, /* max amount to read */ + ssize_t *n) /* amount bytes read */ +{ + ssize_t nread = 0; + ssize_t allread = 0; + int result; + CURLcode err = CURLE_OK; + + *n = 0; + for(;;) { + timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + if(timeout_ms < 0) { + /* we already got the timeout */ + result = CURLE_OPERATION_TIMEDOUT; + break; + } + if(!timeout_ms) + timeout_ms = TIMEDIFF_T_MAX; + if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) { + result = ~CURLE_OK; + break; + } + nread = Curl_conn_cf_recv(cf->next, data, buf, buffersize, &err); + if(nread <= 0) { + result = (int)err; + if(CURLE_AGAIN == err) + continue; + if(err) { + break; + } + } + + if(buffersize == nread) { + allread += nread; + *n = allread; + result = CURLE_OK; + break; + } + if(!nread) { + result = ~CURLE_OK; + break; + } + + buffersize -= nread; + buf += nread; + allread += nread; + } + return result; +} +#endif + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#define DEBUG_AND_VERBOSE +#define sxstate(x,d,y) socksstate(x,d,y, __LINE__) +#else +#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 socks_state *sx, struct Curl_easy *data, + enum connect_t state +#ifdef DEBUG_AND_VERBOSE + , int lineno +#endif +) +{ + enum connect_t oldstate = sx->state; +#ifdef DEBUG_AND_VERBOSE + /* synced with the state list in urldata.h */ + static const char * const socks_statename[] = { + "INIT", + "SOCKS_INIT", + "SOCKS_SEND", + "SOCKS_READ_INIT", + "SOCKS_READ", + "GSSAPI_INIT", + "AUTH_INIT", + "AUTH_SEND", + "AUTH_READ", + "REQ_INIT", + "RESOLVING", + "RESOLVED", + "RESOLVE_REMOTE", + "REQ_SEND", + "REQ_SENDING", + "REQ_READ", + "REQ_READ_MORE", + "DONE" + }; +#endif + + (void)data; + if(oldstate == state) + /* do not bother when the new state is the same as the old state */ + return; + + sx->state = state; + +#ifdef DEBUG_AND_VERBOSE + infof(data, + "SXSTATE: %s => %s; line %d", + socks_statename[oldstate], socks_statename[sx->state], + lineno); +#endif +} + +static CURLproxycode socks_state_send(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data, + CURLproxycode failcode, + const char *description) +{ + ssize_t nwritten; + CURLcode result; + + nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp, + sx->outstanding, &result); + if(nwritten <= 0) { + if(CURLE_AGAIN == result) { + return CURLPX_OK; + } + else if(CURLE_OK == result) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + failf(data, "Failed to send %s: %s", description, + curl_easy_strerror(result)); + return failcode; + } + DEBUGASSERT(sx->outstanding >= nwritten); + /* not done, remain in state */ + sx->outstanding -= nwritten; + sx->outp += nwritten; + return CURLPX_OK; +} + +static CURLproxycode socks_state_recv(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data, + CURLproxycode failcode, + const char *description) +{ + ssize_t nread; + CURLcode result; + + nread = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp, + sx->outstanding, &result); + if(nread <= 0) { + if(CURLE_AGAIN == result) { + return CURLPX_OK; + } + else if(CURLE_OK == result) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + failf(data, "SOCKS: Failed receiving %s: %s", description, + curl_easy_strerror(result)); + return failcode; + } + /* remain in reading state */ + DEBUGASSERT(sx->outstanding >= nread); + sx->outstanding -= nread; + sx->outp += nread; + return CURLPX_OK; +} + +/* +* This function logs in to a SOCKS4 proxy and sends the specifics to the final +* destination server. +* +* Reference : +* https://www.openssh.com/txt/socks4.protocol +* +* Note : +* Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" +* Nonsupport "Identification Protocol (RFC1413)" +*/ +static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data) +{ + struct connectdata *conn = cf->conn; + const bool protocol4a = + (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; + unsigned char *socksreq = sx->buffer; + CURLcode result; + CURLproxycode presult; + struct Curl_dns_entry *dns = NULL; + + 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" : "", sx->hostname, sx->remote_port); + + infof(data, "SOCKS4 communication to %s:%d", + sx->hostname, sx->remote_port); + + /* + * Compose socks4 request + * + * Request format + * + * +----+----+----+----+----+----+----+----+----+----+....+----+ + * | VN | CD | DSTPORT | DSTIP | USERID |NULL| + * +----+----+----+----+----+----+----+----+----+----+....+----+ + * # of bytes: 1 1 2 4 variable 1 + */ + + socksreq[0] = 4; /* version (SOCKS4) */ + socksreq[1] = 1; /* connect */ + 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, sx->hostname, sx->remote_port, TRUE, &dns); + + if(rc == CURLRESOLV_ERROR) + return CURLPX_RESOLVE_HOST; + else if(rc == CURLRESOLV_PENDING) { + sxstate(sx, data, CONNECT_RESOLVING); + infof(data, "SOCKS4 non-blocking resolve of %s", sx->hostname); + return CURLPX_OK; + } + sxstate(sx, data, CONNECT_RESOLVED); + goto CONNECT_RESOLVED; + } + + /* socks4a does not resolve anything locally */ + 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, sx->hostname, conn->primary.remote_port); + + if(dns) { +#ifdef CURLRES_ASYNCH + data->state.async.dns = dns; + data->state.async.done = TRUE; +#endif + infof(data, "Hostname '%s' was found", sx->hostname); + sxstate(sx, data, CONNECT_RESOLVED); + } + else { + result = Curl_resolv_check(data, &dns); + if(!dns) { + if(result) + return CURLPX_RESOLVE_HOST; + return CURLPX_OK; + } + } + FALLTHROUGH(); + case CONNECT_RESOLVED: +CONNECT_RESOLVED: + { + struct Curl_addrinfo *hp = NULL; + /* + * We cannot use 'hostent' as a struct that Curl_resolv() returns. It + * returns a Curl_addrinfo pointer that may not always look the same. + */ + if(dns) { + hp = dns->addr; + + /* scan for the first IPv4 address */ + while(hp && (hp->ai_family != AF_INET)) + hp = hp->ai_next; + + if(hp) { + struct sockaddr_in *saddr_in; + char buf[64]; + Curl_printable_address(hp, buf, sizeof(buf)); + + saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; + socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0]; + socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1]; + socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2]; + socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3]; + + infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf); + + Curl_resolv_unlock(data, dns); /* not used anymore from now on */ + } + else + failf(data, "SOCKS4 connection to %s not supported", sx->hostname); + } + else + failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", + sx->hostname); + + if(!hp) + return CURLPX_RESOLVE_HOST; + } + FALLTHROUGH(); + case CONNECT_REQ_INIT: +CONNECT_REQ_INIT: + /* + * This is currently not supporting "Identification Protocol (RFC1413)". + */ + socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ + if(sx->proxy_user) { + size_t plen = strlen(sx->proxy_user); + if(plen > 255) { + /* there is no real size limit to this field in the protocol, but + SOCKS5 limits the proxy user field to 255 bytes and it seems likely + that a longer field is either a mistake or malicious input */ + failf(data, "Too long SOCKS proxy username"); + return CURLPX_LONG_USER; + } + /* copy the proxy name WITH trailing zero */ + memcpy(socksreq + 8, sx->proxy_user, plen + 1); + } + + /* + * Make connection + */ + { + size_t packetsize = 9 + + strlen((char *)socksreq + 8); /* size including NUL */ + + /* If SOCKS4a, set special invalid IP address 0.0.0.x */ + if(protocol4a) { + size_t hostnamelen = 0; + socksreq[4] = 0; + socksreq[5] = 0; + socksreq[6] = 0; + socksreq[7] = 1; + /* append hostname */ + hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */ + if((hostnamelen <= 255) && + (packetsize + hostnamelen < sizeof(sx->buffer))) + strcpy((char *)socksreq + packetsize, sx->hostname); + else { + failf(data, "SOCKS4: too long hostname"); + return CURLPX_LONG_HOSTNAME; + } + packetsize += hostnamelen; + } + sx->outp = socksreq; + DEBUGASSERT(packetsize <= sizeof(sx->buffer)); + sx->outstanding = packetsize; + sxstate(sx, data, CONNECT_REQ_SENDING); + } + FALLTHROUGH(); + case CONNECT_REQ_SENDING: + /* Send request */ + presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, + "SOCKS4 connect request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ + return CURLPX_OK; + } + /* done sending! */ + sx->outstanding = 8; /* receive data size */ + sx->outp = socksreq; + sxstate(sx, data, CONNECT_SOCKS_READ); + + FALLTHROUGH(); + case CONNECT_SOCKS_READ: + /* Receive response */ + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT, + "connect request ack"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ + return CURLPX_OK; + } + sxstate(sx, data, CONNECT_DONE); + break; + default: /* lots of unused states in SOCKS4 */ + break; + } + + /* + * Response format + * + * +----+----+----+----+----+----+----+----+ + * | VN | CD | DSTPORT | DSTIP | + * +----+----+----+----+----+----+----+----+ + * # of bytes: 1 1 2 4 + * + * VN is the version of the reply code and should be 0. CD is the result + * code with one of the following values: + * + * 90: request granted + * 91: request rejected or failed + * 92: request rejected because SOCKS server cannot connect to + * identd on the client + * 93: request rejected because the client program and identd + * report different user-ids + */ + + /* wrong version ? */ + if(socksreq[0]) { + failf(data, + "SOCKS4 reply has wrong version, version should be 0."); + return CURLPX_BAD_VERSION; + } + + /* Result */ + switch(socksreq[1]) { + case 90: + infof(data, "SOCKS4%s request granted.", protocol4a?"a":""); + break; + case 91: + failf(data, + "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", request rejected or failed.", + socksreq[4], socksreq[5], socksreq[6], socksreq[7], + (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), + (unsigned char)socksreq[1]); + return CURLPX_REQUEST_FAILED; + case 92: + failf(data, + "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", request rejected because SOCKS server cannot connect to " + "identd on the client.", + socksreq[4], socksreq[5], socksreq[6], socksreq[7], + (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), + (unsigned char)socksreq[1]); + return CURLPX_IDENTD; + case 93: + failf(data, + "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", request rejected because the client program and identd " + "report different user-ids.", + socksreq[4], socksreq[5], socksreq[6], socksreq[7], + (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), + (unsigned char)socksreq[1]); + return CURLPX_IDENTD_DIFFER; + default: + failf(data, + "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", Unknown.", + socksreq[4], socksreq[5], socksreq[6], socksreq[7], + (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), + (unsigned char)socksreq[1]); + return CURLPX_UNKNOWN_FAIL; + } + + return CURLPX_OK; /* Proxy was successful! */ +} + +/* + * This function logs in to a SOCKS5 proxy and sends the specifics to the final + * destination server. + */ +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 + replies: + + +----+-----+-------+------+----------+----------+ + |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + + Where: + + o VER protocol version: X'05' + o REP Reply field: + o X'00' succeeded + */ + struct connectdata *conn = cf->conn; + unsigned char *socksreq = sx->buffer; + size_t idx; + CURLcode result; + CURLproxycode presult; + bool socks5_resolve_local = + (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; + const size_t hostname_len = strlen(sx->hostname); + size_t len = 0; + const unsigned char auth = data->set.socks5auth; + bool allow_gssapi = FALSE; + struct Curl_dns_entry *dns = NULL; + + DEBUGASSERT(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI)); + switch(sx->state) { + case CONNECT_SOCKS_INIT: + if(conn->bits.httpproxy) + infof(data, "SOCKS5: connecting to HTTP proxy %s port %d", + sx->hostname, sx->remote_port); + + /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ + if(!socks5_resolve_local && hostname_len > 255) { + failf(data, "SOCKS5: the destination hostname is too long to be " + "resolved remotely by the proxy."); + return CURLPX_LONG_HOSTNAME; + } + + if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) + infof(data, + "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %u", + auth); + if(!(auth & CURLAUTH_BASIC)) + /* disable username/password auth */ + sx->proxy_user = NULL; +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + if(auth & CURLAUTH_GSSAPI) + allow_gssapi = TRUE; +#endif + + idx = 0; + socksreq[idx++] = 5; /* version */ + idx++; /* number of authentication methods */ + socksreq[idx++] = 0; /* no authentication */ + if(allow_gssapi) + socksreq[idx++] = 1; /* GSS-API */ + if(sx->proxy_user) + socksreq[idx++] = 2; /* username/password */ + /* write the number of authentication methods */ + socksreq[1] = (unsigned char) (idx - 2); + + sx->outp = socksreq; + DEBUGASSERT(idx <= sizeof(sx->buffer)); + sx->outstanding = idx; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, + "initial SOCKS5 request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ + return CURLPX_OK; + } + sxstate(sx, data, CONNECT_SOCKS_READ); + goto CONNECT_SOCKS_READ_INIT; + case CONNECT_SOCKS_SEND: + presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, + "initial SOCKS5 request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ + return CURLPX_OK; + } + FALLTHROUGH(); + case CONNECT_SOCKS_READ_INIT: +CONNECT_SOCKS_READ_INIT: + sx->outstanding = 2; /* expect two bytes */ + sx->outp = socksreq; /* store it here */ + FALLTHROUGH(); + case CONNECT_SOCKS_READ: + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT, + "initial SOCKS5 response"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ + return CURLPX_OK; + } + else if(socksreq[0] != 5) { + failf(data, "Received invalid version in initial SOCKS5 response."); + return CURLPX_BAD_VERSION; + } + else if(socksreq[1] == 0) { + /* DONE! No authentication needed. Send request. */ + sxstate(sx, data, CONNECT_REQ_INIT); + goto CONNECT_REQ_INIT; + } + else if(socksreq[1] == 2) { + /* regular name + password authentication */ + 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(sx, data, CONNECT_GSSAPI_INIT); + result = Curl_SOCKS5_gssapi_negotiate(cf, data); + if(result) { + failf(data, "Unable to negotiate SOCKS5 GSS-API context."); + return CURLPX_GSSAPI; + } + } +#endif + else { + /* error */ + if(!allow_gssapi && (socksreq[1] == 1)) { + failf(data, + "SOCKS5 GSSAPI per-message authentication is not supported."); + return CURLPX_GSSAPI_PERMSG; + } + else if(socksreq[1] == 255) { + failf(data, "No authentication method was acceptable."); + return CURLPX_NO_AUTH; + } + } + failf(data, + "Undocumented SOCKS5 mode attempted to be used by server."); + return CURLPX_UNKNOWN_MODE; +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + case CONNECT_GSSAPI_INIT: + /* GSSAPI stuff done non-blocking */ + break; +#endif + + default: /* do nothing! */ + break; + +CONNECT_AUTH_INIT: + case CONNECT_AUTH_INIT: { + /* Needs username and password */ + size_t proxy_user_len, proxy_password_len; + 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; + proxy_password_len = 0; + } + + /* username/password request looks like + * +----+------+----------+------+----------+ + * |VER | ULEN | UNAME | PLEN | PASSWD | + * +----+------+----------+------+----------+ + * | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + * +----+------+----------+------+----------+ + */ + len = 0; + socksreq[len++] = 1; /* username/pw subnegotiation version */ + socksreq[len++] = (unsigned char) 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 username length for proxy auth"); + return CURLPX_LONG_USER; + } + memcpy(socksreq + len, sx->proxy_user, proxy_user_len); + } + len += proxy_user_len; + socksreq[len++] = (unsigned char) 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, sx->proxy_password, proxy_password_len); + } + len += proxy_password_len; + sxstate(sx, data, CONNECT_AUTH_SEND); + DEBUGASSERT(len <= sizeof(sx->buffer)); + sx->outstanding = len; + sx->outp = socksreq; + } + FALLTHROUGH(); + case CONNECT_AUTH_SEND: + presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH, + "SOCKS5 sub-negotiation request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ + return CURLPX_OK; + } + sx->outp = socksreq; + sx->outstanding = 2; + sxstate(sx, data, CONNECT_AUTH_READ); + FALLTHROUGH(); + case CONNECT_AUTH_READ: + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH, + "SOCKS5 sub-negotiation response"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ + return CURLPX_OK; + } + /* ignore the first (VER) byte */ + else if(socksreq[1]) { /* status */ + failf(data, "User was rejected by the SOCKS5 server (%d %d).", + socksreq[0], socksreq[1]); + return CURLPX_USER_REJECTED; + } + + /* Everything is good so far, user was authenticated! */ + sxstate(sx, data, CONNECT_REQ_INIT); + FALLTHROUGH(); + case CONNECT_REQ_INIT: +CONNECT_REQ_INIT: + if(socks5_resolve_local) { + enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port, + TRUE, &dns); + + if(rc == CURLRESOLV_ERROR) + return CURLPX_RESOLVE_HOST; + + if(rc == CURLRESOLV_PENDING) { + sxstate(sx, data, CONNECT_RESOLVING); + return CURLPX_OK; + } + 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, 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", sx->hostname); + } + + if(!dns) { + result = Curl_resolv_check(data, &dns); + if(!dns) { + if(result) + return CURLPX_RESOLVE_HOST; + return CURLPX_OK; + } + } + FALLTHROUGH(); + case CONNECT_RESOLVED: +CONNECT_RESOLVED: + { + char dest[MAX_IPADR_LEN]; /* printable address */ + struct Curl_addrinfo *hp = NULL; + if(dns) + hp = dns->addr; +#ifdef USE_IPV6 + if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) { + int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ? + AF_INET : AF_INET6; + /* scan for the first proper address */ + while(hp && (hp->ai_family != wanted_family)) + hp = hp->ai_next; + } +#endif + if(!hp) { + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", + sx->hostname); + return CURLPX_RESOLVE_HOST; + } + + Curl_printable_address(hp, dest, sizeof(dest)); + + len = 0; + socksreq[len++] = 5; /* version (SOCKS5) */ + socksreq[len++] = 1; /* connect */ + socksreq[len++] = 0; /* must be zero */ + if(hp->ai_family == AF_INET) { + int i; + struct sockaddr_in *saddr_in; + socksreq[len++] = 1; /* ATYP: IPv4 = 1 */ + + saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; + for(i = 0; i < 4; i++) { + socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i]; + } + + infof(data, "SOCKS5 connect to %s:%d (locally resolved)", dest, + sx->remote_port); + } +#ifdef USE_IPV6 + else if(hp->ai_family == AF_INET6) { + int i; + struct sockaddr_in6 *saddr_in6; + socksreq[len++] = 4; /* ATYP: IPv6 = 4 */ + + saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr; + for(i = 0; i < 16; i++) { + socksreq[len++] = + ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i]; + } + + infof(data, "SOCKS5 connect to [%s]:%d (locally resolved)", dest, + sx->remote_port); + } +#endif + else { + hp = NULL; /* fail! */ + failf(data, "SOCKS5 connection to %s not supported", dest); + } + + Curl_resolv_unlock(data, dns); /* not used anymore from now on */ + goto CONNECT_REQ_SEND; + } +CONNECT_RESOLVE_REMOTE: + case CONNECT_RESOLVE_REMOTE: + /* Authentication is complete, now specify destination to the proxy */ + len = 0; + socksreq[len++] = 5; /* version (SOCKS5) */ + socksreq[len++] = 1; /* connect */ + socksreq[len++] = 0; /* must be zero */ + + if(!socks5_resolve_local) { + /* ATYP: domain name = 3, + IPv6 == 4, + IPv4 == 1 */ + unsigned char ip4[4]; +#ifdef USE_IPV6 + if(conn->bits.ipv6_ip) { + char ip6[16]; + if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6)) + return CURLPX_BAD_ADDRESS_TYPE; + socksreq[len++] = 4; + memcpy(&socksreq[len], ip6, sizeof(ip6)); + len += sizeof(ip6); + } + else +#endif + if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) { + socksreq[len++] = 1; + memcpy(&socksreq[len], ip4, sizeof(ip4)); + len += sizeof(ip4); + } + else { + socksreq[len++] = 3; + socksreq[len++] = (unsigned char) hostname_len; /* one byte length */ + memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */ + len += hostname_len; + } + infof(data, "SOCKS5 connect to %s:%d (remotely resolved)", + sx->hostname, sx->remote_port); + } + FALLTHROUGH(); + + case CONNECT_REQ_SEND: +CONNECT_REQ_SEND: + /* PORT MSB */ + socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff); + /* PORT LSB */ + socksreq[len++] = (unsigned char)(sx->remote_port & 0xff); + +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + if(conn->socks5_gssapi_enctype) { + failf(data, "SOCKS5 GSS-API protection not yet implemented."); + return CURLPX_GSSAPI_PROTECTION; + } +#endif + sx->outp = socksreq; + DEBUGASSERT(len <= sizeof(sx->buffer)); + sx->outstanding = len; + sxstate(sx, data, CONNECT_REQ_SENDING); + FALLTHROUGH(); + case CONNECT_REQ_SENDING: + presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST, + "SOCKS5 connect request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in send state */ + return CURLPX_OK; + } +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + if(conn->socks5_gssapi_enctype) { + failf(data, "SOCKS5 GSS-API protection not yet implemented."); + return CURLPX_GSSAPI_PROTECTION; + } +#endif + sx->outstanding = 10; /* minimum packet size is 10 */ + sx->outp = socksreq; + sxstate(sx, data, CONNECT_REQ_READ); + FALLTHROUGH(); + case CONNECT_REQ_READ: + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK, + "SOCKS5 connect request ack"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ + return CURLPX_OK; + } + else if(socksreq[0] != 5) { /* version */ + failf(data, + "SOCKS5 reply has wrong version, version should be 5."); + return CURLPX_BAD_VERSION; + } + else if(socksreq[1]) { /* Anything besides 0 is an error */ + CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; + int code = socksreq[1]; + failf(data, "cannot complete SOCKS5 connection to %s. (%d)", + sx->hostname, (unsigned char)socksreq[1]); + if(code < 9) { + /* RFC 1928 section 6 lists: */ + static const CURLproxycode lookup[] = { + CURLPX_OK, + CURLPX_REPLY_GENERAL_SERVER_FAILURE, + CURLPX_REPLY_NOT_ALLOWED, + CURLPX_REPLY_NETWORK_UNREACHABLE, + CURLPX_REPLY_HOST_UNREACHABLE, + CURLPX_REPLY_CONNECTION_REFUSED, + CURLPX_REPLY_TTL_EXPIRED, + CURLPX_REPLY_COMMAND_NOT_SUPPORTED, + CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED, + }; + rc = lookup[code]; + } + return rc; + } + + /* Fix: in general, returned BND.ADDR is variable length parameter by RFC + 1928, so the reply packet should be read until the end to avoid errors + at subsequent protocol level. + + +----+-----+-------+------+----------+----------+ + |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + + ATYP: + o IP v4 address: X'01', BND.ADDR = 4 byte + o domain name: X'03', BND.ADDR = [ 1 byte length, string ] + o IP v6 address: X'04', BND.ADDR = 16 byte + */ + + /* Calculate real packet size */ + if(socksreq[3] == 3) { + /* domain name */ + int addrlen = (int) socksreq[4]; + len = 5 + addrlen + 2; + } + else if(socksreq[3] == 4) { + /* IPv6 */ + len = 4 + 16 + 2; + } + else if(socksreq[3] == 1) { + len = 4 + 4 + 2; + } + else { + failf(data, "SOCKS5 reply has wrong address type."); + return CURLPX_BAD_ADDRESS_TYPE; + } + + /* At this point we already read first 10 bytes */ +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + if(!conn->socks5_gssapi_enctype) { + /* decrypt_gssapi_blockread already read the whole packet */ +#endif + if(len > 10) { + DEBUGASSERT(len <= sizeof(sx->buffer)); + sx->outstanding = len - 10; /* get the rest */ + sx->outp = &socksreq[10]; + sxstate(sx, data, CONNECT_REQ_READ_MORE); + } + else { + sxstate(sx, data, CONNECT_DONE); + break; + } +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + } +#endif + FALLTHROUGH(); + case CONNECT_REQ_READ_MORE: + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS, + "SOCKS5 connect request address"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ + return CURLPX_OK; + } + sxstate(sx, data, CONNECT_DONE); + } + infof(data, "SOCKS5 request granted."); + + 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' is not 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->do_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + + if(!sx) { + sx = calloc(1, sizeof(*sx)); + 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_verboseconnect(data, conn, cf->sockindex); + socks_proxy_cf_free(cf); + } + + *done = cf->connected; + return result; +} + +static void socks_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct socks_state *sx = cf->ctx; + + if(!cf->connected && sx) { + /* If we are not connected, the filter below is and has nothing + * to wait on, we determine what to wait for. */ + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + switch(sx->state) { + case CONNECT_RESOLVING: + case CONNECT_SOCKS_READ: + case CONNECT_AUTH_READ: + case CONNECT_REQ_READ: + case CONNECT_REQ_READ_MORE: + Curl_pollset_set_in_only(data, ps, sock); + break; + default: + Curl_pollset_set_out_only(data, ps, sock); + break; + } + } +} + +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->do_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_cf_get_host(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char **phost, + const char **pdisplay_host, + int *pport) +{ + (void)data; + if(!cf->connected) { + *phost = cf->conn->socks_proxy.host.name; + *pdisplay_host = cf->conn->http_proxy.host.dispname; + *pport = (int)cf->conn->socks_proxy.port; + } + else { + cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); + } +} + +struct Curl_cftype Curl_cft_socks_proxy = { + "SOCKS-PROXYY", + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, + 0, + socks_proxy_cf_destroy, + socks_proxy_cf_connect, + socks_proxy_cf_close, + Curl_cf_def_shutdown, + socks_cf_get_host, + socks_cf_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + (void)data; + result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); + return result; +} + +#endif /* CURL_DISABLE_PROXY */ diff --git a/third_party/curl/lib/socks.h b/third_party/curl/lib/socks.h new file mode 100644 index 0000000000..a3adcc6e91 --- /dev/null +++ b/third_party/curl/lib/socks.h @@ -0,0 +1,61 @@ +#ifndef HEADER_CURL_SOCKS_H +#define HEADER_CURL_SOCKS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef CURL_DISABLE_PROXY +#define Curl_SOCKS4(a,b,c,d,e) CURLE_NOT_BUILT_IN +#define Curl_SOCKS5(a,b,c,d,e,f) CURLE_NOT_BUILT_IN +#define Curl_SOCKS_getsock(x,y,z) 0 +#else +/* + * Helper read-from-socket functions. Does the same as Curl_read() but it + * blocks until all bytes amount of buffersize will be read. No more, no less. + * + * This is STUPID BLOCKING behavior + */ +int Curl_blockread_all(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, + ssize_t buffersize, + ssize_t *n); + +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) +/* + * This function handles the SOCKS5 GSS-API negotiation and initialization + */ +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + struct Curl_easy *data); +#endif + +CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_socks_proxy; + +#endif /* CURL_DISABLE_PROXY */ + +#endif /* HEADER_CURL_SOCKS_H */ diff --git a/third_party/curl/lib/socks_gssapi.c b/third_party/curl/lib/socks_gssapi.c new file mode 100644 index 0000000000..edcc683261 --- /dev/null +++ b/third_party/curl/lib/socks_gssapi.c @@ -0,0 +1,535 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Markus Moeller, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_PROXY) + +#include "curl_gssapi.h" +#include "urldata.h" +#include "sendf.h" +#include "cfilters.h" +#include "connect.h" +#include "timeval.h" +#include "socks.h" +#include "warnless.h" +#include "strdup.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; + +/* + * Helper GSS-API error functions. + */ +static int check_gss_err(struct Curl_easy *data, + OM_uint32 major_status, + OM_uint32 minor_status, + const char *function) +{ + if(GSS_ERROR(major_status)) { + OM_uint32 maj_stat, min_stat; + OM_uint32 msg_ctx = 0; + gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER; + char buf[1024]; + size_t len; + + len = 0; + msg_ctx = 0; + while(!msg_ctx) { + /* convert major status code (GSS-API error) to text */ + maj_stat = gss_display_status(&min_stat, major_status, + GSS_C_GSS_CODE, + GSS_C_NULL_OID, + &msg_ctx, &status_string); + if(maj_stat == GSS_S_COMPLETE) { + if(sizeof(buf) > len + status_string.length + 1) { + strcpy(buf + len, (char *) status_string.value); + len += status_string.length; + } + gss_release_buffer(&min_stat, &status_string); + break; + } + gss_release_buffer(&min_stat, &status_string); + } + if(sizeof(buf) > len + 3) { + strcpy(buf + len, ".\n"); + len += 2; + } + msg_ctx = 0; + while(!msg_ctx) { + /* convert minor status code (underlying routine error) to text */ + maj_stat = gss_display_status(&min_stat, minor_status, + GSS_C_MECH_CODE, + GSS_C_NULL_OID, + &msg_ctx, &status_string); + if(maj_stat == GSS_S_COMPLETE) { + if(sizeof(buf) > len + status_string.length) + strcpy(buf + len, (char *) status_string.value); + gss_release_buffer(&min_stat, &status_string); + break; + } + gss_release_buffer(&min_stat, &status_string); + } + failf(data, "GSS-API error: %s failed: %s", function, buf); + return 1; + } + + return 0; +} + +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode code; + ssize_t actualread; + ssize_t nwritten; + int result; + OM_uint32 gss_major_status, gss_minor_status, gss_status; + OM_uint32 gss_ret_flags; + int gss_conf_state, gss_enc; + gss_buffer_desc service = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc *gss_token = GSS_C_NO_BUFFER; + gss_name_t server = GSS_C_NO_NAME; + gss_name_t gss_client_name = GSS_C_NO_NAME; + unsigned short us_length; + char *user = NULL; + unsigned char socksreq[4]; /* room for GSS-API exchange header only */ + const char *serviceptr = data->set.str[STRING_PROXY_SERVICE_NAME] ? + data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; + const size_t serviceptr_length = strlen(serviceptr); + + /* GSS-API request looks like + * +----+------+-----+----------------+ + * |VER | MTYP | LEN | TOKEN | + * +----+------+----------------------+ + * | 1 | 1 | 2 | up to 2^16 - 1 | + * +----+------+-----+----------------+ + */ + + /* prepare service name */ + if(strchr(serviceptr, '/')) { + service.length = serviceptr_length; + service.value = Curl_memdup(serviceptr, service.length); + if(!service.value) + return CURLE_OUT_OF_MEMORY; + + gss_major_status = gss_import_name(&gss_minor_status, &service, + (gss_OID) GSS_C_NULL_OID, &server); + } + else { + service.value = malloc(serviceptr_length + + strlen(conn->socks_proxy.host.name) + 2); + if(!service.value) + return CURLE_OUT_OF_MEMORY; + service.length = serviceptr_length + + strlen(conn->socks_proxy.host.name) + 1; + msnprintf(service.value, service.length + 1, "%s@%s", + serviceptr, conn->socks_proxy.host.name); + + gss_major_status = gss_import_name(&gss_minor_status, &service, + GSS_C_NT_HOSTBASED_SERVICE, &server); + } + + gss_release_buffer(&gss_status, &service); /* clear allocated memory */ + + if(check_gss_err(data, gss_major_status, + gss_minor_status, "gss_import_name()")) { + failf(data, "Failed to create service name."); + gss_release_name(&gss_status, &server); + return CURLE_COULDNT_CONNECT; + } + + (void)curlx_nonblock(sock, FALSE); + + /* As long as we need to keep sending some context info, and there is no */ + /* errors, keep sending it... */ + for(;;) { + gss_major_status = Curl_gss_init_sec_context(data, + &gss_minor_status, + &gss_context, + server, + &Curl_krb5_mech_oid, + NULL, + gss_token, + &gss_send_token, + TRUE, + &gss_ret_flags); + + if(gss_token != GSS_C_NO_BUFFER) + gss_release_buffer(&gss_status, &gss_recv_token); + if(check_gss_err(data, gss_major_status, + gss_minor_status, "gss_init_sec_context")) { + gss_release_name(&gss_status, &server); + gss_release_buffer(&gss_status, &gss_recv_token); + gss_release_buffer(&gss_status, &gss_send_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + failf(data, "Failed to initial GSS-API token."); + return CURLE_COULDNT_CONNECT; + } + + if(gss_send_token.length) { + socksreq[0] = 1; /* GSS-API subnegotiation version */ + socksreq[1] = 1; /* authentication message type */ + us_length = htons((unsigned short)gss_send_token.length); + memcpy(socksreq + 2, &us_length, sizeof(short)); + + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + if(code || (4 != nwritten)) { + failf(data, "Failed to send GSS-API authentication request."); + gss_release_name(&gss_status, &server); + gss_release_buffer(&gss_status, &gss_recv_token); + gss_release_buffer(&gss_status, &gss_send_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + nwritten = Curl_conn_cf_send(cf->next, data, + (char *)gss_send_token.value, + gss_send_token.length, &code); + if(code || ((ssize_t)gss_send_token.length != nwritten)) { + failf(data, "Failed to send GSS-API authentication token."); + gss_release_name(&gss_status, &server); + gss_release_buffer(&gss_status, &gss_recv_token); + gss_release_buffer(&gss_status, &gss_send_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + } + + gss_release_buffer(&gss_status, &gss_send_token); + gss_release_buffer(&gss_status, &gss_recv_token); + if(gss_major_status != GSS_S_CONTINUE_NEEDED) + break; + + /* analyse response */ + + /* GSS-API response looks like + * +----+------+-----+----------------+ + * |VER | MTYP | LEN | TOKEN | + * +----+------+----------------------+ + * | 1 | 1 | 2 | up to 2^16 - 1 | + * +----+------+-----+----------------+ + */ + + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + if(result || (actualread != 4)) { + failf(data, "Failed to receive GSS-API authentication response."); + gss_release_name(&gss_status, &server); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + /* ignore the first (VER) byte */ + if(socksreq[1] == 255) { /* status / message type */ + failf(data, "User was rejected by the SOCKS5 server (%d %d).", + socksreq[0], socksreq[1]); + gss_release_name(&gss_status, &server); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + if(socksreq[1] != 1) { /* status / message type */ + failf(data, "Invalid GSS-API authentication response type (%d %d).", + socksreq[0], socksreq[1]); + gss_release_name(&gss_status, &server); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + memcpy(&us_length, socksreq + 2, sizeof(short)); + us_length = ntohs(us_length); + + gss_recv_token.length = us_length; + gss_recv_token.value = malloc(us_length); + if(!gss_recv_token.value) { + failf(data, + "Could not allocate memory for GSS-API authentication " + "response token."); + gss_release_name(&gss_status, &server); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_OUT_OF_MEMORY; + } + + result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, + gss_recv_token.length, &actualread); + + if(result || (actualread != us_length)) { + failf(data, "Failed to receive GSS-API authentication token."); + gss_release_name(&gss_status, &server); + gss_release_buffer(&gss_status, &gss_recv_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + gss_token = &gss_recv_token; + } + + gss_release_name(&gss_status, &server); + + /* Everything is good so far, user was authenticated! */ + gss_major_status = gss_inquire_context(&gss_minor_status, gss_context, + &gss_client_name, NULL, NULL, NULL, + NULL, NULL, NULL); + if(check_gss_err(data, gss_major_status, + gss_minor_status, "gss_inquire_context")) { + gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, &gss_client_name); + failf(data, "Failed to determine username."); + return CURLE_COULDNT_CONNECT; + } + gss_major_status = gss_display_name(&gss_minor_status, gss_client_name, + &gss_send_token, NULL); + if(check_gss_err(data, gss_major_status, + gss_minor_status, "gss_display_name")) { + gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, &gss_client_name); + gss_release_buffer(&gss_status, &gss_send_token); + failf(data, "Failed to determine username."); + return CURLE_COULDNT_CONNECT; + } + user = malloc(gss_send_token.length + 1); + if(!user) { + gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, &gss_client_name); + gss_release_buffer(&gss_status, &gss_send_token); + return CURLE_OUT_OF_MEMORY; + } + + memcpy(user, gss_send_token.value, gss_send_token.length); + user[gss_send_token.length] = '\0'; + gss_release_name(&gss_status, &gss_client_name); + gss_release_buffer(&gss_status, &gss_send_token); + infof(data, "SOCKS5 server authenticated user %s with GSS-API.",user); + free(user); + user = NULL; + + /* Do encryption */ + socksreq[0] = 1; /* GSS-API subnegotiation version */ + socksreq[1] = 2; /* encryption message type */ + + gss_enc = 0; /* no data protection */ + /* do confidentiality protection if supported */ + if(gss_ret_flags & GSS_C_CONF_FLAG) + gss_enc = 2; + /* else do integrity protection */ + else if(gss_ret_flags & GSS_C_INTEG_FLAG) + gss_enc = 1; + + infof(data, "SOCKS5 server supports GSS-API %s data protection.", + (gss_enc == 0)?"no":((gss_enc==1)?"integrity":"confidentiality")); + /* force for the moment to no data protection */ + gss_enc = 0; + /* + * Sending the encryption type in clear seems wrong. It should be + * protected with gss_seal()/gss_wrap(). See RFC1961 extract below + * The NEC reference implementations on which this is based is + * therefore at fault + * + * +------+------+------+.......................+ + * + ver | mtyp | len | token | + * +------+------+------+.......................+ + * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | + * +------+------+------+.......................+ + * + * Where: + * + * - "ver" is the protocol version number, here 1 to represent the + * first version of the SOCKS/GSS-API protocol + * + * - "mtyp" is the message type, here 2 to represent a protection + * -level negotiation message + * + * - "len" is the length of the "token" field in octets + * + * - "token" is the GSS-API encapsulated protection level + * + * The token is produced by encapsulating an octet containing the + * required protection level using gss_seal()/gss_wrap() with conf_req + * set to FALSE. The token is verified using gss_unseal()/ + * gss_unwrap(). + * + */ + if(data->set.socks5_gssapi_nec) { + us_length = htons((short)1); + memcpy(socksreq + 2, &us_length, sizeof(short)); + } + else { + gss_send_token.length = 1; + gss_send_token.value = Curl_memdup(&gss_enc, 1); + if(!gss_send_token.value) { + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_OUT_OF_MEMORY; + } + + gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0, + GSS_C_QOP_DEFAULT, &gss_send_token, + &gss_conf_state, &gss_w_token); + + if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { + gss_release_buffer(&gss_status, &gss_send_token); + gss_release_buffer(&gss_status, &gss_w_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + failf(data, "Failed to wrap GSS-API encryption value into token."); + return CURLE_COULDNT_CONNECT; + } + gss_release_buffer(&gss_status, &gss_send_token); + + us_length = htons((unsigned short)gss_w_token.length); + memcpy(socksreq + 2, &us_length, sizeof(short)); + } + + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + if(code || (4 != nwritten)) { + failf(data, "Failed to send GSS-API encryption request."); + gss_release_buffer(&gss_status, &gss_w_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + if(data->set.socks5_gssapi_nec) { + memcpy(socksreq, &gss_enc, 1); + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code); + if(code || ( 1 != nwritten)) { + failf(data, "Failed to send GSS-API encryption type."); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + } + else { + nwritten = Curl_conn_cf_send(cf->next, data, + (char *)gss_w_token.value, + gss_w_token.length, &code); + if(code || ((ssize_t)gss_w_token.length != nwritten)) { + failf(data, "Failed to send GSS-API encryption type."); + gss_release_buffer(&gss_status, &gss_w_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + gss_release_buffer(&gss_status, &gss_w_token); + } + + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + if(result || (actualread != 4)) { + failf(data, "Failed to receive GSS-API encryption response."); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + /* ignore the first (VER) byte */ + if(socksreq[1] == 255) { /* status / message type */ + failf(data, "User was rejected by the SOCKS5 server (%d %d).", + socksreq[0], socksreq[1]); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + if(socksreq[1] != 2) { /* status / message type */ + failf(data, "Invalid GSS-API encryption response type (%d %d).", + socksreq[0], socksreq[1]); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + memcpy(&us_length, socksreq + 2, sizeof(short)); + us_length = ntohs(us_length); + + gss_recv_token.length = us_length; + gss_recv_token.value = malloc(gss_recv_token.length); + if(!gss_recv_token.value) { + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_OUT_OF_MEMORY; + } + result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, + gss_recv_token.length, &actualread); + + if(result || (actualread != us_length)) { + failf(data, "Failed to receive GSS-API encryption type."); + gss_release_buffer(&gss_status, &gss_recv_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + if(!data->set.socks5_gssapi_nec) { + gss_major_status = gss_unwrap(&gss_minor_status, gss_context, + &gss_recv_token, &gss_w_token, + 0, GSS_C_QOP_DEFAULT); + + if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { + gss_release_buffer(&gss_status, &gss_recv_token); + gss_release_buffer(&gss_status, &gss_w_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + failf(data, "Failed to unwrap GSS-API encryption value into token."); + return CURLE_COULDNT_CONNECT; + } + gss_release_buffer(&gss_status, &gss_recv_token); + + if(gss_w_token.length != 1) { + failf(data, "Invalid GSS-API encryption response length (%zu).", + gss_w_token.length); + gss_release_buffer(&gss_status, &gss_w_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + memcpy(socksreq, gss_w_token.value, gss_w_token.length); + gss_release_buffer(&gss_status, &gss_w_token); + } + else { + if(gss_recv_token.length != 1) { + failf(data, "Invalid GSS-API encryption response length (%zu).", + gss_recv_token.length); + gss_release_buffer(&gss_status, &gss_recv_token); + gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + + memcpy(socksreq, gss_recv_token.value, gss_recv_token.length); + gss_release_buffer(&gss_status, &gss_recv_token); + } + + (void)curlx_nonblock(sock, TRUE); + + infof(data, "SOCKS5 access with%s protection granted.", + (socksreq[0] == 0)?"out GSS-API data": + ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality")); + + conn->socks5_gssapi_enctype = socksreq[0]; + if(socksreq[0] == 0) + gss_delete_sec_context(&gss_status, &gss_context, NULL); + + return CURLE_OK; +} + +#endif /* HAVE_GSSAPI && !CURL_DISABLE_PROXY */ diff --git a/third_party/curl/lib/socks_sspi.c b/third_party/curl/lib/socks_sspi.c new file mode 100644 index 0000000000..a83288dd00 --- /dev/null +++ b/third_party/curl/lib/socks_sspi.c @@ -0,0 +1,620 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Markus Moeller, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_PROXY) + +#include "urldata.h" +#include "sendf.h" +#include "cfilters.h" +#include "connect.h" +#include "strerror.h" +#include "timeval.h" +#include "socks.h" +#include "curl_sspi.h" +#include "curl_multibyte.h" +#include "warnless.h" +#include "strdup.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Helper sspi error functions. + */ +static int check_sspi_err(struct Curl_easy *data, + SECURITY_STATUS status, + const char *function) +{ + if(status != SEC_E_OK && + status != SEC_I_COMPLETE_AND_CONTINUE && + status != SEC_I_COMPLETE_NEEDED && + status != SEC_I_CONTINUE_NEEDED) { + char buffer[STRERROR_LEN]; + failf(data, "SSPI error: %s failed: %s", function, + Curl_sspi_strerror(status, buffer, sizeof(buffer))); + return 1; + } + return 0; +} + +/* This is the SSPI-using version of this function */ +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode code; + ssize_t actualread; + ssize_t written; + int result; + /* Needs GSS-API authentication */ + SECURITY_STATUS status; + unsigned long sspi_ret_flags = 0; + unsigned char gss_enc; + SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3]; + SecBufferDesc input_desc, output_desc, wrap_desc; + SecPkgContext_Sizes sspi_sizes; + CredHandle cred_handle; + CtxtHandle sspi_context; + PCtxtHandle context_handle = NULL; + SecPkgCredentials_Names names; + TimeStamp expiry; + char *service_name = NULL; + unsigned short us_length; + unsigned long qop; + unsigned char socksreq[4]; /* room for GSS-API exchange header only */ + const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ? + data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; + const size_t service_length = strlen(service); + + /* GSS-API request looks like + * +----+------+-----+----------------+ + * |VER | MTYP | LEN | TOKEN | + * +----+------+----------------------+ + * | 1 | 1 | 2 | up to 2^16 - 1 | + * +----+------+-----+----------------+ + */ + + /* prepare service name */ + if(strchr(service, '/')) { + service_name = strdup(service); + if(!service_name) + return CURLE_OUT_OF_MEMORY; + } + else { + service_name = malloc(service_length + + strlen(conn->socks_proxy.host.name) + 2); + if(!service_name) + return CURLE_OUT_OF_MEMORY; + msnprintf(service_name, service_length + + strlen(conn->socks_proxy.host.name) + 2, "%s/%s", + service, conn->socks_proxy.host.name); + } + + input_desc.cBuffers = 1; + input_desc.pBuffers = &sspi_recv_token; + input_desc.ulVersion = SECBUFFER_VERSION; + + sspi_recv_token.BufferType = SECBUFFER_TOKEN; + sspi_recv_token.cbBuffer = 0; + sspi_recv_token.pvBuffer = NULL; + + output_desc.cBuffers = 1; + output_desc.pBuffers = &sspi_send_token; + output_desc.ulVersion = SECBUFFER_VERSION; + + sspi_send_token.BufferType = SECBUFFER_TOKEN; + sspi_send_token.cbBuffer = 0; + sspi_send_token.pvBuffer = NULL; + + wrap_desc.cBuffers = 3; + wrap_desc.pBuffers = sspi_w_token; + wrap_desc.ulVersion = SECBUFFER_VERSION; + + cred_handle.dwLower = 0; + cred_handle.dwUpper = 0; + + status = s_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *) TEXT("Kerberos"), + SECPKG_CRED_OUTBOUND, + NULL, + NULL, + NULL, + NULL, + &cred_handle, + &expiry); + + if(check_sspi_err(data, status, "AcquireCredentialsHandle")) { + failf(data, "Failed to acquire credentials."); + free(service_name); + s_pSecFn->FreeCredentialsHandle(&cred_handle); + return CURLE_COULDNT_CONNECT; + } + + (void)curlx_nonblock(sock, FALSE); + + /* As long as we need to keep sending some context info, and there is no */ + /* errors, keep sending it... */ + for(;;) { + TCHAR *sname; + + sname = curlx_convert_UTF8_to_tchar(service_name); + if(!sname) + return CURLE_OUT_OF_MEMORY; + + status = s_pSecFn->InitializeSecurityContext(&cred_handle, + context_handle, + sname, + ISC_REQ_MUTUAL_AUTH | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_CONFIDENTIALITY | + ISC_REQ_REPLAY_DETECT, + 0, + SECURITY_NATIVE_DREP, + &input_desc, + 0, + &sspi_context, + &output_desc, + &sspi_ret_flags, + &expiry); + + curlx_unicodefree(sname); + + if(sspi_recv_token.pvBuffer) { + s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + sspi_recv_token.pvBuffer = NULL; + sspi_recv_token.cbBuffer = 0; + } + + if(check_sspi_err(data, status, "InitializeSecurityContext")) { + free(service_name); + s_pSecFn->FreeCredentialsHandle(&cred_handle); + s_pSecFn->DeleteSecurityContext(&sspi_context); + if(sspi_recv_token.pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + failf(data, "Failed to initialise security context."); + return CURLE_COULDNT_CONNECT; + } + + if(sspi_send_token.cbBuffer) { + socksreq[0] = 1; /* GSS-API subnegotiation version */ + socksreq[1] = 1; /* authentication message type */ + us_length = htons((unsigned short)sspi_send_token.cbBuffer); + memcpy(socksreq + 2, &us_length, sizeof(short)); + + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + if(code || (4 != written)) { + failf(data, "Failed to send SSPI authentication request."); + free(service_name); + if(sspi_send_token.pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + if(sspi_recv_token.pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + s_pSecFn->FreeCredentialsHandle(&cred_handle); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + written = Curl_conn_cf_send(cf->next, data, + (char *)sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, &code); + if(code || (sspi_send_token.cbBuffer != (size_t)written)) { + failf(data, "Failed to send SSPI authentication token."); + free(service_name); + if(sspi_send_token.pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + if(sspi_recv_token.pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + s_pSecFn->FreeCredentialsHandle(&cred_handle); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + } + + if(sspi_send_token.pvBuffer) { + s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + sspi_send_token.pvBuffer = NULL; + } + sspi_send_token.cbBuffer = 0; + + if(sspi_recv_token.pvBuffer) { + s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + sspi_recv_token.pvBuffer = NULL; + } + sspi_recv_token.cbBuffer = 0; + + if(status != SEC_I_CONTINUE_NEEDED) + break; + + /* analyse response */ + + /* GSS-API response looks like + * +----+------+-----+----------------+ + * |VER | MTYP | LEN | TOKEN | + * +----+------+----------------------+ + * | 1 | 1 | 2 | up to 2^16 - 1 | + * +----+------+-----+----------------+ + */ + + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + if(result || (actualread != 4)) { + failf(data, "Failed to receive SSPI authentication response."); + free(service_name); + s_pSecFn->FreeCredentialsHandle(&cred_handle); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + /* ignore the first (VER) byte */ + if(socksreq[1] == 255) { /* status / message type */ + failf(data, "User was rejected by the SOCKS5 server (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); + free(service_name); + s_pSecFn->FreeCredentialsHandle(&cred_handle); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + if(socksreq[1] != 1) { /* status / message type */ + failf(data, "Invalid SSPI authentication response type (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); + free(service_name); + s_pSecFn->FreeCredentialsHandle(&cred_handle); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + memcpy(&us_length, socksreq + 2, sizeof(short)); + us_length = ntohs(us_length); + + sspi_recv_token.cbBuffer = us_length; + sspi_recv_token.pvBuffer = malloc(us_length); + + if(!sspi_recv_token.pvBuffer) { + free(service_name); + s_pSecFn->FreeCredentialsHandle(&cred_handle); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_OUT_OF_MEMORY; + } + result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer, + sspi_recv_token.cbBuffer, &actualread); + + if(result || (actualread != us_length)) { + failf(data, "Failed to receive SSPI authentication token."); + free(service_name); + if(sspi_recv_token.pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + s_pSecFn->FreeCredentialsHandle(&cred_handle); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + context_handle = &sspi_context; + } + + free(service_name); + + /* Everything is good so far, user was authenticated! */ + status = s_pSecFn->QueryCredentialsAttributes(&cred_handle, + SECPKG_CRED_ATTR_NAMES, + &names); + s_pSecFn->FreeCredentialsHandle(&cred_handle); + if(check_sspi_err(data, status, "QueryCredentialAttributes")) { + s_pSecFn->DeleteSecurityContext(&sspi_context); + s_pSecFn->FreeContextBuffer(names.sUserName); + failf(data, "Failed to determine username."); + return CURLE_COULDNT_CONNECT; + } + else { +#ifndef CURL_DISABLE_VERBOSE_STRINGS + char *user_utf8 = curlx_convert_tchar_to_UTF8(names.sUserName); + infof(data, "SOCKS5 server authenticated user %s with GSS-API.", + (user_utf8 ? user_utf8 : "(unknown)")); + curlx_unicodefree(user_utf8); +#endif + s_pSecFn->FreeContextBuffer(names.sUserName); + } + + /* Do encryption */ + socksreq[0] = 1; /* GSS-API subnegotiation version */ + socksreq[1] = 2; /* encryption message type */ + + gss_enc = 0; /* no data protection */ + /* do confidentiality protection if supported */ + if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY) + gss_enc = 2; + /* else do integrity protection */ + else if(sspi_ret_flags & ISC_REQ_INTEGRITY) + gss_enc = 1; + + infof(data, "SOCKS5 server supports GSS-API %s data protection.", + (gss_enc == 0)?"no":((gss_enc == 1)?"integrity":"confidentiality") ); + /* force to no data protection, avoid encryption/decryption for now */ + gss_enc = 0; + /* + * Sending the encryption type in clear seems wrong. It should be + * protected with gss_seal()/gss_wrap(). See RFC1961 extract below + * The NEC reference implementations on which this is based is + * therefore at fault + * + * +------+------+------+.......................+ + * + ver | mtyp | len | token | + * +------+------+------+.......................+ + * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | + * +------+------+------+.......................+ + * + * Where: + * + * - "ver" is the protocol version number, here 1 to represent the + * first version of the SOCKS/GSS-API protocol + * + * - "mtyp" is the message type, here 2 to represent a protection + * -level negotiation message + * + * - "len" is the length of the "token" field in octets + * + * - "token" is the GSS-API encapsulated protection level + * + * The token is produced by encapsulating an octet containing the + * required protection level using gss_seal()/gss_wrap() with conf_req + * set to FALSE. The token is verified using gss_unseal()/ + * gss_unwrap(). + * + */ + + if(data->set.socks5_gssapi_nec) { + us_length = htons((unsigned short)1); + memcpy(socksreq + 2, &us_length, sizeof(short)); + } + else { + status = s_pSecFn->QueryContextAttributes(&sspi_context, + SECPKG_ATTR_SIZES, + &sspi_sizes); + if(check_sspi_err(data, status, "QueryContextAttributes")) { + s_pSecFn->DeleteSecurityContext(&sspi_context); + failf(data, "Failed to query security context attributes."); + return CURLE_COULDNT_CONNECT; + } + + sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer; + sspi_w_token[0].BufferType = SECBUFFER_TOKEN; + sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer); + + if(!sspi_w_token[0].pvBuffer) { + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_OUT_OF_MEMORY; + } + + sspi_w_token[1].cbBuffer = 1; + sspi_w_token[1].pvBuffer = malloc(1); + if(!sspi_w_token[1].pvBuffer) { + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_OUT_OF_MEMORY; + } + + memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1); + sspi_w_token[2].BufferType = SECBUFFER_PADDING; + sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize; + sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize); + if(!sspi_w_token[2].pvBuffer) { + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_OUT_OF_MEMORY; + } + status = s_pSecFn->EncryptMessage(&sspi_context, + KERB_WRAP_NO_ENCRYPT, + &wrap_desc, + 0); + if(check_sspi_err(data, status, "EncryptMessage")) { + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); + s_pSecFn->DeleteSecurityContext(&sspi_context); + failf(data, "Failed to query security context attributes."); + return CURLE_COULDNT_CONNECT; + } + sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer + + sspi_w_token[1].cbBuffer + + sspi_w_token[2].cbBuffer; + sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer); + if(!sspi_send_token.pvBuffer) { + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_OUT_OF_MEMORY; + } + + memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer, + sspi_w_token[0].cbBuffer); + memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer, + sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); + memcpy((PUCHAR) sspi_send_token.pvBuffer + + sspi_w_token[0].cbBuffer + + sspi_w_token[1].cbBuffer, + sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer); + + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + sspi_w_token[0].pvBuffer = NULL; + sspi_w_token[0].cbBuffer = 0; + s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + sspi_w_token[1].pvBuffer = NULL; + sspi_w_token[1].cbBuffer = 0; + s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); + sspi_w_token[2].pvBuffer = NULL; + sspi_w_token[2].cbBuffer = 0; + + us_length = htons((unsigned short)sspi_send_token.cbBuffer); + memcpy(socksreq + 2, &us_length, sizeof(short)); + } + + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + if(code || (4 != written)) { + failf(data, "Failed to send SSPI encryption request."); + if(sspi_send_token.pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + if(data->set.socks5_gssapi_nec) { + memcpy(socksreq, &gss_enc, 1); + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code); + if(code || (1 != written)) { + failf(data, "Failed to send SSPI encryption type."); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + } + else { + written = Curl_conn_cf_send(cf->next, data, + (char *)sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, &code); + if(code || (sspi_send_token.cbBuffer != (size_t)written)) { + failf(data, "Failed to send SSPI encryption type."); + if(sspi_send_token.pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + if(sspi_send_token.pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + } + + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + if(result || (actualread != 4)) { + failf(data, "Failed to receive SSPI encryption response."); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + /* ignore the first (VER) byte */ + if(socksreq[1] == 255) { /* status / message type */ + failf(data, "User was rejected by the SOCKS5 server (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + if(socksreq[1] != 2) { /* status / message type */ + failf(data, "Invalid SSPI encryption response type (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + memcpy(&us_length, socksreq + 2, sizeof(short)); + us_length = ntohs(us_length); + + sspi_w_token[0].cbBuffer = us_length; + sspi_w_token[0].pvBuffer = malloc(us_length); + if(!sspi_w_token[0].pvBuffer) { + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_OUT_OF_MEMORY; + } + + result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer, + sspi_w_token[0].cbBuffer, &actualread); + + if(result || (actualread != us_length)) { + failf(data, "Failed to receive SSPI encryption type."); + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + + if(!data->set.socks5_gssapi_nec) { + wrap_desc.cBuffers = 2; + sspi_w_token[0].BufferType = SECBUFFER_STREAM; + sspi_w_token[1].BufferType = SECBUFFER_DATA; + sspi_w_token[1].cbBuffer = 0; + sspi_w_token[1].pvBuffer = NULL; + + status = s_pSecFn->DecryptMessage(&sspi_context, + &wrap_desc, + 0, + &qop); + + if(check_sspi_err(data, status, "DecryptMessage")) { + if(sspi_w_token[0].pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + if(sspi_w_token[1].pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + s_pSecFn->DeleteSecurityContext(&sspi_context); + failf(data, "Failed to query security context attributes."); + return CURLE_COULDNT_CONNECT; + } + + if(sspi_w_token[1].cbBuffer != 1) { + failf(data, "Invalid SSPI encryption response length (%lu).", + (unsigned long)sspi_w_token[1].cbBuffer); + if(sspi_w_token[0].pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + if(sspi_w_token[1].pvBuffer) + s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + + memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + } + else { + if(sspi_w_token[0].cbBuffer != 1) { + failf(data, "Invalid SSPI encryption response length (%lu).", + (unsigned long)sspi_w_token[0].cbBuffer); + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + s_pSecFn->DeleteSecurityContext(&sspi_context); + return CURLE_COULDNT_CONNECT; + } + memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); + s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + } + (void)curlx_nonblock(sock, TRUE); + + infof(data, "SOCKS5 access with%s protection granted.", + (socksreq[0] == 0)?"out GSS-API data": + ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality")); + + /* For later use if encryption is required + conn->socks5_gssapi_enctype = socksreq[0]; + if(socksreq[0] != 0) + conn->socks5_sspi_context = sspi_context; + else { + s_pSecFn->DeleteSecurityContext(&sspi_context); + conn->socks5_sspi_context = sspi_context; + } + */ + return CURLE_OK; +} +#endif diff --git a/third_party/curl/lib/speedcheck.c b/third_party/curl/lib/speedcheck.c new file mode 100644 index 0000000000..580efbde75 --- /dev/null +++ b/third_party/curl/lib/speedcheck.c @@ -0,0 +1,79 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include +#include "urldata.h" +#include "sendf.h" +#include "multiif.h" +#include "speedcheck.h" + +void Curl_speedinit(struct Curl_easy *data) +{ + memset(&data->state.keeps_speed, 0, sizeof(struct curltime)); +} + +/* + * @unittest: 1606 + */ +CURLcode Curl_speedcheck(struct Curl_easy *data, + struct curltime now) +{ + if(data->req.keepon & KEEP_RECV_PAUSE) + /* A paused transfer is not qualified for speed checks */ + return CURLE_OK; + + if((data->progress.current_speed >= 0) && data->set.low_speed_time) { + if(data->progress.current_speed < data->set.low_speed_limit) { + if(!data->state.keeps_speed.tv_sec) + /* under the limit at this very moment */ + data->state.keeps_speed = now; + else { + /* how long has it been under the limit */ + timediff_t howlong = Curl_timediff(now, data->state.keeps_speed); + + if(howlong >= data->set.low_speed_time * 1000) { + /* too long */ + failf(data, + "Operation too slow. " + "Less than %ld bytes/sec transferred the last %ld seconds", + data->set.low_speed_limit, + data->set.low_speed_time); + return CURLE_OPERATION_TIMEDOUT; + } + } + } + else + /* faster right now */ + data->state.keeps_speed.tv_sec = 0; + } + + if(data->set.low_speed_limit) + /* if low speed limit is enabled, set the expire timer to make this + connection's speed get checked again in a second */ + Curl_expire(data, 1000, EXPIRE_SPEEDCHECK); + + return CURLE_OK; +} diff --git a/third_party/curl/lib/speedcheck.h b/third_party/curl/lib/speedcheck.h new file mode 100644 index 0000000000..bff2f32b77 --- /dev/null +++ b/third_party/curl/lib/speedcheck.h @@ -0,0 +1,35 @@ +#ifndef HEADER_CURL_SPEEDCHECK_H +#define HEADER_CURL_SPEEDCHECK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "timeval.h" + +void Curl_speedinit(struct Curl_easy *data); +CURLcode Curl_speedcheck(struct Curl_easy *data, + struct curltime now); + +#endif /* HEADER_CURL_SPEEDCHECK_H */ diff --git a/third_party/curl/lib/splay.c b/third_party/curl/lib/splay.c new file mode 100644 index 0000000000..99bb149971 --- /dev/null +++ b/third_party/curl/lib/splay.c @@ -0,0 +1,278 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "splay.h" + +/* + * This macro compares two node keys i and j and returns: + * + * negative value: when i is smaller than j + * zero : when i is equal to j + * positive when : when i is larger than j + */ +#define compare(i,j) Curl_splaycomparekeys((i),(j)) + +/* + * Splay using the key i (which may or may not be in the tree.) The starting + * root is t. + */ +struct Curl_tree *Curl_splay(struct curltime i, + struct Curl_tree *t) +{ + struct Curl_tree N, *l, *r, *y; + + if(!t) + return t; + N.smaller = N.larger = NULL; + l = r = &N; + + for(;;) { + long comp = compare(i, t->key); + if(comp < 0) { + if(!t->smaller) + break; + if(compare(i, t->smaller->key) < 0) { + y = t->smaller; /* rotate smaller */ + t->smaller = y->larger; + y->larger = t; + t = y; + if(!t->smaller) + break; + } + r->smaller = t; /* link smaller */ + r = t; + t = t->smaller; + } + else if(comp > 0) { + if(!t->larger) + break; + if(compare(i, t->larger->key) > 0) { + y = t->larger; /* rotate larger */ + t->larger = y->smaller; + y->smaller = t; + t = y; + if(!t->larger) + break; + } + l->larger = t; /* link larger */ + l = t; + t = t->larger; + } + else + break; + } + + l->larger = t->smaller; /* assemble */ + r->smaller = t->larger; + t->smaller = N.larger; + t->larger = N.smaller; + + return t; +} + +/* Insert key i into the tree t. Return a pointer to the resulting tree or + * NULL if something went wrong. + * + * @unittest: 1309 + */ +struct Curl_tree *Curl_splayinsert(struct curltime i, + struct Curl_tree *t, + struct Curl_tree *node) +{ + static const struct curltime KEY_NOTUSED = { + ~0, -1 + }; /* will *NEVER* appear */ + + if(!node) + return t; + + if(t) { + t = Curl_splay(i, t); + if(compare(i, t->key) == 0) { + /* There already exists a node in the tree with the very same key. Build + a doubly-linked circular list of nodes. We add the new 'node' struct + to the end of this list. */ + + node->key = KEY_NOTUSED; /* we set the key in the sub node to NOTUSED + to quickly identify this node as a subnode */ + node->samen = t; + node->samep = t->samep; + t->samep->samen = node; + t->samep = node; + + return t; /* the root node always stays the same */ + } + } + + if(!t) { + node->smaller = node->larger = NULL; + } + else if(compare(i, t->key) < 0) { + node->smaller = t->smaller; + node->larger = t; + t->smaller = NULL; + + } + else { + node->larger = t->larger; + node->smaller = t; + t->larger = NULL; + } + node->key = i; + + /* no identical nodes (yet), we are the only one in the list of nodes */ + node->samen = node; + node->samep = node; + return node; +} + +/* Finds and deletes the best-fit node from the tree. Return a pointer to the + resulting tree. best-fit means the smallest node if it is not larger than + the key */ +struct Curl_tree *Curl_splaygetbest(struct curltime i, + struct Curl_tree *t, + struct Curl_tree **removed) +{ + static const struct curltime tv_zero = {0, 0}; + struct Curl_tree *x; + + if(!t) { + *removed = NULL; /* none removed since there was no root */ + return NULL; + } + + /* find smallest */ + t = Curl_splay(tv_zero, t); + if(compare(i, t->key) < 0) { + /* even the smallest is too big */ + *removed = NULL; + return t; + } + + /* FIRST! Check if there is a list with identical keys */ + x = t->samen; + if(x != t) { + /* there is, pick one from the list */ + + /* 'x' is the new root node */ + + x->key = t->key; + x->larger = t->larger; + x->smaller = t->smaller; + x->samep = t->samep; + t->samep->samen = x; + + *removed = t; + return x; /* new root */ + } + + /* we splayed the tree to the smallest element, there is no smaller */ + x = t->larger; + *removed = t; + + return x; +} + + +/* Deletes the very node we point out from the tree if it is there. Stores a + * pointer to the new resulting tree in 'newroot'. + * + * Returns zero on success and non-zero on errors! + * When returning error, it does not touch the 'newroot' pointer. + * + * NOTE: when the last node of the tree is removed, there is no tree left so + * 'newroot' will be made to point to NULL. + * + * @unittest: 1309 + */ +int Curl_splayremove(struct Curl_tree *t, + struct Curl_tree *removenode, + struct Curl_tree **newroot) +{ + static const struct curltime KEY_NOTUSED = { + ~0, -1 + }; /* will *NEVER* appear */ + struct Curl_tree *x; + + if(!t || !removenode) + return 1; + + if(compare(KEY_NOTUSED, removenode->key) == 0) { + /* Key set to NOTUSED means it is a subnode within a 'same' linked list + and thus we can unlink it easily. */ + if(removenode->samen == removenode) + /* A non-subnode should never be set to KEY_NOTUSED */ + return 3; + + removenode->samep->samen = removenode->samen; + removenode->samen->samep = removenode->samep; + + /* Ensures that double-remove gets caught. */ + removenode->samen = removenode; + + *newroot = t; /* return the same root */ + return 0; + } + + t = Curl_splay(removenode->key, t); + + /* First make sure that we got the same root node as the one we want + to remove, as otherwise we might be trying to remove a node that + is not actually in the tree. + + We cannot just compare the keys here as a double remove in quick + succession of a node with key != KEY_NOTUSED && same != NULL + could return the same key but a different node. */ + if(t != removenode) + return 2; + + /* Check if there is a list with identical sizes, as then we are trying to + remove the root node of a list of nodes with identical keys. */ + x = t->samen; + if(x != t) { + /* 'x' is the new root node, we just make it use the root node's + smaller/larger links */ + + x->key = t->key; + x->larger = t->larger; + x->smaller = t->smaller; + x->samep = t->samep; + t->samep->samen = x; + } + else { + /* Remove the root node */ + if(!t->smaller) + x = t->larger; + else { + x = Curl_splay(removenode->key, t->smaller); + x->larger = t->larger; + } + } + + *newroot = x; /* store new root pointer */ + + return 0; +} diff --git a/third_party/curl/lib/splay.h b/third_party/curl/lib/splay.h new file mode 100644 index 0000000000..20b2bb69a9 --- /dev/null +++ b/third_party/curl/lib/splay.h @@ -0,0 +1,58 @@ +#ifndef HEADER_CURL_SPLAY_H +#define HEADER_CURL_SPLAY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include "timeval.h" + +struct Curl_tree { + struct Curl_tree *smaller; /* smaller node */ + struct Curl_tree *larger; /* larger node */ + struct Curl_tree *samen; /* points to the next node with identical key */ + struct Curl_tree *samep; /* points to the prev node with identical key */ + struct curltime key; /* this node's "sort" key */ + void *payload; /* data the splay code does not care about */ +}; + +struct Curl_tree *Curl_splay(struct curltime i, + struct Curl_tree *t); + +struct Curl_tree *Curl_splayinsert(struct curltime key, + struct Curl_tree *t, + struct Curl_tree *newnode); + +struct Curl_tree *Curl_splaygetbest(struct curltime key, + struct Curl_tree *t, + struct Curl_tree **removed); + +int Curl_splayremove(struct Curl_tree *t, + struct Curl_tree *removenode, + struct Curl_tree **newroot); + +#define Curl_splaycomparekeys(i,j) ( ((i.tv_sec) < (j.tv_sec)) ? -1 : \ + ( ((i.tv_sec) > (j.tv_sec)) ? 1 : \ + ( ((i.tv_usec) < (j.tv_usec)) ? -1 : \ + ( ((i.tv_usec) > (j.tv_usec)) ? 1 : 0)))) + +#endif /* HEADER_CURL_SPLAY_H */ diff --git a/third_party/curl/lib/strcase.c b/third_party/curl/lib/strcase.c new file mode 100644 index 0000000000..b22dd31fc8 --- /dev/null +++ b/third_party/curl/lib/strcase.c @@ -0,0 +1,204 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#include "strcase.h" + +/* Mapping table to go from lowercase to uppercase for plain ASCII.*/ +static const unsigned char touppermap[256] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, +22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, +41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, +60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, +79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, +66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, +85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, +134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, +150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, +166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, +182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, +198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, +214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, +230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, +246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; + +/* Mapping table to go from uppercase to lowercase for plain ASCII.*/ +static const unsigned char tolowermap[256] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, +22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, +42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, +62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, +111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, +96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, +112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, +128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, +144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, +160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, +176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, +192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, +208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, +224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, +240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; + + +/* Portable, consistent toupper. Do not use toupper() because its behavior is + altered by the current locale. */ +char Curl_raw_toupper(char in) +{ + return (char)touppermap[(unsigned char) in]; +} + + +/* Portable, consistent tolower. Do not use tolower() because its behavior is + altered by the current locale. */ +char Curl_raw_tolower(char in) +{ + return (char)tolowermap[(unsigned char) in]; +} + +/* + * curl_strequal() is for doing "raw" case insensitive strings. This is meant + * to be locale independent and only compare strings we know are safe for + * this. See https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for + * further explanations as to why this function is necessary. + */ + +static int casecompare(const char *first, const char *second) +{ + while(*first && *second) { + if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) + /* get out of the loop as soon as they do not match */ + return 0; + first++; + second++; + } + /* If we are here either the strings are the same or the length is different. + We can just test if the "current" character is non-zero for one and zero + for the other. Note that the characters may not be exactly the same even + if they match, we only want to compare zero-ness. */ + return !*first == !*second; +} + +/* --- public function --- */ +int curl_strequal(const char *first, const char *second) +{ + if(first && second) + /* both pointers point to something then compare them */ + return casecompare(first, second); + + /* if both pointers are NULL then treat them as equal */ + return (NULL == first && NULL == second); +} + +static int ncasecompare(const char *first, const char *second, size_t max) +{ + while(*first && *second && max) { + if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) + return 0; + max--; + first++; + second++; + } + if(0 == max) + return 1; /* they are equal this far */ + + return Curl_raw_toupper(*first) == Curl_raw_toupper(*second); +} + +/* --- public function --- */ +int curl_strnequal(const char *first, const char *second, size_t max) +{ + if(first && second) + /* both pointers point to something then compare them */ + return ncasecompare(first, second, max); + + /* if both pointers are NULL then treat them as equal if max is non-zero */ + return (NULL == first && NULL == second && max); +} +/* Copy an upper case version of the string from src to dest. The + * strings may overlap. No more than n characters of the string are copied + * (including any NUL) and the destination string will NOT be + * NUL-terminated if that limit is reached. + */ +void Curl_strntoupper(char *dest, const char *src, size_t n) +{ + if(n < 1) + return; + + do { + *dest++ = Curl_raw_toupper(*src); + } while(*src++ && --n); +} + +/* Copy a lower case version of the string from src to dest. The + * strings may overlap. No more than n characters of the string are copied + * (including any NUL) and the destination string will NOT be + * NUL-terminated if that limit is reached. + */ +void Curl_strntolower(char *dest, const char *src, size_t n) +{ + if(n < 1) + return; + + do { + *dest++ = Curl_raw_tolower(*src); + } while(*src++ && --n); +} + +/* Compare case-sensitive NUL-terminated strings, taking care of possible + * null pointers. Return true if arguments match. + */ +bool Curl_safecmp(char *a, char *b) +{ + if(a && b) + return !strcmp(a, b); + return !a && !b; +} + +/* + * Curl_timestrcmp() returns 0 if the two strings are identical. The time this + * function spends is a function of the shortest string, not of the contents. + */ +int Curl_timestrcmp(const char *a, const char *b) +{ + int match = 0; + int i = 0; + + if(a && b) { + while(1) { + match |= a[i]^b[i]; + if(!a[i] || !b[i]) + break; + i++; + } + } + else + return a || b; + return match; +} diff --git a/third_party/curl/lib/strcase.h b/third_party/curl/lib/strcase.h new file mode 100644 index 0000000000..8c50bbcba6 --- /dev/null +++ b/third_party/curl/lib/strcase.h @@ -0,0 +1,54 @@ +#ifndef HEADER_CURL_STRCASE_H +#define HEADER_CURL_STRCASE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include + +/* + * Only "raw" case insensitive strings. This is meant to be locale independent + * and only compare strings we know are safe for this. + * + * The function is capable of comparing a-z case insensitively. + * + * Result is 1 if text matches and 0 if not. + */ + +#define strcasecompare(a,b) curl_strequal(a,b) +#define strncasecompare(a,b,c) curl_strnequal(a,b,c) + +char Curl_raw_toupper(char in); +char Curl_raw_tolower(char in); + +/* checkprefix() is a shorter version of the above, used when the first + argument is the string literal */ +#define checkprefix(a,b) curl_strnequal(b, STRCONST(a)) + +void Curl_strntoupper(char *dest, const char *src, size_t n); +void Curl_strntolower(char *dest, const char *src, size_t n); + +bool Curl_safecmp(char *a, char *b); +int Curl_timestrcmp(const char *first, const char *second); + +#endif /* HEADER_CURL_STRCASE_H */ diff --git a/third_party/curl/lib/strdup.c b/third_party/curl/lib/strdup.c new file mode 100644 index 0000000000..299c9cc36b --- /dev/null +++ b/third_party/curl/lib/strdup.c @@ -0,0 +1,143 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include + +#ifdef _WIN32 +#include +#endif + +#include "strdup.h" +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +#ifndef HAVE_STRDUP +char *Curl_strdup(const char *str) +{ + size_t len; + char *newstr; + + if(!str) + return (char *)NULL; + + len = strlen(str) + 1; + + newstr = malloc(len); + if(!newstr) + return (char *)NULL; + + memcpy(newstr, str, len); + return newstr; +} +#endif + +#ifdef _WIN32 +/*************************************************************************** + * + * Curl_wcsdup(source) + * + * Copies the 'source' wchar string to a newly allocated buffer (that is + * returned). + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +wchar_t *Curl_wcsdup(const wchar_t *src) +{ + size_t length = wcslen(src); + + if(length > (SIZE_T_MAX / sizeof(wchar_t)) - 1) + return (wchar_t *)NULL; /* integer overflow */ + + return (wchar_t *)Curl_memdup(src, (length + 1) * sizeof(wchar_t)); +} +#endif + +/*************************************************************************** + * + * Curl_memdup(source, length) + * + * Copies the 'source' data to a newly allocated buffer (that is + * returned). Copies 'length' bytes. + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +void *Curl_memdup(const void *src, size_t length) +{ + void *buffer = malloc(length); + if(!buffer) + return NULL; /* fail */ + + memcpy(buffer, src, length); + + return buffer; +} + +/*************************************************************************** + * + * Curl_memdup0(source, length) + * + * Copies the 'source' string to a newly allocated buffer (that is returned). + * Copies 'length' bytes then adds a null terminator. + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +void *Curl_memdup0(const char *src, size_t length) +{ + char *buf = malloc(length + 1); + if(!buf) + return NULL; + memcpy(buf, src, length); + buf[length] = 0; + return buf; +} + +/*************************************************************************** + * + * Curl_saferealloc(ptr, size) + * + * Does a normal realloc(), but will free the data pointer if the realloc + * fails. If 'size' is non-zero, it will free the data and return a failure. + * + * This convenience function is provided and used to help us avoid a common + * mistake pattern when we could pass in a zero, catch the NULL return and end + * up free'ing the memory twice. + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +void *Curl_saferealloc(void *ptr, size_t size) +{ + void *datap = realloc(ptr, size); + if(size && !datap) + /* only free 'ptr' if size was non-zero */ + free(ptr); + return datap; +} diff --git a/third_party/curl/lib/strdup.h b/third_party/curl/lib/strdup.h new file mode 100644 index 0000000000..238a2611f6 --- /dev/null +++ b/third_party/curl/lib/strdup.h @@ -0,0 +1,38 @@ +#ifndef HEADER_CURL_STRDUP_H +#define HEADER_CURL_STRDUP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifndef HAVE_STRDUP +char *Curl_strdup(const char *str); +#endif +#ifdef _WIN32 +wchar_t* Curl_wcsdup(const wchar_t* src); +#endif +void *Curl_memdup(const void *src, size_t buffer_length); +void *Curl_saferealloc(void *ptr, size_t size); +void *Curl_memdup0(const char *src, size_t length); + +#endif /* HEADER_CURL_STRDUP_H */ diff --git a/third_party/curl/lib/strerror.c b/third_party/curl/lib/strerror.c new file mode 100644 index 0000000000..76a8ba2d7e --- /dev/null +++ b/third_party/curl/lib/strerror.c @@ -0,0 +1,1116 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_STRERROR_R +# if (!defined(HAVE_POSIX_STRERROR_R) && \ + !defined(HAVE_GLIBC_STRERROR_R)) || \ + (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)) +# error "strerror_r MUST be either POSIX, glibc style" +# endif +#endif + +#include + +#ifdef USE_LIBIDN2 +#include +#endif + +#ifdef USE_WINDOWS_SSPI +#include "curl_sspi.h" +#endif + +#include "strerror.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if defined(_WIN32) || defined(_WIN32_WCE) +#define PRESERVE_WINDOWS_ERROR_CODE +#endif + +const char * +curl_easy_strerror(CURLcode error) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + switch(error) { + case CURLE_OK: + return "No error"; + + case CURLE_UNSUPPORTED_PROTOCOL: + return "Unsupported protocol"; + + case CURLE_FAILED_INIT: + return "Failed initialization"; + + case CURLE_URL_MALFORMAT: + return "URL using bad/illegal format or missing URL"; + + case CURLE_NOT_BUILT_IN: + return "A requested feature, protocol or option was not found built-in in" + " this libcurl due to a build-time decision."; + + case CURLE_COULDNT_RESOLVE_PROXY: + return "Could not resolve proxy name"; + + case CURLE_COULDNT_RESOLVE_HOST: + return "Could not resolve hostname"; + + case CURLE_COULDNT_CONNECT: + return "Could not connect to server"; + + case CURLE_WEIRD_SERVER_REPLY: + return "Weird server reply"; + + case CURLE_REMOTE_ACCESS_DENIED: + return "Access denied to remote resource"; + + case CURLE_FTP_ACCEPT_FAILED: + return "FTP: The server failed to connect to data port"; + + case CURLE_FTP_ACCEPT_TIMEOUT: + return "FTP: Accepting server connect has timed out"; + + case CURLE_FTP_PRET_FAILED: + return "FTP: The server did not accept the PRET command."; + + case CURLE_FTP_WEIRD_PASS_REPLY: + return "FTP: unknown PASS reply"; + + case CURLE_FTP_WEIRD_PASV_REPLY: + return "FTP: unknown PASV reply"; + + case CURLE_FTP_WEIRD_227_FORMAT: + return "FTP: unknown 227 response format"; + + case CURLE_FTP_CANT_GET_HOST: + return "FTP: cannot figure out the host in the PASV response"; + + case CURLE_HTTP2: + return "Error in the HTTP2 framing layer"; + + case CURLE_FTP_COULDNT_SET_TYPE: + return "FTP: could not set file type"; + + case CURLE_PARTIAL_FILE: + return "Transferred a partial file"; + + case CURLE_FTP_COULDNT_RETR_FILE: + return "FTP: could not retrieve (RETR failed) the specified file"; + + case CURLE_QUOTE_ERROR: + return "Quote command returned error"; + + case CURLE_HTTP_RETURNED_ERROR: + return "HTTP response code said error"; + + case CURLE_WRITE_ERROR: + return "Failed writing received data to disk/application"; + + case CURLE_UPLOAD_FAILED: + return "Upload failed (at start/before it took off)"; + + case CURLE_READ_ERROR: + return "Failed to open/read local data from file/application"; + + case CURLE_OUT_OF_MEMORY: + return "Out of memory"; + + case CURLE_OPERATION_TIMEDOUT: + return "Timeout was reached"; + + case CURLE_FTP_PORT_FAILED: + return "FTP: command PORT failed"; + + case CURLE_FTP_COULDNT_USE_REST: + return "FTP: command REST failed"; + + case CURLE_RANGE_ERROR: + return "Requested range was not delivered by the server"; + + case CURLE_HTTP_POST_ERROR: + return "Internal problem setting up the POST"; + + case CURLE_SSL_CONNECT_ERROR: + return "SSL connect error"; + + case CURLE_BAD_DOWNLOAD_RESUME: + return "Could not resume download"; + + case CURLE_FILE_COULDNT_READ_FILE: + return "Could not read a file:// file"; + + case CURLE_LDAP_CANNOT_BIND: + return "LDAP: cannot bind"; + + case CURLE_LDAP_SEARCH_FAILED: + return "LDAP: search failed"; + + case CURLE_FUNCTION_NOT_FOUND: + return "A required function in the library was not found"; + + case CURLE_ABORTED_BY_CALLBACK: + return "Operation was aborted by an application callback"; + + case CURLE_BAD_FUNCTION_ARGUMENT: + return "A libcurl function was given a bad argument"; + + case CURLE_INTERFACE_FAILED: + return "Failed binding local connection end"; + + case CURLE_TOO_MANY_REDIRECTS: + return "Number of redirects hit maximum amount"; + + case CURLE_UNKNOWN_OPTION: + return "An unknown option was passed in to libcurl"; + + case CURLE_SETOPT_OPTION_SYNTAX: + return "Malformed option provided in a setopt"; + + case CURLE_GOT_NOTHING: + return "Server returned nothing (no headers, no data)"; + + case CURLE_SSL_ENGINE_NOTFOUND: + return "SSL crypto engine not found"; + + case CURLE_SSL_ENGINE_SETFAILED: + return "Can not set SSL crypto engine as default"; + + case CURLE_SSL_ENGINE_INITFAILED: + return "Failed to initialise SSL crypto engine"; + + case CURLE_SEND_ERROR: + return "Failed sending data to the peer"; + + case CURLE_RECV_ERROR: + return "Failure when receiving data from the peer"; + + case CURLE_SSL_CERTPROBLEM: + return "Problem with the local SSL certificate"; + + case CURLE_SSL_CIPHER: + return "Could not use specified SSL cipher"; + + case CURLE_PEER_FAILED_VERIFICATION: + return "SSL peer certificate or SSH remote key was not OK"; + + case CURLE_SSL_CACERT_BADFILE: + return "Problem with the SSL CA cert (path? access rights?)"; + + case CURLE_BAD_CONTENT_ENCODING: + return "Unrecognized or bad HTTP Content or Transfer-Encoding"; + + case CURLE_FILESIZE_EXCEEDED: + return "Maximum file size exceeded"; + + case CURLE_USE_SSL_FAILED: + return "Requested SSL level failed"; + + case CURLE_SSL_SHUTDOWN_FAILED: + return "Failed to shut down the SSL connection"; + + case CURLE_SSL_CRL_BADFILE: + return "Failed to load CRL file (path? access rights?, format?)"; + + case CURLE_SSL_ISSUER_ERROR: + return "Issuer check against peer certificate failed"; + + case CURLE_SEND_FAIL_REWIND: + return "Send failed since rewinding of the data stream failed"; + + case CURLE_LOGIN_DENIED: + return "Login denied"; + + case CURLE_TFTP_NOTFOUND: + return "TFTP: File Not Found"; + + case CURLE_TFTP_PERM: + return "TFTP: Access Violation"; + + case CURLE_REMOTE_DISK_FULL: + return "Disk full or allocation exceeded"; + + case CURLE_TFTP_ILLEGAL: + return "TFTP: Illegal operation"; + + case CURLE_TFTP_UNKNOWNID: + return "TFTP: Unknown transfer ID"; + + case CURLE_REMOTE_FILE_EXISTS: + return "Remote file already exists"; + + case CURLE_TFTP_NOSUCHUSER: + return "TFTP: No such user"; + + case CURLE_REMOTE_FILE_NOT_FOUND: + return "Remote file not found"; + + case CURLE_SSH: + return "Error in the SSH layer"; + + case CURLE_AGAIN: + return "Socket not ready for send/recv"; + + case CURLE_RTSP_CSEQ_ERROR: + return "RTSP CSeq mismatch or invalid CSeq"; + + case CURLE_RTSP_SESSION_ERROR: + return "RTSP session error"; + + case CURLE_FTP_BAD_FILE_LIST: + return "Unable to parse FTP file list"; + + case CURLE_CHUNK_FAILED: + return "Chunk callback failed"; + + case CURLE_NO_CONNECTION_AVAILABLE: + return "The max connection limit is reached"; + + case CURLE_SSL_PINNEDPUBKEYNOTMATCH: + return "SSL public key does not match pinned public key"; + + case CURLE_SSL_INVALIDCERTSTATUS: + return "SSL server certificate status verification FAILED"; + + case CURLE_HTTP2_STREAM: + return "Stream error in the HTTP/2 framing layer"; + + case CURLE_RECURSIVE_API_CALL: + return "API function called from within callback"; + + case CURLE_AUTH_ERROR: + return "An authentication function returned an error"; + + case CURLE_HTTP3: + return "HTTP/3 error"; + + case CURLE_QUIC_CONNECT_ERROR: + return "QUIC connection error"; + + case CURLE_PROXY: + return "proxy handshake error"; + + case CURLE_SSL_CLIENTCERT: + return "SSL Client Certificate required"; + + case CURLE_UNRECOVERABLE_POLL: + return "Unrecoverable error in select/poll"; + + case CURLE_TOO_LARGE: + return "A value or data field grew larger than allowed"; + + case CURLE_ECH_REQUIRED: + return "ECH attempted but failed"; + + /* error codes not used by current libcurl */ + case CURLE_OBSOLETE20: + case CURLE_OBSOLETE24: + case CURLE_OBSOLETE29: + case CURLE_OBSOLETE32: + case CURLE_OBSOLETE40: + case CURLE_OBSOLETE44: + case CURLE_OBSOLETE46: + case CURLE_OBSOLETE50: + case CURLE_OBSOLETE51: + case CURLE_OBSOLETE57: + case CURLE_OBSOLETE62: + case CURLE_OBSOLETE75: + case CURLE_OBSOLETE76: + case CURL_LAST: + break; + } + /* + * By using a switch, gcc -Wall will complain about enum values + * which do not appear, helping keep this function up-to-date. + * By using gcc -Wall -Werror, you cannot forget. + * + * A table would not have the same benefit. Most compilers will generate + * code very similar to a table in any case, so there is little performance + * gain from a table. Something is broken for the user's application, + * anyways, so does it matter how fast it _does not_ work? + * + * The line number for the error will be near this comment, which is why it + * is here, and not at the start of the switch. + */ + return "Unknown error"; +#else + if(!error) + return "No error"; + else + return "Error"; +#endif +} + +const char * +curl_multi_strerror(CURLMcode error) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + switch(error) { + case CURLM_CALL_MULTI_PERFORM: + return "Please call curl_multi_perform() soon"; + + case CURLM_OK: + return "No error"; + + case CURLM_BAD_HANDLE: + return "Invalid multi handle"; + + case CURLM_BAD_EASY_HANDLE: + return "Invalid easy handle"; + + case CURLM_OUT_OF_MEMORY: + return "Out of memory"; + + case CURLM_INTERNAL_ERROR: + return "Internal error"; + + case CURLM_BAD_SOCKET: + return "Invalid socket argument"; + + case CURLM_UNKNOWN_OPTION: + return "Unknown option"; + + case CURLM_ADDED_ALREADY: + return "The easy handle is already added to a multi handle"; + + case CURLM_RECURSIVE_API_CALL: + return "API function called from within callback"; + + case CURLM_WAKEUP_FAILURE: + return "Wakeup is unavailable or failed"; + + case CURLM_BAD_FUNCTION_ARGUMENT: + return "A libcurl function was given a bad argument"; + + case CURLM_ABORTED_BY_CALLBACK: + return "Operation was aborted by an application callback"; + + case CURLM_UNRECOVERABLE_POLL: + return "Unrecoverable error in select/poll"; + + case CURLM_LAST: + break; + } + + return "Unknown error"; +#else + if(error == CURLM_OK) + return "No error"; + else + return "Error"; +#endif +} + +const char * +curl_share_strerror(CURLSHcode error) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + switch(error) { + case CURLSHE_OK: + return "No error"; + + case CURLSHE_BAD_OPTION: + return "Unknown share option"; + + case CURLSHE_IN_USE: + return "Share currently in use"; + + case CURLSHE_INVALID: + return "Invalid share handle"; + + case CURLSHE_NOMEM: + return "Out of memory"; + + case CURLSHE_NOT_BUILT_IN: + return "Feature not enabled in this library"; + + case CURLSHE_LAST: + break; + } + + return "CURLSHcode unknown"; +#else + if(error == CURLSHE_OK) + return "No error"; + else + return "Error"; +#endif +} + +const char * +curl_url_strerror(CURLUcode error) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + switch(error) { + case CURLUE_OK: + return "No error"; + + case CURLUE_BAD_HANDLE: + return "An invalid CURLU pointer was passed as argument"; + + case CURLUE_BAD_PARTPOINTER: + return "An invalid 'part' argument was passed as argument"; + + case CURLUE_MALFORMED_INPUT: + return "Malformed input to a URL function"; + + case CURLUE_BAD_PORT_NUMBER: + return "Port number was not a decimal number between 0 and 65535"; + + case CURLUE_UNSUPPORTED_SCHEME: + return "Unsupported URL scheme"; + + case CURLUE_URLDECODE: + return "URL decode error, most likely because of rubbish in the input"; + + case CURLUE_OUT_OF_MEMORY: + return "A memory function failed"; + + case CURLUE_USER_NOT_ALLOWED: + return "Credentials was passed in the URL when prohibited"; + + case CURLUE_UNKNOWN_PART: + return "An unknown part ID was passed to a URL API function"; + + case CURLUE_NO_SCHEME: + return "No scheme part in the URL"; + + case CURLUE_NO_USER: + return "No user part in the URL"; + + case CURLUE_NO_PASSWORD: + return "No password part in the URL"; + + case CURLUE_NO_OPTIONS: + return "No options part in the URL"; + + case CURLUE_NO_HOST: + return "No host part in the URL"; + + case CURLUE_NO_PORT: + return "No port part in the URL"; + + case CURLUE_NO_QUERY: + return "No query part in the URL"; + + case CURLUE_NO_FRAGMENT: + return "No fragment part in the URL"; + + case CURLUE_NO_ZONEID: + return "No zoneid part in the URL"; + + case CURLUE_BAD_LOGIN: + return "Bad login part"; + + case CURLUE_BAD_IPV6: + return "Bad IPv6 address"; + + case CURLUE_BAD_HOSTNAME: + return "Bad hostname"; + + case CURLUE_BAD_FILE_URL: + return "Bad file:// URL"; + + case CURLUE_BAD_SLASHES: + return "Unsupported number of slashes following scheme"; + + case CURLUE_BAD_SCHEME: + return "Bad scheme"; + + case CURLUE_BAD_PATH: + return "Bad path"; + + case CURLUE_BAD_FRAGMENT: + return "Bad fragment"; + + case CURLUE_BAD_QUERY: + return "Bad query"; + + case CURLUE_BAD_PASSWORD: + return "Bad password"; + + case CURLUE_BAD_USER: + return "Bad user"; + + case CURLUE_LACKS_IDN: + return "libcurl lacks IDN support"; + + case CURLUE_TOO_LARGE: + return "A value or data field is larger than allowed"; + + case CURLUE_LAST: + break; + } + + return "CURLUcode unknown"; +#else + if(error == CURLUE_OK) + return "No error"; + else + return "Error"; +#endif +} + +#ifdef USE_WINSOCK +/* This is a helper function for Curl_strerror that converts Winsock error + * codes (WSAGetLastError) to error messages. + * Returns NULL if no error message was found for error code. + */ +static const char * +get_winsock_error(int err, char *buf, size_t len) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + const char *p; + size_t alen; +#endif + + if(!len) + return NULL; + + *buf = '\0'; + +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)err; + return NULL; +#else + switch(err) { + case WSAEINTR: + p = "Call interrupted"; + break; + case WSAEBADF: + p = "Bad file"; + break; + case WSAEACCES: + p = "Bad access"; + break; + case WSAEFAULT: + p = "Bad argument"; + break; + case WSAEINVAL: + p = "Invalid arguments"; + break; + case WSAEMFILE: + p = "Out of file descriptors"; + break; + case WSAEWOULDBLOCK: + p = "Call would block"; + break; + case WSAEINPROGRESS: + case WSAEALREADY: + p = "Blocking call in progress"; + break; + case WSAENOTSOCK: + p = "Descriptor is not a socket"; + break; + case WSAEDESTADDRREQ: + p = "Need destination address"; + break; + case WSAEMSGSIZE: + p = "Bad message size"; + break; + case WSAEPROTOTYPE: + p = "Bad protocol"; + break; + case WSAENOPROTOOPT: + p = "Protocol option is unsupported"; + break; + case WSAEPROTONOSUPPORT: + p = "Protocol is unsupported"; + break; + case WSAESOCKTNOSUPPORT: + p = "Socket is unsupported"; + break; + case WSAEOPNOTSUPP: + p = "Operation not supported"; + break; + case WSAEAFNOSUPPORT: + p = "Address family not supported"; + break; + case WSAEPFNOSUPPORT: + p = "Protocol family not supported"; + break; + case WSAEADDRINUSE: + p = "Address already in use"; + break; + case WSAEADDRNOTAVAIL: + p = "Address not available"; + break; + case WSAENETDOWN: + p = "Network down"; + break; + case WSAENETUNREACH: + p = "Network unreachable"; + break; + case WSAENETRESET: + p = "Network has been reset"; + break; + case WSAECONNABORTED: + p = "Connection was aborted"; + break; + case WSAECONNRESET: + p = "Connection was reset"; + break; + case WSAENOBUFS: + p = "No buffer space"; + break; + case WSAEISCONN: + p = "Socket is already connected"; + break; + case WSAENOTCONN: + p = "Socket is not connected"; + break; + case WSAESHUTDOWN: + p = "Socket has been shut down"; + break; + case WSAETOOMANYREFS: + p = "Too many references"; + break; + case WSAETIMEDOUT: + p = "Timed out"; + break; + case WSAECONNREFUSED: + p = "Connection refused"; + break; + case WSAELOOP: + p = "Loop??"; + break; + case WSAENAMETOOLONG: + p = "Name too long"; + break; + case WSAEHOSTDOWN: + p = "Host down"; + break; + case WSAEHOSTUNREACH: + p = "Host unreachable"; + break; + case WSAENOTEMPTY: + p = "Not empty"; + break; + case WSAEPROCLIM: + p = "Process limit reached"; + break; + case WSAEUSERS: + p = "Too many users"; + break; + case WSAEDQUOT: + p = "Bad quota"; + break; + case WSAESTALE: + p = "Something is stale"; + break; + case WSAEREMOTE: + p = "Remote error"; + break; +#ifdef WSAEDISCON /* missing in SalfordC! */ + case WSAEDISCON: + p = "Disconnected"; + break; +#endif + /* Extended Winsock errors */ + case WSASYSNOTREADY: + p = "Winsock library is not ready"; + break; + case WSANOTINITIALISED: + p = "Winsock library not initialised"; + break; + case WSAVERNOTSUPPORTED: + p = "Winsock version not supported"; + break; + + /* getXbyY() errors (already handled in herrmsg): + * Authoritative Answer: Host not found */ + case WSAHOST_NOT_FOUND: + p = "Host not found"; + break; + + /* Non-Authoritative: Host not found, or SERVERFAIL */ + case WSATRY_AGAIN: + p = "Host not found, try again"; + break; + + /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ + case WSANO_RECOVERY: + p = "Unrecoverable error in call to nameserver"; + break; + + /* Valid name, no data record of requested type */ + case WSANO_DATA: + p = "No data record of requested type"; + break; + + default: + return NULL; + } + alen = strlen(p); + if(alen < len) + strcpy(buf, p); + return buf; +#endif +} +#endif /* USE_WINSOCK */ + +#if defined(_WIN32) || defined(_WIN32_WCE) +/* This is a helper function for Curl_strerror that converts Windows API error + * codes (GetLastError) to error messages. + * Returns NULL if no error message was found for error code. + */ +static const char * +get_winapi_error(int err, char *buf, size_t buflen) +{ + char *p; + wchar_t wbuf[256]; + + if(!buflen) + return NULL; + + *buf = '\0'; + *wbuf = L'\0'; + + /* We return the local codepage version of the error string because if it is + output to the user's terminal it will likely be with functions which + expect the local codepage (eg fprintf, failf, infof). + FormatMessageW -> wcstombs is used for Windows CE compatibility. */ + if(FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS), NULL, (DWORD)err, + LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) { + size_t written = wcstombs(buf, wbuf, buflen - 1); + if(written != (size_t)-1) + buf[written] = '\0'; + else + *buf = '\0'; + } + + /* Truncate multiple lines */ + p = strchr(buf, '\n'); + if(p) { + if(p > buf && *(p-1) == '\r') + *(p-1) = '\0'; + else + *p = '\0'; + } + + return (*buf ? buf : NULL); +} +#endif /* _WIN32 || _WIN32_WCE */ + +/* + * Our thread-safe and smart strerror() replacement. + * + * The 'err' argument passed in to this function MUST be a true errno number + * as reported on this system. We do no range checking on the number before + * we pass it to the "number-to-message" conversion function and there might + * be systems that do not do proper range checking in there themselves. + * + * We do not do range checking (on systems other than Windows) since there is + * no good reliable and portable way to do it. + * + * On Windows different types of error codes overlap. This function has an + * order of preference when trying to match error codes: + * CRT (errno), Winsock (WSAGetLastError), Windows API (GetLastError). + * + * It may be more correct to call one of the variant functions instead: + * Call Curl_sspi_strerror if the error code is definitely Windows SSPI. + * Call Curl_winapi_strerror if the error code is definitely Windows API. + */ +const char *Curl_strerror(int err, char *buf, size_t buflen) +{ +#ifdef PRESERVE_WINDOWS_ERROR_CODE + DWORD old_win_err = GetLastError(); +#endif + int old_errno = errno; + char *p; + + if(!buflen) + return NULL; + +#ifndef _WIN32 + DEBUGASSERT(err >= 0); +#endif + + *buf = '\0'; + +#if defined(_WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) + /* 'sys_nerr' is the maximum errno number, it is not widely portable */ + if(err >= 0 && err < sys_nerr) + msnprintf(buf, buflen, "%s", sys_errlist[err]); + else +#endif + { + if( +#ifdef USE_WINSOCK + !get_winsock_error(err, buf, buflen) && +#endif + !get_winapi_error(err, buf, buflen)) + msnprintf(buf, buflen, "Unknown error %d (%#x)", err, err); + } +#else /* not Windows coming up */ + +#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R) + /* + * The POSIX-style strerror_r() may set errno to ERANGE if insufficient + * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated + * message string, or EINVAL if 'errnum' is not a valid error number. + */ + if(0 != strerror_r(err, buf, buflen)) { + if('\0' == buf[0]) + msnprintf(buf, buflen, "Unknown error %d", err); + } +#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R) + /* + * The glibc-style strerror_r() only *might* use the buffer we pass to + * the function, but it always returns the error message as a pointer, + * so we must copy that string unconditionally (if non-NULL). + */ + { + char buffer[256]; + char *msg = strerror_r(err, buffer, sizeof(buffer)); + if(msg) + msnprintf(buf, buflen, "%s", msg); + else + msnprintf(buf, buflen, "Unknown error %d", err); + } +#else + { + /* !checksrc! disable STRERROR 1 */ + const char *msg = strerror(err); + if(msg) + msnprintf(buf, buflen, "%s", msg); + else + msnprintf(buf, buflen, "Unknown error %d", err); + } +#endif + +#endif /* end of not Windows */ + + /* strip trailing '\r\n' or '\n'. */ + p = strrchr(buf, '\n'); + if(p && (p - buf) >= 2) + *p = '\0'; + p = strrchr(buf, '\r'); + if(p && (p - buf) >= 1) + *p = '\0'; + + if(errno != old_errno) + errno = old_errno; + +#ifdef PRESERVE_WINDOWS_ERROR_CODE + if(old_win_err != GetLastError()) + SetLastError(old_win_err); +#endif + + return buf; +} + +/* + * Curl_winapi_strerror: + * Variant of Curl_strerror if the error code is definitely Windows API. + */ +#if defined(_WIN32) || defined(_WIN32_WCE) +const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen) +{ +#ifdef PRESERVE_WINDOWS_ERROR_CODE + DWORD old_win_err = GetLastError(); +#endif + int old_errno = errno; + + if(!buflen) + return NULL; + + *buf = '\0'; + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(!get_winapi_error((int)err, buf, buflen)) { + msnprintf(buf, buflen, "Unknown error %lu (0x%08lX)", err, err); + } +#else + { + const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error"; + if(strlen(txt) < buflen) + strcpy(buf, txt); + } +#endif + + if(errno != old_errno) + errno = old_errno; + +#ifdef PRESERVE_WINDOWS_ERROR_CODE + if(old_win_err != GetLastError()) + SetLastError(old_win_err); +#endif + + return buf; +} +#endif /* _WIN32 || _WIN32_WCE */ + +#ifdef USE_WINDOWS_SSPI +/* + * Curl_sspi_strerror: + * Variant of Curl_strerror if the error code is definitely Windows SSPI. + */ +const char *Curl_sspi_strerror(int err, char *buf, size_t buflen) +{ +#ifdef PRESERVE_WINDOWS_ERROR_CODE + DWORD old_win_err = GetLastError(); +#endif + int old_errno = errno; + const char *txt; + + if(!buflen) + return NULL; + + *buf = '\0'; + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + + switch(err) { + case SEC_E_OK: + txt = "No error"; + break; +#define SEC2TXT(sec) case sec: txt = #sec; break + SEC2TXT(CRYPT_E_REVOKED); + SEC2TXT(CRYPT_E_NO_REVOCATION_DLL); + SEC2TXT(CRYPT_E_NO_REVOCATION_CHECK); + SEC2TXT(CRYPT_E_REVOCATION_OFFLINE); + SEC2TXT(CRYPT_E_NOT_IN_REVOCATION_DATABASE); + SEC2TXT(SEC_E_ALGORITHM_MISMATCH); + SEC2TXT(SEC_E_BAD_BINDINGS); + SEC2TXT(SEC_E_BAD_PKGID); + SEC2TXT(SEC_E_BUFFER_TOO_SMALL); + SEC2TXT(SEC_E_CANNOT_INSTALL); + SEC2TXT(SEC_E_CANNOT_PACK); + SEC2TXT(SEC_E_CERT_EXPIRED); + SEC2TXT(SEC_E_CERT_UNKNOWN); + SEC2TXT(SEC_E_CERT_WRONG_USAGE); + SEC2TXT(SEC_E_CONTEXT_EXPIRED); + SEC2TXT(SEC_E_CROSSREALM_DELEGATION_FAILURE); + SEC2TXT(SEC_E_CRYPTO_SYSTEM_INVALID); + SEC2TXT(SEC_E_DECRYPT_FAILURE); + SEC2TXT(SEC_E_DELEGATION_POLICY); + SEC2TXT(SEC_E_DELEGATION_REQUIRED); + SEC2TXT(SEC_E_DOWNGRADE_DETECTED); + SEC2TXT(SEC_E_ENCRYPT_FAILURE); + SEC2TXT(SEC_E_ILLEGAL_MESSAGE); + SEC2TXT(SEC_E_INCOMPLETE_CREDENTIALS); + SEC2TXT(SEC_E_INCOMPLETE_MESSAGE); + SEC2TXT(SEC_E_INSUFFICIENT_MEMORY); + SEC2TXT(SEC_E_INTERNAL_ERROR); + SEC2TXT(SEC_E_INVALID_HANDLE); + SEC2TXT(SEC_E_INVALID_PARAMETER); + SEC2TXT(SEC_E_INVALID_TOKEN); + SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED); + SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED_KDC); + SEC2TXT(SEC_E_KDC_CERT_EXPIRED); + SEC2TXT(SEC_E_KDC_CERT_REVOKED); + SEC2TXT(SEC_E_KDC_INVALID_REQUEST); + SEC2TXT(SEC_E_KDC_UNABLE_TO_REFER); + SEC2TXT(SEC_E_KDC_UNKNOWN_ETYPE); + SEC2TXT(SEC_E_LOGON_DENIED); + SEC2TXT(SEC_E_MAX_REFERRALS_EXCEEDED); + SEC2TXT(SEC_E_MESSAGE_ALTERED); + SEC2TXT(SEC_E_MULTIPLE_ACCOUNTS); + SEC2TXT(SEC_E_MUST_BE_KDC); + SEC2TXT(SEC_E_NOT_OWNER); + SEC2TXT(SEC_E_NO_AUTHENTICATING_AUTHORITY); + SEC2TXT(SEC_E_NO_CREDENTIALS); + SEC2TXT(SEC_E_NO_IMPERSONATION); + SEC2TXT(SEC_E_NO_IP_ADDRESSES); + SEC2TXT(SEC_E_NO_KERB_KEY); + SEC2TXT(SEC_E_NO_PA_DATA); + SEC2TXT(SEC_E_NO_S4U_PROT_SUPPORT); + SEC2TXT(SEC_E_NO_TGT_REPLY); + SEC2TXT(SEC_E_OUT_OF_SEQUENCE); + SEC2TXT(SEC_E_PKINIT_CLIENT_FAILURE); + SEC2TXT(SEC_E_PKINIT_NAME_MISMATCH); + SEC2TXT(SEC_E_POLICY_NLTM_ONLY); + SEC2TXT(SEC_E_QOP_NOT_SUPPORTED); + SEC2TXT(SEC_E_REVOCATION_OFFLINE_C); + SEC2TXT(SEC_E_REVOCATION_OFFLINE_KDC); + SEC2TXT(SEC_E_SECPKG_NOT_FOUND); + SEC2TXT(SEC_E_SECURITY_QOS_FAILED); + SEC2TXT(SEC_E_SHUTDOWN_IN_PROGRESS); + SEC2TXT(SEC_E_SMARTCARD_CERT_EXPIRED); + SEC2TXT(SEC_E_SMARTCARD_CERT_REVOKED); + SEC2TXT(SEC_E_SMARTCARD_LOGON_REQUIRED); + SEC2TXT(SEC_E_STRONG_CRYPTO_NOT_SUPPORTED); + SEC2TXT(SEC_E_TARGET_UNKNOWN); + SEC2TXT(SEC_E_TIME_SKEW); + SEC2TXT(SEC_E_TOO_MANY_PRINCIPALS); + SEC2TXT(SEC_E_UNFINISHED_CONTEXT_DELETED); + SEC2TXT(SEC_E_UNKNOWN_CREDENTIALS); + SEC2TXT(SEC_E_UNSUPPORTED_FUNCTION); + SEC2TXT(SEC_E_UNSUPPORTED_PREAUTH); + SEC2TXT(SEC_E_UNTRUSTED_ROOT); + SEC2TXT(SEC_E_WRONG_CREDENTIAL_HANDLE); + SEC2TXT(SEC_E_WRONG_PRINCIPAL); + SEC2TXT(SEC_I_COMPLETE_AND_CONTINUE); + SEC2TXT(SEC_I_COMPLETE_NEEDED); + SEC2TXT(SEC_I_CONTEXT_EXPIRED); + SEC2TXT(SEC_I_CONTINUE_NEEDED); + SEC2TXT(SEC_I_INCOMPLETE_CREDENTIALS); + SEC2TXT(SEC_I_LOCAL_LOGON); + SEC2TXT(SEC_I_NO_LSA_CONTEXT); + SEC2TXT(SEC_I_RENEGOTIATE); + SEC2TXT(SEC_I_SIGNATURE_NEEDED); + default: + txt = "Unknown error"; + } + + if(err == SEC_E_ILLEGAL_MESSAGE) { + msnprintf(buf, buflen, + "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually occurs " + "when a fatal SSL/TLS alert is received (e.g. handshake failed)." + " More detail may be available in the Windows System event log.", + err); + } + else { + char msgbuf[256]; + if(get_winapi_error(err, msgbuf, sizeof(msgbuf))) + msnprintf(buf, buflen, "%s (0x%08X) - %s", txt, err, msgbuf); + else + msnprintf(buf, buflen, "%s (0x%08X)", txt, err); + } + +#else + if(err == SEC_E_OK) + txt = "No error"; + else + txt = "Error"; + if(buflen > strlen(txt)) + strcpy(buf, txt); +#endif + + if(errno != old_errno) + errno = old_errno; + +#ifdef PRESERVE_WINDOWS_ERROR_CODE + if(old_win_err != GetLastError()) + SetLastError(old_win_err); +#endif + + return buf; +} +#endif /* USE_WINDOWS_SSPI */ diff --git a/third_party/curl/lib/strerror.h b/third_party/curl/lib/strerror.h new file mode 100644 index 0000000000..6806867345 --- /dev/null +++ b/third_party/curl/lib/strerror.h @@ -0,0 +1,39 @@ +#ifndef HEADER_CURL_STRERROR_H +#define HEADER_CURL_STRERROR_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "urldata.h" + +#define STRERROR_LEN 256 /* a suitable length */ + +const char *Curl_strerror(int err, char *buf, size_t buflen); +#if defined(_WIN32) || defined(_WIN32_WCE) +const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen); +#endif +#ifdef USE_WINDOWS_SSPI +const char *Curl_sspi_strerror(int err, char *buf, size_t buflen); +#endif + +#endif /* HEADER_CURL_STRERROR_H */ diff --git a/third_party/curl/lib/strtok.c b/third_party/curl/lib/strtok.c new file mode 100644 index 0000000000..d2cc71c47d --- /dev/null +++ b/third_party/curl/lib/strtok.c @@ -0,0 +1,68 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef HAVE_STRTOK_R +#include + +#include "strtok.h" + +char * +Curl_strtok_r(char *ptr, const char *sep, char **end) +{ + if(!ptr) + /* we got NULL input so then we get our last position instead */ + ptr = *end; + + /* pass all letters that are including in the separator string */ + while(*ptr && strchr(sep, *ptr)) + ++ptr; + + if(*ptr) { + /* so this is where the next piece of string starts */ + char *start = ptr; + + /* set the end pointer to the first byte after the start */ + *end = start + 1; + + /* scan through the string to find where it ends, it ends on a + null byte or a character that exists in the separator string */ + while(**end && !strchr(sep, **end)) + ++*end; + + if(**end) { + /* the end is not a null byte */ + **end = '\0'; /* null-terminate it! */ + ++*end; /* advance the last pointer to beyond the null byte */ + } + + return start; /* return the position where the string starts */ + } + + /* we ended up on a null byte, there are no more strings to find! */ + return NULL; +} + +#endif /* this was only compiled if strtok_r was not present */ diff --git a/third_party/curl/lib/strtok.h b/third_party/curl/lib/strtok.h new file mode 100644 index 0000000000..321cba2326 --- /dev/null +++ b/third_party/curl/lib/strtok.h @@ -0,0 +1,36 @@ +#ifndef HEADER_CURL_STRTOK_H +#define HEADER_CURL_STRTOK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include + +#ifndef HAVE_STRTOK_R +char *Curl_strtok_r(char *s, const char *delim, char **last); +#define strtok_r Curl_strtok_r +#else +#include +#endif + +#endif /* HEADER_CURL_STRTOK_H */ diff --git a/third_party/curl/lib/strtoofft.c b/third_party/curl/lib/strtoofft.c new file mode 100644 index 0000000000..8fac230f2e --- /dev/null +++ b/third_party/curl/lib/strtoofft.c @@ -0,0 +1,240 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include +#include "curl_setup.h" + +#include "strtoofft.h" + +/* + * NOTE: + * + * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we + * could use in case strtoll() does not exist... See + * https://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html + */ + +#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) +# ifdef HAVE_STRTOLL +# define strtooff strtoll +# else +# if defined(_MSC_VER) && (_MSC_VER >= 1300) && (_INTEGRAL_MAX_BITS >= 64) +# if defined(_SAL_VERSION) + _Check_return_ _CRTIMP __int64 __cdecl _strtoi64( + _In_z_ const char *_String, + _Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix); +# else + _CRTIMP __int64 __cdecl _strtoi64(const char *_String, + char **_EndPtr, int _Radix); +# endif +# define strtooff _strtoi64 +# else +# define PRIVATE_STRTOOFF 1 +# endif +# endif +#else +# define strtooff strtol +#endif + +#ifdef PRIVATE_STRTOOFF + +/* Range tests can be used for alphanum decoding if characters are consecutive, + like in ASCII. Else an array is scanned. Determine this condition now. */ + +#if('9' - '0') != 9 || ('Z' - 'A') != 25 || ('z' - 'a') != 25 + +#define NO_RANGE_TEST + +static const char valchars[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +#endif + +static int get_char(char c, int base); + +/** + * Custom version of the strtooff function. This extracts a curl_off_t + * value from the given input string and returns it. + */ +static curl_off_t strtooff(const char *nptr, char **endptr, int base) +{ + char *end; + bool is_negative = FALSE; + bool overflow = FALSE; + int i; + curl_off_t value = 0; + + /* Skip leading whitespace. */ + end = (char *)nptr; + while(ISBLANK(end[0])) { + end++; + } + + /* Handle the sign, if any. */ + if(end[0] == '-') { + is_negative = TRUE; + end++; + } + else if(end[0] == '+') { + end++; + } + else if(end[0] == '\0') { + /* We had nothing but perhaps some whitespace -- there was no number. */ + if(endptr) { + *endptr = end; + } + return 0; + } + + /* Handle special beginnings, if present and allowed. */ + if(end[0] == '0' && end[1] == 'x') { + if(base == 16 || base == 0) { + end += 2; + base = 16; + } + } + else if(end[0] == '0') { + if(base == 8 || base == 0) { + end++; + base = 8; + } + } + + /* Matching strtol, if the base is 0 and it does not look like + * the number is octal or hex, we assume it is base 10. + */ + if(base == 0) { + base = 10; + } + + /* Loop handling digits. */ + for(i = get_char(end[0], base); + i != -1; + end++, i = get_char(end[0], base)) { + + if(value > (CURL_OFF_T_MAX - i) / base) { + overflow = TRUE; + break; + } + value = base * value + i; + } + + if(!overflow) { + if(is_negative) { + /* Fix the sign. */ + value *= -1; + } + } + else { + if(is_negative) + value = CURL_OFF_T_MIN; + else + value = CURL_OFF_T_MAX; + + errno = ERANGE; + } + + if(endptr) + *endptr = end; + + return value; +} + +/** + * Returns the value of c in the given base, or -1 if c cannot + * be interpreted properly in that base (i.e., is out of range, + * is a null, etc.). + * + * @param c the character to interpret according to base + * @param base the base in which to interpret c + * + * @return the value of c in base, or -1 if c is not in range + */ +static int get_char(char c, int base) +{ +#ifndef NO_RANGE_TEST + int value = -1; + if(c <= '9' && c >= '0') { + value = c - '0'; + } + else if(c <= 'Z' && c >= 'A') { + value = c - 'A' + 10; + } + else if(c <= 'z' && c >= 'a') { + value = c - 'a' + 10; + } +#else + const char *cp; + int value; + + cp = memchr(valchars, c, 10 + 26 + 26); + + if(!cp) + return -1; + + value = cp - valchars; + + if(value >= 10 + 26) + value -= 26; /* Lowercase. */ +#endif + + if(value >= base) { + value = -1; + } + + return value; +} +#endif /* Only present if we need strtoll, but do not have it. */ + +/* + * Parse a *positive* up to 64-bit number written in ascii. + */ +CURLofft curlx_strtoofft(const char *str, char **endp, int base, + curl_off_t *num) +{ + char *end = NULL; + curl_off_t number; + errno = 0; + *num = 0; /* clear by default */ + DEBUGASSERT(base); /* starting now, avoid base zero */ + + while(*str && ISBLANK(*str)) + str++; + if(('-' == *str) || (ISSPACE(*str))) { + if(endp) + *endp = (char *)str; /* did not actually move */ + return CURL_OFFT_INVAL; /* nothing parsed */ + } + number = strtooff(str, &end, base); + if(endp) + *endp = end; + if(errno == ERANGE) + /* overflow/underflow */ + return CURL_OFFT_FLOW; + else if(str == end) + /* nothing parsed */ + return CURL_OFFT_INVAL; + + *num = number; + return CURL_OFFT_OK; +} diff --git a/third_party/curl/lib/strtoofft.h b/third_party/curl/lib/strtoofft.h new file mode 100644 index 0000000000..71808b719c --- /dev/null +++ b/third_party/curl/lib/strtoofft.h @@ -0,0 +1,54 @@ +#ifndef HEADER_CURL_STRTOOFFT_H +#define HEADER_CURL_STRTOOFFT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +/* + * Determine which string to integral data type conversion function we use + * to implement string conversion to our curl_off_t integral data type. + * + * Notice that curl_off_t might be 64 or 32 bits wide, and that it might use + * an underlying data type which might be 'long', 'int64_t', 'long long' or + * '__int64' and more remotely other data types. + * + * On systems where the size of curl_off_t is greater than the size of 'long' + * the conversion function to use is strtoll() if it is available, otherwise, + * we emulate its functionality with our own clone. + * + * On systems where the size of curl_off_t is smaller or equal than the size + * of 'long' the conversion function to use is strtol(). + */ + +typedef enum { + CURL_OFFT_OK, /* parsed fine */ + CURL_OFFT_FLOW, /* over or underflow */ + CURL_OFFT_INVAL /* nothing was parsed */ +} CURLofft; + +CURLofft curlx_strtoofft(const char *str, char **endp, int base, + curl_off_t *num); + +#endif /* HEADER_CURL_STRTOOFFT_H */ diff --git a/third_party/curl/lib/system_win32.c b/third_party/curl/lib/system_win32.c new file mode 100644 index 0000000000..4af86699e3 --- /dev/null +++ b/third_party/curl/lib/system_win32.c @@ -0,0 +1,286 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(_WIN32) + +#include +#include "system_win32.h" +#include "version_win32.h" +#include "curl_sspi.h" +#include "warnless.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +LARGE_INTEGER Curl_freq; +bool Curl_isVistaOrGreater; +bool Curl_isWindows8OrGreater; + +/* Handle of iphlpapp.dll */ +static HMODULE s_hIpHlpApiDll = NULL; + +/* Function pointers */ +IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL; +FREEADDRINFOEXW_FN Curl_FreeAddrInfoExW = NULL; +GETADDRINFOEXCANCEL_FN Curl_GetAddrInfoExCancel = NULL; +GETADDRINFOEXW_FN Curl_GetAddrInfoExW = NULL; + +/* Curl_win32_init() performs win32 global initialization */ +CURLcode Curl_win32_init(long flags) +{ +#ifdef USE_WINSOCK + HMODULE ws2_32Dll; +#endif + /* CURL_GLOBAL_WIN32 controls the *optional* part of the initialization which + is just for Winsock at the moment. Any required win32 initialization + should take place after this block. */ + if(flags & CURL_GLOBAL_WIN32) { +#ifdef USE_WINSOCK + WORD wVersionRequested; + WSADATA wsaData; + int res; + + wVersionRequested = MAKEWORD(2, 2); + res = WSAStartup(wVersionRequested, &wsaData); + + if(res) + /* Tell the user that we could not find a usable */ + /* winsock.dll. */ + return CURLE_FAILED_INIT; + + /* Confirm that the Windows Sockets DLL supports what we need.*/ + /* Note that if the DLL supports versions greater */ + /* than wVersionRequested, it will still return */ + /* wVersionRequested in wVersion. wHighVersion contains the */ + /* highest supported version. */ + + if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) || + HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested) ) { + /* Tell the user that we could not find a usable */ + + /* winsock.dll. */ + WSACleanup(); + return CURLE_FAILED_INIT; + } + /* The Windows Sockets DLL is acceptable. Proceed. */ +#elif defined(USE_LWIPSOCK) + lwip_init(); +#endif + } /* CURL_GLOBAL_WIN32 */ + +#ifdef USE_WINDOWS_SSPI + { + CURLcode result = Curl_sspi_global_init(); + if(result) + return result; + } +#endif + + s_hIpHlpApiDll = Curl_load_library(TEXT("iphlpapi.dll")); + if(s_hIpHlpApiDll) { + /* Get the address of the if_nametoindex function */ + IF_NAMETOINDEX_FN pIfNameToIndex = + CURLX_FUNCTION_CAST(IF_NAMETOINDEX_FN, + (GetProcAddress(s_hIpHlpApiDll, "if_nametoindex"))); + + if(pIfNameToIndex) + Curl_if_nametoindex = pIfNameToIndex; + } + +#ifdef USE_WINSOCK +#ifdef CURL_WINDOWS_APP + ws2_32Dll = Curl_load_library(TEXT("ws2_32.dll")); +#else + ws2_32Dll = GetModuleHandleA("ws2_32"); +#endif + if(ws2_32Dll) { + Curl_FreeAddrInfoExW = CURLX_FUNCTION_CAST(FREEADDRINFOEXW_FN, + GetProcAddress(ws2_32Dll, "FreeAddrInfoExW")); + Curl_GetAddrInfoExCancel = CURLX_FUNCTION_CAST(GETADDRINFOEXCANCEL_FN, + GetProcAddress(ws2_32Dll, "GetAddrInfoExCancel")); + Curl_GetAddrInfoExW = CURLX_FUNCTION_CAST(GETADDRINFOEXW_FN, + GetProcAddress(ws2_32Dll, "GetAddrInfoExW")); + } +#endif + + /* curlx_verify_windows_version must be called during init at least once + because it has its own initialization routine. */ + if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + Curl_isVistaOrGreater = TRUE; + } + else + Curl_isVistaOrGreater = FALSE; + + if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + Curl_isWindows8OrGreater = TRUE; + } + else + Curl_isWindows8OrGreater = FALSE; + + QueryPerformanceFrequency(&Curl_freq); + return CURLE_OK; +} + +/* Curl_win32_cleanup() is the opposite of Curl_win32_init() */ +void Curl_win32_cleanup(long init_flags) +{ + Curl_FreeAddrInfoExW = NULL; + Curl_GetAddrInfoExCancel = NULL; + Curl_GetAddrInfoExW = NULL; + if(s_hIpHlpApiDll) { + FreeLibrary(s_hIpHlpApiDll); + s_hIpHlpApiDll = NULL; + Curl_if_nametoindex = NULL; + } + +#ifdef USE_WINDOWS_SSPI + Curl_sspi_global_cleanup(); +#endif + + if(init_flags & CURL_GLOBAL_WIN32) { +#ifdef USE_WINSOCK + WSACleanup(); +#endif + } +} + +#if !defined(LOAD_WITH_ALTERED_SEARCH_PATH) +#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 +#endif + +#if !defined(LOAD_LIBRARY_SEARCH_SYSTEM32) +#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 +#endif + +/* We use our own typedef here since some headers might lack these */ +typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD); + +/* See function definitions in winbase.h */ +#ifdef UNICODE +# ifdef _WIN32_WCE +# define LOADLIBARYEX L"LoadLibraryExW" +# else +# define LOADLIBARYEX "LoadLibraryExW" +# endif +#else +# define LOADLIBARYEX "LoadLibraryExA" +#endif + +/* + * Curl_load_library() + * + * This is used to dynamically load DLLs using the most secure method available + * for the version of Windows that we are running on. + * + * Parameters: + * + * filename [in] - The filename or full path of the DLL to load. If only the + * filename is passed then the DLL will be loaded from the + * Windows system directory. + * + * Returns the handle of the module on success; otherwise NULL. + */ +HMODULE Curl_load_library(LPCTSTR filename) +{ +#ifndef CURL_WINDOWS_APP + HMODULE hModule = NULL; + LOADLIBRARYEX_FN pLoadLibraryEx = NULL; + + /* Get a handle to kernel32 so we can access it is functions at runtime */ + HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32")); + if(!hKernel32) + return NULL; + + /* Attempt to find LoadLibraryEx() which is only available on Windows 2000 + and above */ + pLoadLibraryEx = + CURLX_FUNCTION_CAST(LOADLIBRARYEX_FN, + (GetProcAddress(hKernel32, LOADLIBARYEX))); + + /* Detect if there is already a path in the filename and load the library if + there is. Note: Both back slashes and forward slashes have been supported + since the earlier days of DOS at an API level although they are not + supported by command prompt */ + if(_tcspbrk(filename, TEXT("\\/"))) { + /** !checksrc! disable BANNEDFUNC 1 **/ + hModule = pLoadLibraryEx ? + pLoadLibraryEx(filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) : + LoadLibrary(filename); + } + /* Detect if KB2533623 is installed, as LOAD_LIBRARY_SEARCH_SYSTEM32 is only + supported on Windows Vista, Windows Server 2008, Windows 7 and Windows + Server 2008 R2 with this patch or natively on Windows 8 and above */ + else if(pLoadLibraryEx && GetProcAddress(hKernel32, "AddDllDirectory")) { + /* Load the DLL from the Windows system directory */ + hModule = pLoadLibraryEx(filename, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + } + else { + /* Attempt to get the Windows system path */ + UINT systemdirlen = GetSystemDirectory(NULL, 0); + if(systemdirlen) { + /* Allocate space for the full DLL path (Room for the null terminator + is included in systemdirlen) */ + size_t filenamelen = _tcslen(filename); + TCHAR *path = malloc(sizeof(TCHAR) * (systemdirlen + 1 + filenamelen)); + if(path && GetSystemDirectory(path, systemdirlen)) { + /* Calculate the full DLL path */ + _tcscpy(path + _tcslen(path), TEXT("\\")); + _tcscpy(path + _tcslen(path), filename); + + /* Load the DLL from the Windows system directory */ + /** !checksrc! disable BANNEDFUNC 1 **/ + hModule = pLoadLibraryEx ? + pLoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) : + LoadLibrary(path); + + } + free(path); + } + } + return hModule; +#else + /* the Universal Windows Platform (UWP) cannot do this */ + (void)filename; + return NULL; +#endif +} + +bool Curl_win32_impersonating(void) +{ +#ifndef CURL_WINDOWS_APP + HANDLE token = NULL; + if(OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token)) { + CloseHandle(token); + return TRUE; + } +#endif + return FALSE; +} + +#endif /* _WIN32 */ diff --git a/third_party/curl/lib/system_win32.h b/third_party/curl/lib/system_win32.h new file mode 100644 index 0000000000..534a5e4988 --- /dev/null +++ b/third_party/curl/lib/system_win32.h @@ -0,0 +1,81 @@ +#ifndef HEADER_CURL_SYSTEM_WIN32_H +#define HEADER_CURL_SYSTEM_WIN32_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef _WIN32 + +#include + +extern LARGE_INTEGER Curl_freq; +extern bool Curl_isVistaOrGreater; +extern bool Curl_isWindows8OrGreater; + +CURLcode Curl_win32_init(long flags); +void Curl_win32_cleanup(long init_flags); + +/* We use our own typedef here since some headers might lack this */ +typedef unsigned int(WINAPI *IF_NAMETOINDEX_FN)(const char *); + +/* This is used instead of if_nametoindex if available on Windows */ +extern IF_NAMETOINDEX_FN Curl_if_nametoindex; + +/* Identical copy of addrinfoexW/ADDRINFOEXW */ +typedef struct addrinfoexW_ +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + PWSTR ai_canonname; + struct sockaddr *ai_addr; + void *ai_blob; + size_t ai_bloblen; + LPGUID ai_provider; + struct addrinfoexW_ *ai_next; +} ADDRINFOEXW_; + +typedef void (CALLBACK *LOOKUP_COMPLETION_FN)(DWORD, DWORD, LPWSAOVERLAPPED); +typedef void (WSAAPI *FREEADDRINFOEXW_FN)(ADDRINFOEXW_*); +typedef int (WSAAPI *GETADDRINFOEXCANCEL_FN)(LPHANDLE); +typedef int (WSAAPI *GETADDRINFOEXW_FN)(PCWSTR, PCWSTR, DWORD, LPGUID, + const ADDRINFOEXW_*, ADDRINFOEXW_**, struct timeval*, LPOVERLAPPED, + LOOKUP_COMPLETION_FN, LPHANDLE); + +extern FREEADDRINFOEXW_FN Curl_FreeAddrInfoExW; +extern GETADDRINFOEXCANCEL_FN Curl_GetAddrInfoExCancel; +extern GETADDRINFOEXW_FN Curl_GetAddrInfoExW; + +bool Curl_win32_impersonating(void); + +/* This is used to dynamically load DLLs */ +HMODULE Curl_load_library(LPCTSTR filename); +#else /* _WIN32 */ +#define Curl_win32_init(x) CURLE_OK +#endif /* !_WIN32 */ + +#endif /* HEADER_CURL_SYSTEM_WIN32_H */ diff --git a/third_party/curl/lib/telnet.c b/third_party/curl/lib/telnet.c new file mode 100644 index 0000000000..91fa10ada6 --- /dev/null +++ b/third_party/curl/lib/telnet.c @@ -0,0 +1,1652 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_TELNET + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include "urldata.h" +#include +#include "transfer.h" +#include "sendf.h" +#include "telnet.h" +#include "connect.h" +#include "progress.h" +#include "system_win32.h" +#include "arpa_telnet.h" +#include "select.h" +#include "strcase.h" +#include "warnless.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define SUBBUFSIZE 512 + +#define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer +#define CURL_SB_TERM(x) \ + do { \ + x->subend = x->subpointer; \ + CURL_SB_CLEAR(x); \ + } while(0) +#define CURL_SB_ACCUM(x,c) \ + do { \ + if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer))) \ + *x->subpointer++ = (c); \ + } while(0) + +#define CURL_SB_GET(x) ((*x->subpointer++)&0xff) +#define CURL_SB_LEN(x) (x->subend - x->subpointer) + +/* For posterity: +#define CURL_SB_PEEK(x) ((*x->subpointer)&0xff) +#define CURL_SB_EOF(x) (x->subpointer >= x->subend) */ + +#ifdef CURL_DISABLE_VERBOSE_STRINGS +#define printoption(a,b,c,d) Curl_nop_stmt +#endif + +static +CURLcode telrcv(struct Curl_easy *data, + const unsigned char *inbuf, /* Data received from socket */ + ssize_t count); /* Number of bytes received */ + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void printoption(struct Curl_easy *data, + const char *direction, + int cmd, int option); +#endif + +static void negotiate(struct Curl_easy *data); +static void send_negotiation(struct Curl_easy *data, int cmd, int option); +static void set_local_option(struct Curl_easy *data, + int option, int newstate); +static void set_remote_option(struct Curl_easy *data, + int option, int newstate); + +static void printsub(struct Curl_easy *data, + int direction, unsigned char *pointer, + size_t length); +static void suboption(struct Curl_easy *data); +static void sendsuboption(struct Curl_easy *data, int option); + +static CURLcode telnet_do(struct Curl_easy *data, bool *done); +static CURLcode telnet_done(struct Curl_easy *data, + CURLcode, bool premature); +static CURLcode send_telnet_data(struct Curl_easy *data, + char *buffer, ssize_t nread); + +/* For negotiation compliant to RFC 1143 */ +#define CURL_NO 0 +#define CURL_YES 1 +#define CURL_WANTYES 2 +#define CURL_WANTNO 3 + +#define CURL_EMPTY 0 +#define CURL_OPPOSITE 1 + +/* + * Telnet receiver states for fsm + */ +typedef enum +{ + CURL_TS_DATA = 0, + CURL_TS_IAC, + CURL_TS_WILL, + CURL_TS_WONT, + CURL_TS_DO, + CURL_TS_DONT, + CURL_TS_CR, + CURL_TS_SB, /* sub-option collection */ + CURL_TS_SE /* looking for sub-option end */ +} TelnetReceive; + +struct TELNET { + int please_negotiate; + int already_negotiated; + int us[256]; + int usq[256]; + int us_preferred[256]; + int him[256]; + int himq[256]; + int him_preferred[256]; + int subnegotiation[256]; + char subopt_ttype[32]; /* Set with suboption TTYPE */ + char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */ + unsigned short subopt_wsx; /* Set with suboption NAWS */ + unsigned short subopt_wsy; /* Set with suboption NAWS */ + TelnetReceive telrcv_state; + struct curl_slist *telnet_vars; /* Environment variables */ + struct dynbuf out; /* output buffer */ + + /* suboptions */ + unsigned char subbuffer[SUBBUFSIZE]; + unsigned char *subpointer, *subend; /* buffer for sub-options */ +}; + + +/* + * TELNET protocol handler. + */ + +const struct Curl_handler Curl_handler_telnet = { + "telnet", /* scheme */ + ZERO_NULL, /* setup_connection */ + telnet_do, /* do_it */ + telnet_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_TELNET, /* defport */ + CURLPROTO_TELNET, /* protocol */ + CURLPROTO_TELNET, /* family */ + PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ +}; + + +static +CURLcode init_telnet(struct Curl_easy *data) +{ + struct TELNET *tn; + + tn = calloc(1, sizeof(struct TELNET)); + if(!tn) + return CURLE_OUT_OF_MEMORY; + + Curl_dyn_init(&tn->out, 0xffff); + data->req.p.telnet = tn; /* make us known */ + + tn->telrcv_state = CURL_TS_DATA; + + /* Init suboptions */ + CURL_SB_CLEAR(tn); + + /* Set the options we want by default */ + tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES; + tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES; + + /* To be compliant with previous releases of libcurl + we enable this option by default. This behavior + can be changed thanks to the "BINARY" option in + CURLOPT_TELNETOPTIONS + */ + tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES; + tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES; + + /* We must allow the server to echo what we sent + but it is not necessary to request the server + to do so (it might forces the server to close + the connection). Hence, we ignore ECHO in the + negotiate function + */ + tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES; + + /* Set the subnegotiation fields to send information + just after negotiation passed (do/will) + + Default values are (0,0) initialized by calloc. + According to the RFC1013 it is valid: + A value equal to zero is acceptable for the width (or height), + and means that no character width (or height) is being sent. + In this case, the width (or height) that will be assumed by the + Telnet server is operating system specific (it will probably be + based upon the terminal type information that may have been sent + using the TERMINAL TYPE Telnet option). */ + tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES; + return CURLE_OK; +} + +static void negotiate(struct Curl_easy *data) +{ + int i; + struct TELNET *tn = data->req.p.telnet; + + for(i = 0; i < CURL_NTELOPTS; i++) { + if(i == CURL_TELOPT_ECHO) + continue; + + if(tn->us_preferred[i] == CURL_YES) + set_local_option(data, i, CURL_YES); + + if(tn->him_preferred[i] == CURL_YES) + set_remote_option(data, i, CURL_YES); + } +} + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void printoption(struct Curl_easy *data, + const char *direction, int cmd, int option) +{ + if(data->set.verbose) { + if(cmd == CURL_IAC) { + if(CURL_TELCMD_OK(option)) + infof(data, "%s IAC %s", direction, CURL_TELCMD(option)); + else + infof(data, "%s IAC %d", direction, option); + } + else { + const char *fmt = (cmd == CURL_WILL) ? "WILL" : + (cmd == CURL_WONT) ? "WONT" : + (cmd == CURL_DO) ? "DO" : + (cmd == CURL_DONT) ? "DONT" : 0; + if(fmt) { + const char *opt; + if(CURL_TELOPT_OK(option)) + opt = CURL_TELOPT(option); + else if(option == CURL_TELOPT_EXOPL) + opt = "EXOPL"; + else + opt = NULL; + + if(opt) + infof(data, "%s %s %s", direction, fmt, opt); + else + infof(data, "%s %s %d", direction, fmt, option); + } + else + infof(data, "%s %d %d", direction, cmd, option); + } + } +} +#endif + +static void send_negotiation(struct Curl_easy *data, int cmd, int option) +{ + unsigned char buf[3]; + ssize_t bytes_written; + struct connectdata *conn = data->conn; + + buf[0] = CURL_IAC; + buf[1] = (unsigned char)cmd; + buf[2] = (unsigned char)option; + + bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3); + if(bytes_written < 0) { + int err = SOCKERRNO; + failf(data,"Sending data failed (%d)",err); + } + + printoption(data, "SENT", cmd, option); +} + +static +void set_remote_option(struct Curl_easy *data, int option, int newstate) +{ + struct TELNET *tn = data->req.p.telnet; + if(newstate == CURL_YES) { + switch(tn->him[option]) { + case CURL_NO: + tn->him[option] = CURL_WANTYES; + send_negotiation(data, CURL_DO, option); + break; + + case CURL_YES: + /* Already enabled */ + break; + + case CURL_WANTNO: + switch(tn->himq[option]) { + case CURL_EMPTY: + /* Already negotiating for CURL_YES, queue the request */ + tn->himq[option] = CURL_OPPOSITE; + break; + case CURL_OPPOSITE: + /* Error: already queued an enable request */ + break; + } + break; + + case CURL_WANTYES: + switch(tn->himq[option]) { + case CURL_EMPTY: + /* Error: already negotiating for enable */ + break; + case CURL_OPPOSITE: + tn->himq[option] = CURL_EMPTY; + break; + } + break; + } + } + else { /* NO */ + switch(tn->him[option]) { + case CURL_NO: + /* Already disabled */ + break; + + case CURL_YES: + tn->him[option] = CURL_WANTNO; + send_negotiation(data, CURL_DONT, option); + break; + + case CURL_WANTNO: + switch(tn->himq[option]) { + case CURL_EMPTY: + /* Already negotiating for NO */ + break; + case CURL_OPPOSITE: + tn->himq[option] = CURL_EMPTY; + break; + } + break; + + case CURL_WANTYES: + switch(tn->himq[option]) { + case CURL_EMPTY: + tn->himq[option] = CURL_OPPOSITE; + break; + case CURL_OPPOSITE: + break; + } + break; + } + } +} + +static +void rec_will(struct Curl_easy *data, int option) +{ + struct TELNET *tn = data->req.p.telnet; + switch(tn->him[option]) { + case CURL_NO: + if(tn->him_preferred[option] == CURL_YES) { + tn->him[option] = CURL_YES; + send_negotiation(data, CURL_DO, option); + } + else + send_negotiation(data, CURL_DONT, option); + + break; + + case CURL_YES: + /* Already enabled */ + break; + + case CURL_WANTNO: + switch(tn->himq[option]) { + case CURL_EMPTY: + /* Error: DONT answered by WILL */ + tn->him[option] = CURL_NO; + break; + case CURL_OPPOSITE: + /* Error: DONT answered by WILL */ + tn->him[option] = CURL_YES; + tn->himq[option] = CURL_EMPTY; + break; + } + break; + + case CURL_WANTYES: + switch(tn->himq[option]) { + case CURL_EMPTY: + tn->him[option] = CURL_YES; + break; + case CURL_OPPOSITE: + tn->him[option] = CURL_WANTNO; + tn->himq[option] = CURL_EMPTY; + send_negotiation(data, CURL_DONT, option); + break; + } + break; + } +} + +static +void rec_wont(struct Curl_easy *data, int option) +{ + struct TELNET *tn = data->req.p.telnet; + switch(tn->him[option]) { + case CURL_NO: + /* Already disabled */ + break; + + case CURL_YES: + tn->him[option] = CURL_NO; + send_negotiation(data, CURL_DONT, option); + break; + + case CURL_WANTNO: + switch(tn->himq[option]) { + case CURL_EMPTY: + tn->him[option] = CURL_NO; + break; + + case CURL_OPPOSITE: + tn->him[option] = CURL_WANTYES; + tn->himq[option] = CURL_EMPTY; + send_negotiation(data, CURL_DO, option); + break; + } + break; + + case CURL_WANTYES: + switch(tn->himq[option]) { + case CURL_EMPTY: + tn->him[option] = CURL_NO; + break; + case CURL_OPPOSITE: + tn->him[option] = CURL_NO; + tn->himq[option] = CURL_EMPTY; + break; + } + break; + } +} + +static void +set_local_option(struct Curl_easy *data, int option, int newstate) +{ + struct TELNET *tn = data->req.p.telnet; + if(newstate == CURL_YES) { + switch(tn->us[option]) { + case CURL_NO: + tn->us[option] = CURL_WANTYES; + send_negotiation(data, CURL_WILL, option); + break; + + case CURL_YES: + /* Already enabled */ + break; + + case CURL_WANTNO: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Already negotiating for CURL_YES, queue the request */ + tn->usq[option] = CURL_OPPOSITE; + break; + case CURL_OPPOSITE: + /* Error: already queued an enable request */ + break; + } + break; + + case CURL_WANTYES: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Error: already negotiating for enable */ + break; + case CURL_OPPOSITE: + tn->usq[option] = CURL_EMPTY; + break; + } + break; + } + } + else { /* NO */ + switch(tn->us[option]) { + case CURL_NO: + /* Already disabled */ + break; + + case CURL_YES: + tn->us[option] = CURL_WANTNO; + send_negotiation(data, CURL_WONT, option); + break; + + case CURL_WANTNO: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Already negotiating for NO */ + break; + case CURL_OPPOSITE: + tn->usq[option] = CURL_EMPTY; + break; + } + break; + + case CURL_WANTYES: + switch(tn->usq[option]) { + case CURL_EMPTY: + tn->usq[option] = CURL_OPPOSITE; + break; + case CURL_OPPOSITE: + break; + } + break; + } + } +} + +static +void rec_do(struct Curl_easy *data, int option) +{ + struct TELNET *tn = data->req.p.telnet; + switch(tn->us[option]) { + case CURL_NO: + if(tn->us_preferred[option] == CURL_YES) { + tn->us[option] = CURL_YES; + send_negotiation(data, CURL_WILL, option); + if(tn->subnegotiation[option] == CURL_YES) + /* transmission of data option */ + sendsuboption(data, option); + } + else if(tn->subnegotiation[option] == CURL_YES) { + /* send information to achieve this option */ + tn->us[option] = CURL_YES; + send_negotiation(data, CURL_WILL, option); + sendsuboption(data, option); + } + else + send_negotiation(data, CURL_WONT, option); + break; + + case CURL_YES: + /* Already enabled */ + break; + + case CURL_WANTNO: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Error: DONT answered by WILL */ + tn->us[option] = CURL_NO; + break; + case CURL_OPPOSITE: + /* Error: DONT answered by WILL */ + tn->us[option] = CURL_YES; + tn->usq[option] = CURL_EMPTY; + break; + } + break; + + case CURL_WANTYES: + switch(tn->usq[option]) { + case CURL_EMPTY: + tn->us[option] = CURL_YES; + if(tn->subnegotiation[option] == CURL_YES) { + /* transmission of data option */ + sendsuboption(data, option); + } + break; + case CURL_OPPOSITE: + tn->us[option] = CURL_WANTNO; + tn->himq[option] = CURL_EMPTY; + send_negotiation(data, CURL_WONT, option); + break; + } + break; + } +} + +static +void rec_dont(struct Curl_easy *data, int option) +{ + struct TELNET *tn = data->req.p.telnet; + switch(tn->us[option]) { + case CURL_NO: + /* Already disabled */ + break; + + case CURL_YES: + tn->us[option] = CURL_NO; + send_negotiation(data, CURL_WONT, option); + break; + + case CURL_WANTNO: + switch(tn->usq[option]) { + case CURL_EMPTY: + tn->us[option] = CURL_NO; + break; + + case CURL_OPPOSITE: + tn->us[option] = CURL_WANTYES; + tn->usq[option] = CURL_EMPTY; + send_negotiation(data, CURL_WILL, option); + break; + } + break; + + case CURL_WANTYES: + switch(tn->usq[option]) { + case CURL_EMPTY: + tn->us[option] = CURL_NO; + break; + case CURL_OPPOSITE: + tn->us[option] = CURL_NO; + tn->usq[option] = CURL_EMPTY; + break; + } + break; + } +} + + +static void printsub(struct Curl_easy *data, + int direction, /* '<' or '>' */ + unsigned char *pointer, /* where suboption data is */ + size_t length) /* length of suboption data */ +{ + if(data->set.verbose) { + unsigned int i = 0; + if(direction) { + infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT"); + if(length >= 3) { + int j; + + i = pointer[length-2]; + j = pointer[length-1]; + + if(i != CURL_IAC || j != CURL_SE) { + infof(data, "(terminated by "); + if(CURL_TELOPT_OK(i)) + infof(data, "%s ", CURL_TELOPT(i)); + else if(CURL_TELCMD_OK(i)) + infof(data, "%s ", CURL_TELCMD(i)); + else + infof(data, "%u ", i); + if(CURL_TELOPT_OK(j)) + infof(data, "%s", CURL_TELOPT(j)); + else if(CURL_TELCMD_OK(j)) + infof(data, "%s", CURL_TELCMD(j)); + else + infof(data, "%d", j); + infof(data, ", not IAC SE) "); + } + } + length -= 2; + } + if(length < 1) { + infof(data, "(Empty suboption?)"); + return; + } + + if(CURL_TELOPT_OK(pointer[0])) { + switch(pointer[0]) { + case CURL_TELOPT_TTYPE: + case CURL_TELOPT_XDISPLOC: + case CURL_TELOPT_NEW_ENVIRON: + case CURL_TELOPT_NAWS: + infof(data, "%s", CURL_TELOPT(pointer[0])); + break; + default: + infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0])); + break; + } + } + else + infof(data, "%d (unknown)", pointer[i]); + + switch(pointer[0]) { + case CURL_TELOPT_NAWS: + if(length > 4) + infof(data, "Width: %d ; Height: %d", (pointer[1]<<8) | pointer[2], + (pointer[3]<<8) | pointer[4]); + break; + default: + switch(pointer[1]) { + case CURL_TELQUAL_IS: + infof(data, " IS"); + break; + case CURL_TELQUAL_SEND: + infof(data, " SEND"); + break; + case CURL_TELQUAL_INFO: + infof(data, " INFO/REPLY"); + break; + case CURL_TELQUAL_NAME: + infof(data, " NAME"); + break; + } + + switch(pointer[0]) { + case CURL_TELOPT_TTYPE: + case CURL_TELOPT_XDISPLOC: + pointer[length] = 0; + infof(data, " \"%s\"", &pointer[2]); + break; + case CURL_TELOPT_NEW_ENVIRON: + if(pointer[1] == CURL_TELQUAL_IS) { + infof(data, " "); + for(i = 3; i < length; i++) { + switch(pointer[i]) { + case CURL_NEW_ENV_VAR: + infof(data, ", "); + break; + case CURL_NEW_ENV_VALUE: + infof(data, " = "); + break; + default: + infof(data, "%c", pointer[i]); + break; + } + } + } + break; + default: + for(i = 2; i < length; i++) + infof(data, " %.2x", pointer[i]); + break; + } + } + } +} + +#ifdef _MSC_VER +#pragma warning(push) +/* warning C4706: assignment within conditional expression */ +#pragma warning(disable:4706) +#endif +static bool str_is_nonascii(const char *str) +{ + char c; + while((c = *str++)) + if(c & 0x80) + return TRUE; + + return FALSE; +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +static CURLcode check_telnet_options(struct Curl_easy *data) +{ + struct curl_slist *head; + struct curl_slist *beg; + struct TELNET *tn = data->req.p.telnet; + CURLcode result = CURLE_OK; + + /* Add the username as an environment variable if it + was given on the command line */ + if(data->state.aptr.user) { + char buffer[256]; + if(str_is_nonascii(data->conn->user)) { + DEBUGF(infof(data, "set a non ASCII username in telnet")); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user); + beg = curl_slist_append(tn->telnet_vars, buffer); + if(!beg) { + curl_slist_free_all(tn->telnet_vars); + tn->telnet_vars = NULL; + return CURLE_OUT_OF_MEMORY; + } + tn->telnet_vars = beg; + tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; + } + + for(head = data->set.telnet_options; head && !result; head = head->next) { + size_t olen; + char *option = head->data; + char *arg; + char *sep = strchr(option, '='); + if(sep) { + olen = sep - option; + arg = ++sep; + if(str_is_nonascii(arg)) + continue; + switch(olen) { + case 5: + /* Terminal type */ + if(strncasecompare(option, "TTYPE", 5)) { + size_t l = strlen(arg); + if(l < sizeof(tn->subopt_ttype)) { + strcpy(tn->subopt_ttype, arg); + tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; + break; + } + } + result = CURLE_UNKNOWN_OPTION; + break; + + case 8: + /* Display variable */ + if(strncasecompare(option, "XDISPLOC", 8)) { + size_t l = strlen(arg); + if(l < sizeof(tn->subopt_xdisploc)) { + strcpy(tn->subopt_xdisploc, arg); + tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; + break; + } + } + result = CURLE_UNKNOWN_OPTION; + break; + + case 7: + /* Environment variable */ + if(strncasecompare(option, "NEW_ENV", 7)) { + beg = curl_slist_append(tn->telnet_vars, arg); + if(!beg) { + result = CURLE_OUT_OF_MEMORY; + break; + } + tn->telnet_vars = beg; + tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; + } + else + result = CURLE_UNKNOWN_OPTION; + break; + + case 2: + /* Window Size */ + if(strncasecompare(option, "WS", 2)) { + char *p; + unsigned long x = strtoul(arg, &p, 10); + unsigned long y = 0; + if(x && (x <= 0xffff) && Curl_raw_tolower(*p) == 'x') { + p++; + y = strtoul(p, NULL, 10); + if(y && (y <= 0xffff)) { + tn->subopt_wsx = (unsigned short)x; + tn->subopt_wsy = (unsigned short)y; + tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES; + } + } + if(!y) { + failf(data, "Syntax error in telnet option: %s", head->data); + result = CURLE_SETOPT_OPTION_SYNTAX; + } + } + else + result = CURLE_UNKNOWN_OPTION; + break; + + case 6: + /* To take care or not of the 8th bit in data exchange */ + if(strncasecompare(option, "BINARY", 6)) { + int binary_option = atoi(arg); + if(binary_option != 1) { + tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; + tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO; + } + } + else + result = CURLE_UNKNOWN_OPTION; + break; + default: + failf(data, "Unknown telnet option %s", head->data); + result = CURLE_UNKNOWN_OPTION; + break; + } + } + else { + failf(data, "Syntax error in telnet option: %s", head->data); + result = CURLE_SETOPT_OPTION_SYNTAX; + } + } + + if(result) { + curl_slist_free_all(tn->telnet_vars); + tn->telnet_vars = NULL; + } + + return result; +} + +/* + * suboption() + * + * Look at the sub-option buffer, and try to be helpful to the other + * side. + */ + +static void suboption(struct Curl_easy *data) +{ + struct curl_slist *v; + unsigned char temp[2048]; + ssize_t bytes_written; + size_t len; + int err; + struct TELNET *tn = data->req.p.telnet; + struct connectdata *conn = data->conn; + + printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2); + switch(CURL_SB_GET(tn)) { + case CURL_TELOPT_TTYPE: + len = strlen(tn->subopt_ttype) + 4 + 2; + msnprintf((char *)temp, sizeof(temp), + "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE, + CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE); + bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data,"Sending data failed (%d)",err); + } + printsub(data, '>', &temp[2], len-2); + break; + case CURL_TELOPT_XDISPLOC: + len = strlen(tn->subopt_xdisploc) + 4 + 2; + msnprintf((char *)temp, sizeof(temp), + "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC, + CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE); + bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data,"Sending data failed (%d)",err); + } + printsub(data, '>', &temp[2], len-2); + break; + case CURL_TELOPT_NEW_ENVIRON: + msnprintf((char *)temp, sizeof(temp), + "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON, + CURL_TELQUAL_IS); + len = 4; + + for(v = tn->telnet_vars; v; v = v->next) { + size_t tmplen = (strlen(v->data) + 1); + /* Add the variable if it fits */ + if(len + tmplen < (int)sizeof(temp)-6) { + char *s = strchr(v->data, ','); + if(!s) + len += msnprintf((char *)&temp[len], sizeof(temp) - len, + "%c%s", CURL_NEW_ENV_VAR, v->data); + else { + size_t vlen = s - v->data; + len += msnprintf((char *)&temp[len], sizeof(temp) - len, + "%c%.*s%c%s", CURL_NEW_ENV_VAR, + (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s); + } + } + } + msnprintf((char *)&temp[len], sizeof(temp) - len, + "%c%c", CURL_IAC, CURL_SE); + len += 2; + bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data,"Sending data failed (%d)",err); + } + printsub(data, '>', &temp[2], len-2); + break; + } + return; +} + + +/* + * sendsuboption() + * + * Send suboption information to the server side. + */ + +static void sendsuboption(struct Curl_easy *data, int option) +{ + ssize_t bytes_written; + int err; + unsigned short x, y; + unsigned char *uc1, *uc2; + struct TELNET *tn = data->req.p.telnet; + struct connectdata *conn = data->conn; + + switch(option) { + case CURL_TELOPT_NAWS: + /* We prepare data to be sent */ + CURL_SB_CLEAR(tn); + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SB); + CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS); + /* We must deal either with little or big endian processors */ + /* Window size must be sent according to the 'network order' */ + x = htons(tn->subopt_wsx); + y = htons(tn->subopt_wsy); + uc1 = (unsigned char *)&x; + uc2 = (unsigned char *)&y; + CURL_SB_ACCUM(tn, uc1[0]); + CURL_SB_ACCUM(tn, uc1[1]); + CURL_SB_ACCUM(tn, uc2[0]); + CURL_SB_ACCUM(tn, uc2[1]); + + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SE); + CURL_SB_TERM(tn); + /* data suboption is now ready */ + + printsub(data, '>', (unsigned char *)tn->subbuffer + 2, + CURL_SB_LEN(tn)-2); + + /* we send the header of the suboption... */ + bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data, "Sending data failed (%d)", err); + } + /* ... then the window size with the send_telnet_data() function + to deal with 0xFF cases ... */ + send_telnet_data(data, (char *)tn->subbuffer + 3, 4); + /* ... and the footer */ + bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data, "Sending data failed (%d)", err); + } + break; + } +} + + +static +CURLcode telrcv(struct Curl_easy *data, + const unsigned char *inbuf, /* Data received from socket */ + ssize_t count) /* Number of bytes received */ +{ + unsigned char c; + CURLcode result; + int in = 0; + int startwrite = -1; + struct TELNET *tn = data->req.p.telnet; + +#define startskipping() \ + if(startwrite >= 0) { \ + result = Curl_client_write(data, \ + CLIENTWRITE_BODY, \ + (char *)&inbuf[startwrite], \ + in-startwrite); \ + if(result) \ + return result; \ + } \ + startwrite = -1 + +#define writebyte() \ + if(startwrite < 0) \ + startwrite = in + +#define bufferflush() startskipping() + + while(count--) { + c = inbuf[in]; + + switch(tn->telrcv_state) { + case CURL_TS_CR: + tn->telrcv_state = CURL_TS_DATA; + if(c == '\0') { + startskipping(); + break; /* Ignore \0 after CR */ + } + writebyte(); + break; + + case CURL_TS_DATA: + if(c == CURL_IAC) { + tn->telrcv_state = CURL_TS_IAC; + startskipping(); + break; + } + else if(c == '\r') + tn->telrcv_state = CURL_TS_CR; + writebyte(); + break; + + case CURL_TS_IAC: +process_iac: + DEBUGASSERT(startwrite < 0); + switch(c) { + case CURL_WILL: + tn->telrcv_state = CURL_TS_WILL; + break; + case CURL_WONT: + tn->telrcv_state = CURL_TS_WONT; + break; + case CURL_DO: + tn->telrcv_state = CURL_TS_DO; + break; + case CURL_DONT: + tn->telrcv_state = CURL_TS_DONT; + break; + case CURL_SB: + CURL_SB_CLEAR(tn); + tn->telrcv_state = CURL_TS_SB; + break; + case CURL_IAC: + tn->telrcv_state = CURL_TS_DATA; + writebyte(); + break; + case CURL_DM: + case CURL_NOP: + case CURL_GA: + default: + tn->telrcv_state = CURL_TS_DATA; + printoption(data, "RCVD", CURL_IAC, c); + break; + } + break; + + case CURL_TS_WILL: + printoption(data, "RCVD", CURL_WILL, c); + tn->please_negotiate = 1; + rec_will(data, c); + tn->telrcv_state = CURL_TS_DATA; + break; + + case CURL_TS_WONT: + printoption(data, "RCVD", CURL_WONT, c); + tn->please_negotiate = 1; + rec_wont(data, c); + tn->telrcv_state = CURL_TS_DATA; + break; + + case CURL_TS_DO: + printoption(data, "RCVD", CURL_DO, c); + tn->please_negotiate = 1; + rec_do(data, c); + tn->telrcv_state = CURL_TS_DATA; + break; + + case CURL_TS_DONT: + printoption(data, "RCVD", CURL_DONT, c); + tn->please_negotiate = 1; + rec_dont(data, c); + tn->telrcv_state = CURL_TS_DATA; + break; + + case CURL_TS_SB: + if(c == CURL_IAC) + tn->telrcv_state = CURL_TS_SE; + else + CURL_SB_ACCUM(tn, c); + break; + + case CURL_TS_SE: + if(c != CURL_SE) { + if(c != CURL_IAC) { + /* + * This is an error. We only expect to get "IAC IAC" or "IAC SE". + * Several things may have happened. An IAC was not doubled, the + * IAC SE was left off, or another option got inserted into the + * suboption are all possibilities. If we assume that the IAC was + * not doubled, and really the IAC SE was left off, we could get + * into an infinite loop here. So, instead, we terminate the + * suboption, and process the partial suboption if we can. + */ + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, c); + tn->subpointer -= 2; + CURL_SB_TERM(tn); + + printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c); + suboption(data); /* handle sub-option */ + tn->telrcv_state = CURL_TS_IAC; + goto process_iac; + } + CURL_SB_ACCUM(tn, c); + tn->telrcv_state = CURL_TS_SB; + } + else { + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SE); + tn->subpointer -= 2; + CURL_SB_TERM(tn); + suboption(data); /* handle sub-option */ + tn->telrcv_state = CURL_TS_DATA; + } + break; + } + ++in; + } + bufferflush(); + return CURLE_OK; +} + +/* Escape and send a telnet data block */ +static CURLcode send_telnet_data(struct Curl_easy *data, + char *buffer, ssize_t nread) +{ + size_t i, outlen; + unsigned char *outbuf; + CURLcode result = CURLE_OK; + size_t bytes_written; + size_t total_written = 0; + struct connectdata *conn = data->conn; + struct TELNET *tn = data->req.p.telnet; + + DEBUGASSERT(tn); + DEBUGASSERT(nread > 0); + if(nread < 0) + return CURLE_TOO_LARGE; + + if(memchr(buffer, CURL_IAC, nread)) { + /* only use the escape buffer when necessary */ + Curl_dyn_reset(&tn->out); + + for(i = 0; i < (size_t)nread && !result; i++) { + result = Curl_dyn_addn(&tn->out, &buffer[i], 1); + if(!result && ((unsigned char)buffer[i] == CURL_IAC)) + /* IAC is FF in hex */ + result = Curl_dyn_addn(&tn->out, "\xff", 1); + } + + outlen = Curl_dyn_len(&tn->out); + outbuf = Curl_dyn_uptr(&tn->out); + } + else { + outlen = (size_t)nread; + outbuf = (unsigned char *)buffer; + } + while(!result && total_written < outlen) { + /* Make sure socket is writable to avoid EWOULDBLOCK condition */ + struct pollfd pfd[1]; + pfd[0].fd = conn->sock[FIRSTSOCKET]; + pfd[0].events = POLLOUT; + switch(Curl_poll(pfd, 1, -1)) { + case -1: /* error, abort writing */ + case 0: /* timeout (will never happen) */ + result = CURLE_SEND_ERROR; + break; + default: /* write! */ + bytes_written = 0; + result = Curl_xfer_send(data, outbuf + total_written, + outlen - total_written, &bytes_written); + total_written += bytes_written; + break; + } + } + + return result; +} + +static CURLcode telnet_done(struct Curl_easy *data, + CURLcode status, bool premature) +{ + struct TELNET *tn = data->req.p.telnet; + (void)status; /* unused */ + (void)premature; /* not used */ + + if(!tn) + return CURLE_OK; + + curl_slist_free_all(tn->telnet_vars); + tn->telnet_vars = NULL; + Curl_dyn_free(&tn->out); + return CURLE_OK; +} + +static CURLcode telnet_do(struct Curl_easy *data, bool *done) +{ + CURLcode result; + struct connectdata *conn = data->conn; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; +#ifdef USE_WINSOCK + WSAEVENT event_handle; + WSANETWORKEVENTS events; + HANDLE stdin_handle; + HANDLE objs[2]; + DWORD obj_count; + DWORD wait_timeout; + DWORD readfile_read; + int err; +#else + timediff_t interval_ms; + struct pollfd pfd[2]; + int poll_cnt; + curl_off_t total_dl = 0; + curl_off_t total_ul = 0; +#endif + ssize_t nread; + struct curltime now; + bool keepon = TRUE; + char buffer[4*1024]; + struct TELNET *tn; + + *done = TRUE; /* unconditionally */ + + result = init_telnet(data); + if(result) + return result; + + tn = data->req.p.telnet; + + result = check_telnet_options(data); + if(result) + return result; + +#ifdef USE_WINSOCK + /* We want to wait for both stdin and the socket. Since + ** the select() function in winsock only works on sockets + ** we have to use the WaitForMultipleObjects() call. + */ + + /* First, create a sockets event object */ + event_handle = WSACreateEvent(); + if(event_handle == WSA_INVALID_EVENT) { + failf(data, "WSACreateEvent failed (%d)", SOCKERRNO); + return CURLE_FAILED_INIT; + } + + /* Tell winsock what events we want to listen to */ + if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) { + WSACloseEvent(event_handle); + return CURLE_OK; + } + + /* The get the Windows file handle for stdin */ + stdin_handle = GetStdHandle(STD_INPUT_HANDLE); + + /* Create the list of objects to wait for */ + objs[0] = event_handle; + objs[1] = stdin_handle; + + /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it, + else use the old WaitForMultipleObjects() way */ + if(GetFileType(stdin_handle) == FILE_TYPE_PIPE || + data->set.is_fread_set) { + /* Do not wait for stdin_handle, just wait for event_handle */ + obj_count = 1; + /* Check stdin_handle per 100 milliseconds */ + wait_timeout = 100; + } + else { + obj_count = 2; + wait_timeout = 1000; + } + + /* Keep on listening and act on events */ + while(keepon) { + const DWORD buf_size = (DWORD)sizeof(buffer); + DWORD waitret = WaitForMultipleObjects(obj_count, objs, + FALSE, wait_timeout); + switch(waitret) { + + case WAIT_TIMEOUT: + { + for(;;) { + if(data->set.is_fread_set) { + size_t n; + /* read from user-supplied method */ + n = data->state.fread_func(buffer, 1, buf_size, data->state.in); + if(n == CURL_READFUNC_ABORT) { + keepon = FALSE; + result = CURLE_READ_ERROR; + break; + } + + if(n == CURL_READFUNC_PAUSE) + break; + + if(n == 0) /* no bytes */ + break; + + /* fall through with number of bytes read */ + readfile_read = (DWORD)n; + } + else { + /* read from stdin */ + if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL, + &readfile_read, NULL)) { + keepon = FALSE; + result = CURLE_READ_ERROR; + break; + } + + if(!readfile_read) + break; + + if(!ReadFile(stdin_handle, buffer, buf_size, + &readfile_read, NULL)) { + keepon = FALSE; + result = CURLE_READ_ERROR; + break; + } + } + + result = send_telnet_data(data, buffer, readfile_read); + if(result) { + keepon = FALSE; + break; + } + } + } + break; + + case WAIT_OBJECT_0 + 1: + { + if(!ReadFile(stdin_handle, buffer, buf_size, + &readfile_read, NULL)) { + keepon = FALSE; + result = CURLE_READ_ERROR; + break; + } + + result = send_telnet_data(data, buffer, readfile_read); + if(result) { + keepon = FALSE; + break; + } + } + break; + + case WAIT_OBJECT_0: + { + events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) { + err = SOCKERRNO; + if(err != EINPROGRESS) { + infof(data, "WSAEnumNetworkEvents failed (%d)", err); + keepon = FALSE; + result = CURLE_READ_ERROR; + } + break; + } + if(events.lNetworkEvents & FD_READ) { + /* read data from network */ + result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread); + /* read would have blocked. Loop again */ + if(result == CURLE_AGAIN) + break; + /* returned not-zero, this an error */ + else if(result) { + keepon = FALSE; + break; + } + /* returned zero but actually received 0 or less here, + the server closed the connection and we bail out */ + else if(nread <= 0) { + keepon = FALSE; + break; + } + + result = telrcv(data, (unsigned char *) buffer, nread); + if(result) { + keepon = FALSE; + break; + } + + /* Negotiate if the peer has started negotiating, + otherwise do not. We do not want to speak telnet with + non-telnet servers, like POP or SMTP. */ + if(tn->please_negotiate && !tn->already_negotiated) { + negotiate(data); + tn->already_negotiated = 1; + } + } + if(events.lNetworkEvents & FD_CLOSE) { + keepon = FALSE; + } + } + break; + + } + + if(data->set.timeout) { + now = Curl_now(); + if(Curl_timediff(now, conn->created) >= data->set.timeout) { + failf(data, "Time-out"); + result = CURLE_OPERATION_TIMEDOUT; + keepon = FALSE; + } + } + } + + /* We called WSACreateEvent, so call WSACloseEvent */ + if(!WSACloseEvent(event_handle)) { + infof(data, "WSACloseEvent failed (%d)", SOCKERRNO); + } +#else + pfd[0].fd = sockfd; + pfd[0].events = POLLIN; + + if(data->set.is_fread_set) { + poll_cnt = 1; + interval_ms = 100; /* poll user-supplied read function */ + } + else { + /* really using fread, so infile is a FILE* */ + pfd[1].fd = fileno((FILE *)data->state.in); + pfd[1].events = POLLIN; + poll_cnt = 2; + interval_ms = 1 * 1000; + if(pfd[1].fd < 0) { + failf(data, "cannot read input"); + result = CURLE_RECV_ERROR; + keepon = FALSE; + } + } + + while(keepon) { + DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt)); + switch(Curl_poll(pfd, (unsigned int)poll_cnt, interval_ms)) { + case -1: /* error, stop reading */ + keepon = FALSE; + continue; + case 0: /* timeout */ + pfd[0].revents = 0; + pfd[1].revents = 0; + FALLTHROUGH(); + default: /* read! */ + if(pfd[0].revents & POLLIN) { + /* read data from network */ + result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread); + /* read would have blocked. Loop again */ + if(result == CURLE_AGAIN) + break; + /* 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, unexpected ECONNRESET on recv")); + } + break; + } + /* returned zero but actually received 0 or less here, + the server closed the connection and we bail out */ + else if(nread <= 0) { + keepon = FALSE; + break; + } + + total_dl += nread; + result = Curl_pgrsSetDownloadCounter(data, total_dl); + if(!result) + result = telrcv(data, (unsigned char *)buffer, nread); + if(result) { + keepon = FALSE; + break; + } + + /* Negotiate if the peer has started negotiating, + otherwise do not. We do not want to speak telnet with + non-telnet servers, like POP or SMTP. */ + if(tn->please_negotiate && !tn->already_negotiated) { + negotiate(data); + tn->already_negotiated = 1; + } + } + + nread = 0; + if(poll_cnt == 2) { + if(pfd[1].revents & POLLIN) { /* read from in file */ + nread = read(pfd[1].fd, buffer, sizeof(buffer)); + } + } + else { + /* read from user-supplied method */ + nread = (int)data->state.fread_func(buffer, 1, sizeof(buffer), + data->state.in); + if(nread == CURL_READFUNC_ABORT) { + keepon = FALSE; + break; + } + if(nread == CURL_READFUNC_PAUSE) + break; + } + + if(nread > 0) { + result = send_telnet_data(data, buffer, nread); + if(result) { + keepon = FALSE; + break; + } + total_ul += nread; + Curl_pgrsSetUploadCounter(data, total_ul); + } + else if(nread < 0) + keepon = FALSE; + + break; + } /* poll switch statement */ + + if(data->set.timeout) { + now = Curl_now(); + if(Curl_timediff(now, conn->created) >= data->set.timeout) { + failf(data, "Time-out"); + result = CURLE_OPERATION_TIMEDOUT; + keepon = FALSE; + } + } + + if(Curl_pgrsUpdate(data)) { + result = CURLE_ABORTED_BY_CALLBACK; + break; + } + } +#endif + /* mark this as "no further transfer wanted" */ + Curl_xfer_setup_nop(data); + + return result; +} +#endif diff --git a/third_party/curl/lib/telnet.h b/third_party/curl/lib/telnet.h new file mode 100644 index 0000000000..30782d8375 --- /dev/null +++ b/third_party/curl/lib/telnet.h @@ -0,0 +1,30 @@ +#ifndef HEADER_CURL_TELNET_H +#define HEADER_CURL_TELNET_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#ifndef CURL_DISABLE_TELNET +extern const struct Curl_handler Curl_handler_telnet; +#endif + +#endif /* HEADER_CURL_TELNET_H */ diff --git a/third_party/curl/lib/tftp.c b/third_party/curl/lib/tftp.c new file mode 100644 index 0000000000..c4cc8049d6 --- /dev/null +++ b/third_party/curl/lib/tftp.c @@ -0,0 +1,1407 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_TFTP + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include "urldata.h" +#include +#include "cf-socket.h" +#include "transfer.h" +#include "sendf.h" +#include "tftp.h" +#include "progress.h" +#include "connect.h" +#include "strerror.h" +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "multiif.h" +#include "url.h" +#include "strcase.h" +#include "speedcheck.h" +#include "select.h" +#include "escape.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* RFC2348 allows the block size to be negotiated */ +#define TFTP_BLKSIZE_DEFAULT 512 +#define TFTP_OPTION_BLKSIZE "blksize" + +/* from RFC2349: */ +#define TFTP_OPTION_TSIZE "tsize" +#define TFTP_OPTION_INTERVAL "timeout" + +typedef enum { + TFTP_MODE_NETASCII = 0, + TFTP_MODE_OCTET +} tftp_mode_t; + +typedef enum { + TFTP_STATE_START = 0, + TFTP_STATE_RX, + TFTP_STATE_TX, + TFTP_STATE_FIN +} tftp_state_t; + +typedef enum { + TFTP_EVENT_NONE = -1, + TFTP_EVENT_INIT = 0, + TFTP_EVENT_RRQ = 1, + TFTP_EVENT_WRQ = 2, + TFTP_EVENT_DATA = 3, + TFTP_EVENT_ACK = 4, + TFTP_EVENT_ERROR = 5, + TFTP_EVENT_OACK = 6, + TFTP_EVENT_TIMEOUT +} tftp_event_t; + +typedef enum { + TFTP_ERR_UNDEF = 0, + TFTP_ERR_NOTFOUND, + TFTP_ERR_PERM, + TFTP_ERR_DISKFULL, + TFTP_ERR_ILLEGAL, + TFTP_ERR_UNKNOWNID, + TFTP_ERR_EXISTS, + TFTP_ERR_NOSUCHUSER, /* This will never be triggered by this code */ + + /* The remaining error codes are internal to curl */ + TFTP_ERR_NONE = -100, + TFTP_ERR_TIMEOUT, + TFTP_ERR_NORESPONSE +} tftp_error_t; + +struct tftp_packet { + unsigned char *data; +}; + +struct tftp_state_data { + tftp_state_t state; + tftp_mode_t mode; + tftp_error_t error; + tftp_event_t event; + struct Curl_easy *data; + curl_socket_t sockfd; + int retries; + int retry_time; + int retry_max; + time_t rx_time; + struct Curl_sockaddr_storage local_addr; + struct Curl_sockaddr_storage remote_addr; + curl_socklen_t remote_addrlen; + int rbytes; + int sbytes; + int blksize; + int requested_blksize; + unsigned short block; + struct tftp_packet rpacket; + struct tftp_packet spacket; +}; + + +/* Forward declarations */ +static CURLcode tftp_rx(struct tftp_state_data *state, tftp_event_t event); +static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event); +static CURLcode tftp_connect(struct Curl_easy *data, bool *done); +static CURLcode tftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection); +static CURLcode tftp_do(struct Curl_easy *data, bool *done); +static CURLcode tftp_done(struct Curl_easy *data, + CURLcode, bool premature); +static CURLcode tftp_setup_connection(struct Curl_easy *data, + struct connectdata *conn); +static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done); +static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done); +static int tftp_getsock(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t *socks); +static CURLcode tftp_translate_code(tftp_error_t error); + + +/* + * TFTP protocol handler. + */ + +const struct Curl_handler Curl_handler_tftp = { + "tftp", /* scheme */ + tftp_setup_connection, /* setup_connection */ + tftp_do, /* do_it */ + tftp_done, /* done */ + ZERO_NULL, /* do_more */ + tftp_connect, /* connect_it */ + tftp_multi_statemach, /* connecting */ + tftp_doing, /* doing */ + tftp_getsock, /* proto_getsock */ + tftp_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + tftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_TFTP, /* defport */ + CURLPROTO_TFTP, /* protocol */ + CURLPROTO_TFTP, /* family */ + PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */ +}; + +/********************************************************** + * + * tftp_set_timeouts - + * + * Set timeouts based on state machine state. + * Use user provided connect timeouts until DATA or ACK + * packet is received, then use user-provided transfer timeouts + * + * + **********************************************************/ +static CURLcode tftp_set_timeouts(struct tftp_state_data *state) +{ + time_t maxtime, timeout; + timediff_t timeout_ms; + bool start = (state->state == TFTP_STATE_START) ? TRUE : FALSE; + + /* Compute drop-dead time */ + timeout_ms = Curl_timeleft(state->data, NULL, start); + + if(timeout_ms < 0) { + /* time-out, bail out, go home */ + failf(state->data, "Connection time-out"); + return CURLE_OPERATION_TIMEDOUT; + } + + if(timeout_ms > 0) + maxtime = (time_t)(timeout_ms + 500) / 1000; + else + maxtime = 3600; /* use for calculating block timeouts */ + + /* Set per-block timeout to total */ + timeout = maxtime; + + /* Average reposting an ACK after 5 seconds */ + state->retry_max = (int)timeout/5; + + /* But bound the total number */ + if(state->retry_max<3) + state->retry_max = 3; + + if(state->retry_max>50) + state->retry_max = 50; + + /* Compute the re-ACK interval to suit the timeout */ + state->retry_time = (int)(timeout/state->retry_max); + if(state->retry_time<1) + state->retry_time = 1; + + infof(state->data, + "set timeouts for state %d; Total % " CURL_FORMAT_CURL_OFF_T + ", retry %d maxtry %d", + (int)state->state, timeout_ms, state->retry_time, state->retry_max); + + /* init RX time */ + state->rx_time = time(NULL); + + return CURLE_OK; +} + +/********************************************************** + * + * tftp_set_send_first + * + * Event handler for the START state + * + **********************************************************/ + +static void setpacketevent(struct tftp_packet *packet, unsigned short num) +{ + packet->data[0] = (unsigned char)(num >> 8); + packet->data[1] = (unsigned char)(num & 0xff); +} + + +static void setpacketblock(struct tftp_packet *packet, unsigned short num) +{ + packet->data[2] = (unsigned char)(num >> 8); + packet->data[3] = (unsigned char)(num & 0xff); +} + +static unsigned short getrpacketevent(const struct tftp_packet *packet) +{ + return (unsigned short)((packet->data[0] << 8) | packet->data[1]); +} + +static unsigned short getrpacketblock(const struct tftp_packet *packet) +{ + return (unsigned short)((packet->data[2] << 8) | packet->data[3]); +} + +static size_t tftp_strnlen(const char *string, size_t maxlen) +{ + const char *end = memchr(string, '\0', maxlen); + return end ? (size_t) (end - string) : maxlen; +} + +static const char *tftp_option_get(const char *buf, size_t len, + const char **option, const char **value) +{ + size_t loc; + + loc = tftp_strnlen(buf, len); + loc++; /* NULL term */ + + if(loc >= len) + return NULL; + *option = buf; + + loc += tftp_strnlen(buf + loc, len-loc); + loc++; /* NULL term */ + + if(loc > len) + return NULL; + *value = &buf[strlen(*option) + 1]; + + return &buf[loc]; +} + +static CURLcode tftp_parse_option_ack(struct tftp_state_data *state, + const char *ptr, int len) +{ + const char *tmp = ptr; + struct Curl_easy *data = state->data; + + /* if OACK does not contain blksize option, the default (512) must be used */ + state->blksize = TFTP_BLKSIZE_DEFAULT; + + while(tmp < ptr + len) { + const char *option, *value; + + tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value); + if(!tmp) { + failf(data, "Malformed ACK packet, rejecting"); + return CURLE_TFTP_ILLEGAL; + } + + infof(data, "got option=(%s) value=(%s)", option, value); + + if(checkprefix(TFTP_OPTION_BLKSIZE, option)) { + long blksize; + + blksize = strtol(value, NULL, 10); + + if(!blksize) { + failf(data, "invalid blocksize value in OACK packet"); + return CURLE_TFTP_ILLEGAL; + } + if(blksize > TFTP_BLKSIZE_MAX) { + failf(data, "%s (%d)", "blksize is larger than max supported", + TFTP_BLKSIZE_MAX); + return CURLE_TFTP_ILLEGAL; + } + else if(blksize < TFTP_BLKSIZE_MIN) { + failf(data, "%s (%d)", "blksize is smaller than min supported", + TFTP_BLKSIZE_MIN); + return CURLE_TFTP_ILLEGAL; + } + else if(blksize > state->requested_blksize) { + /* could realloc pkt buffers here, but the spec does not call out + * support for the server requesting a bigger blksize than the client + * requests */ + failf(data, "%s (%ld)", + "server requested blksize larger than allocated", blksize); + return CURLE_TFTP_ILLEGAL; + } + + state->blksize = (int)blksize; + infof(data, "%s (%d) %s (%d)", "blksize parsed from OACK", + state->blksize, "requested", state->requested_blksize); + } + else if(checkprefix(TFTP_OPTION_TSIZE, option)) { + long tsize = 0; + + tsize = strtol(value, NULL, 10); + infof(data, "%s (%ld)", "tsize parsed from OACK", tsize); + + /* tsize should be ignored on upload: Who cares about the size of the + remote file? */ + if(!data->state.upload) { + if(!tsize) { + failf(data, "invalid tsize -:%s:- value in OACK packet", value); + return CURLE_TFTP_ILLEGAL; + } + Curl_pgrsSetDownloadSize(data, tsize); + } + } + } + + return CURLE_OK; +} + +static CURLcode tftp_option_add(struct tftp_state_data *state, size_t *csize, + char *buf, const char *option) +{ + if(( strlen(option) + *csize + 1) > (size_t)state->blksize) + return CURLE_TFTP_ILLEGAL; + strcpy(buf, option); + *csize += strlen(option) + 1; + return CURLE_OK; +} + +static CURLcode tftp_connect_for_tx(struct tftp_state_data *state, + tftp_event_t event) +{ + CURLcode result; +#ifndef CURL_DISABLE_VERBOSE_STRINGS + struct Curl_easy *data = state->data; + + infof(data, "%s", "Connected for transmit"); +#endif + state->state = TFTP_STATE_TX; + result = tftp_set_timeouts(state); + if(result) + return result; + return tftp_tx(state, event); +} + +static CURLcode tftp_connect_for_rx(struct tftp_state_data *state, + tftp_event_t event) +{ + CURLcode result; +#ifndef CURL_DISABLE_VERBOSE_STRINGS + struct Curl_easy *data = state->data; + + infof(data, "%s", "Connected for receive"); +#endif + state->state = TFTP_STATE_RX; + result = tftp_set_timeouts(state); + if(result) + return result; + return tftp_rx(state, event); +} + +static CURLcode tftp_send_first(struct tftp_state_data *state, + tftp_event_t event) +{ + size_t sbytes; + ssize_t senddata; + const char *mode = "octet"; + char *filename; + struct Curl_easy *data = state->data; + CURLcode result = CURLE_OK; + + /* Set ascii mode if -B flag was used */ + if(data->state.prefer_ascii) + mode = "netascii"; + + switch(event) { + + case TFTP_EVENT_INIT: /* Send the first packet out */ + case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */ + /* Increment the retry counter, quit if over the limit */ + state->retries++; + if(state->retries>state->retry_max) { + state->error = TFTP_ERR_NORESPONSE; + state->state = TFTP_STATE_FIN; + return result; + } + + if(data->state.upload) { + /* If we are uploading, send an WRQ */ + setpacketevent(&state->spacket, TFTP_EVENT_WRQ); + if(data->state.infilesize != -1) + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + else { + /* If we are downloading, send an RRQ */ + setpacketevent(&state->spacket, TFTP_EVENT_RRQ); + } + /* As RFC3617 describes the separator slash is not actually part of the + filename so we skip the always-present first letter of the path + string. */ + result = Curl_urldecode(&state->data->state.up.path[1], 0, + &filename, NULL, REJECT_ZERO); + if(result) + return result; + + if(strlen(filename) > (state->blksize - strlen(mode) - 4)) { + failf(data, "TFTP filename too long"); + free(filename); + return CURLE_TFTP_ILLEGAL; /* too long filename field */ + } + + msnprintf((char *)state->spacket.data + 2, + state->blksize, + "%s%c%s%c", filename, '\0', mode, '\0'); + sbytes = 4 + strlen(filename) + strlen(mode); + + /* optional addition of TFTP options */ + if(!data->set.tftp_no_options) { + char buf[64]; + /* add tsize option */ + if(data->state.upload && (data->state.infilesize != -1)) + msnprintf(buf, sizeof(buf), "%" CURL_FORMAT_CURL_OFF_T, + data->state.infilesize); + else + strcpy(buf, "0"); /* the destination is large enough */ + + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, + TFTP_OPTION_TSIZE); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, buf); + + /* add blksize option */ + msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, + TFTP_OPTION_BLKSIZE); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, buf); + + /* add timeout option */ + msnprintf(buf, sizeof(buf), "%d", state->retry_time); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, + TFTP_OPTION_INTERVAL); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, buf); + + if(result != CURLE_OK) { + failf(data, "TFTP buffer too small for options"); + free(filename); + return CURLE_TFTP_ILLEGAL; + } + } + + /* the typecase for the 3rd argument is mostly for systems that do + not have a size_t argument, like older unixes that want an 'int' */ + senddata = sendto(state->sockfd, (void *)state->spacket.data, + (SEND_TYPE_ARG3)sbytes, 0, + &data->conn->remote_addr->sa_addr, + (curl_socklen_t)data->conn->remote_addr->addrlen); + if(senddata != (ssize_t)sbytes) { + char buffer[STRERROR_LEN]; + failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + } + free(filename); + break; + + case TFTP_EVENT_OACK: + if(data->state.upload) { + result = tftp_connect_for_tx(state, event); + } + else { + result = tftp_connect_for_rx(state, event); + } + break; + + case TFTP_EVENT_ACK: /* Connected for transmit */ + result = tftp_connect_for_tx(state, event); + break; + + case TFTP_EVENT_DATA: /* Connected for receive */ + result = tftp_connect_for_rx(state, event); + break; + + case TFTP_EVENT_ERROR: + state->state = TFTP_STATE_FIN; + break; + + default: + failf(state->data, "tftp_send_first: internal error"); + break; + } + + return result; +} + +/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit + boundary */ +#define NEXT_BLOCKNUM(x) (((x) + 1)&0xffff) + +/********************************************************** + * + * tftp_rx + * + * Event handler for the RX state + * + **********************************************************/ +static CURLcode tftp_rx(struct tftp_state_data *state, + tftp_event_t event) +{ + ssize_t sbytes; + int rblock; + struct Curl_easy *data = state->data; + char buffer[STRERROR_LEN]; + + switch(event) { + + case TFTP_EVENT_DATA: + /* Is this the block we expect? */ + rblock = getrpacketblock(&state->rpacket); + if(NEXT_BLOCKNUM(state->block) == rblock) { + /* This is the expected block. Reset counters and ACK it. */ + state->retries = 0; + } + else if(state->block == rblock) { + /* This is the last recently received block again. Log it and ACK it + again. */ + infof(data, "Received last DATA packet block %d again.", rblock); + } + else { + /* totally unexpected, just log it */ + infof(data, + "Received unexpected DATA packet block %d, expecting block %d", + rblock, NEXT_BLOCKNUM(state->block)); + break; + } + + /* ACK this block. */ + state->block = (unsigned short)rblock; + setpacketevent(&state->spacket, TFTP_EVENT_ACK); + setpacketblock(&state->spacket, state->block); + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + if(sbytes < 0) { + failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + + /* Check if completed (That is, a less than full packet is received) */ + if(state->rbytes < (ssize_t)state->blksize + 4) { + state->state = TFTP_STATE_FIN; + } + else { + state->state = TFTP_STATE_RX; + } + state->rx_time = time(NULL); + break; + + case TFTP_EVENT_OACK: + /* ACK option acknowledgement so we can move on to data */ + state->block = 0; + state->retries = 0; + setpacketevent(&state->spacket, TFTP_EVENT_ACK); + setpacketblock(&state->spacket, state->block); + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + if(sbytes < 0) { + failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + + /* we are ready to RX data */ + state->state = TFTP_STATE_RX; + state->rx_time = time(NULL); + break; + + case TFTP_EVENT_TIMEOUT: + /* Increment the retry count and fail if over the limit */ + state->retries++; + infof(data, + "Timeout waiting for block %d ACK. Retries = %d", + NEXT_BLOCKNUM(state->block), state->retries); + if(state->retries > state->retry_max) { + state->error = TFTP_ERR_TIMEOUT; + state->state = TFTP_STATE_FIN; + } + else { + /* Resend the previous ACK */ + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + if(sbytes<0) { + failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + } + break; + + case TFTP_EVENT_ERROR: + setpacketevent(&state->spacket, TFTP_EVENT_ERROR); + setpacketblock(&state->spacket, state->block); + (void)sendto(state->sockfd, (void *)state->spacket.data, + 4, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* do not bother with the return code, but if the socket is still up we + * should be a good TFTP client and let the server know we are done */ + state->state = TFTP_STATE_FIN; + break; + + default: + failf(data, "%s", "tftp_rx: internal error"); + return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for + this */ + } + return CURLE_OK; +} + +/********************************************************** + * + * tftp_tx + * + * Event handler for the TX state + * + **********************************************************/ +static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event) +{ + struct Curl_easy *data = state->data; + ssize_t sbytes; + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + size_t cb; /* Bytes currently read */ + char buffer[STRERROR_LEN]; + char *bufptr; + bool eos; + + switch(event) { + + case TFTP_EVENT_ACK: + case TFTP_EVENT_OACK: + if(event == TFTP_EVENT_ACK) { + /* Ack the packet */ + int rblock = getrpacketblock(&state->rpacket); + + if(rblock != state->block && + /* There is a bug in tftpd-hpa that causes it to send us an ack for + * 65535 when the block number wraps to 0. So when we are expecting + * 0, also accept 65535. See + * https://www.syslinux.org/archives/2010-September/015612.html + * */ + !(state->block == 0 && rblock == 65535)) { + /* This is not the expected block. Log it and up the retry counter */ + infof(data, "Received ACK for block %d, expecting %d", + rblock, state->block); + state->retries++; + /* Bail out if over the maximum */ + if(state->retries>state->retry_max) { + failf(data, "tftp_tx: giving up waiting for block %d ack", + state->block); + result = CURLE_SEND_ERROR; + } + else { + /* Re-send the data packet */ + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* Check all sbytes were sent */ + if(sbytes<0) { + failf(data, "%s", Curl_strerror(SOCKERRNO, + buffer, sizeof(buffer))); + result = CURLE_SEND_ERROR; + } + } + + return result; + } + /* This is the expected packet. Reset the counters and send the next + block */ + state->rx_time = time(NULL); + state->block++; + } + else + state->block = 1; /* first data block is 1 when using OACK */ + + state->retries = 0; + setpacketevent(&state->spacket, TFTP_EVENT_DATA); + setpacketblock(&state->spacket, state->block); + if(state->block > 1 && state->sbytes < state->blksize) { + state->state = TFTP_STATE_FIN; + return CURLE_OK; + } + + /* TFTP considers data block size < 512 bytes as an end of session. So + * in some cases we must wait for additional data to build full (512 bytes) + * data block. + * */ + state->sbytes = 0; + bufptr = (char *)state->spacket.data + 4; + do { + result = Curl_client_read(data, bufptr, state->blksize - state->sbytes, + &cb, &eos); + if(result) + return result; + state->sbytes += (int)cb; + bufptr += cb; + } while(state->sbytes < state->blksize && cb); + + sbytes = sendto(state->sockfd, (void *) state->spacket.data, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* Check all sbytes were sent */ + if(sbytes<0) { + failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + /* Update the progress meter */ + k->writebytecount += state->sbytes; + Curl_pgrsSetUploadCounter(data, k->writebytecount); + break; + + case TFTP_EVENT_TIMEOUT: + /* Increment the retry counter and log the timeout */ + state->retries++; + infof(data, "Timeout waiting for block %d ACK. " + " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries); + /* Decide if we have had enough */ + if(state->retries > state->retry_max) { + state->error = TFTP_ERR_TIMEOUT; + state->state = TFTP_STATE_FIN; + } + else { + /* Re-send the data packet */ + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* Check all sbytes were sent */ + if(sbytes<0) { + failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + /* since this was a re-send, we remain at the still byte position */ + Curl_pgrsSetUploadCounter(data, k->writebytecount); + } + break; + + case TFTP_EVENT_ERROR: + state->state = TFTP_STATE_FIN; + setpacketevent(&state->spacket, TFTP_EVENT_ERROR); + setpacketblock(&state->spacket, state->block); + (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* do not bother with the return code, but if the socket is still up we + * should be a good TFTP client and let the server know we are done */ + state->state = TFTP_STATE_FIN; + break; + + default: + failf(data, "tftp_tx: internal error, event: %i", (int)(event)); + break; + } + + return result; +} + +/********************************************************** + * + * tftp_translate_code + * + * Translate internal error codes to CURL error codes + * + **********************************************************/ +static CURLcode tftp_translate_code(tftp_error_t error) +{ + CURLcode result = CURLE_OK; + + if(error != TFTP_ERR_NONE) { + switch(error) { + case TFTP_ERR_NOTFOUND: + result = CURLE_TFTP_NOTFOUND; + break; + case TFTP_ERR_PERM: + result = CURLE_TFTP_PERM; + break; + case TFTP_ERR_DISKFULL: + result = CURLE_REMOTE_DISK_FULL; + break; + case TFTP_ERR_UNDEF: + case TFTP_ERR_ILLEGAL: + result = CURLE_TFTP_ILLEGAL; + break; + case TFTP_ERR_UNKNOWNID: + result = CURLE_TFTP_UNKNOWNID; + break; + case TFTP_ERR_EXISTS: + result = CURLE_REMOTE_FILE_EXISTS; + break; + case TFTP_ERR_NOSUCHUSER: + result = CURLE_TFTP_NOSUCHUSER; + break; + case TFTP_ERR_TIMEOUT: + result = CURLE_OPERATION_TIMEDOUT; + break; + case TFTP_ERR_NORESPONSE: + result = CURLE_COULDNT_CONNECT; + break; + default: + result = CURLE_ABORTED_BY_CALLBACK; + break; + } + } + else + result = CURLE_OK; + + return result; +} + +/********************************************************** + * + * tftp_state_machine + * + * The tftp state machine event dispatcher + * + **********************************************************/ +static CURLcode tftp_state_machine(struct tftp_state_data *state, + tftp_event_t event) +{ + CURLcode result = CURLE_OK; + struct Curl_easy *data = state->data; + + switch(state->state) { + case TFTP_STATE_START: + DEBUGF(infof(data, "TFTP_STATE_START")); + result = tftp_send_first(state, event); + break; + case TFTP_STATE_RX: + DEBUGF(infof(data, "TFTP_STATE_RX")); + result = tftp_rx(state, event); + break; + case TFTP_STATE_TX: + DEBUGF(infof(data, "TFTP_STATE_TX")); + result = tftp_tx(state, event); + break; + case TFTP_STATE_FIN: + infof(data, "%s", "TFTP finished"); + break; + default: + DEBUGF(infof(data, "STATE: %d", state->state)); + failf(data, "%s", "Internal state machine error"); + result = CURLE_TFTP_ILLEGAL; + break; + } + + return result; +} + +/********************************************************** + * + * tftp_disconnect + * + * The disconnect callback + * + **********************************************************/ +static CURLcode tftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) +{ + struct tftp_state_data *state = conn->proto.tftpc; + (void) data; + (void) dead_connection; + + /* done, free dynamically allocated pkt buffers */ + if(state) { + Curl_safefree(state->rpacket.data); + Curl_safefree(state->spacket.data); + free(state); + } + + return CURLE_OK; +} + +/********************************************************** + * + * tftp_connect + * + * The connect callback + * + **********************************************************/ +static CURLcode tftp_connect(struct Curl_easy *data, bool *done) +{ + struct tftp_state_data *state; + int blksize; + int need_blksize; + struct connectdata *conn = data->conn; + + blksize = TFTP_BLKSIZE_DEFAULT; + + state = conn->proto.tftpc = calloc(1, sizeof(struct tftp_state_data)); + if(!state) + return CURLE_OUT_OF_MEMORY; + + /* alloc pkt buffers based on specified blksize */ + if(data->set.tftp_blksize) + /* range checked when set */ + blksize = (int)data->set.tftp_blksize; + + need_blksize = blksize; + /* default size is the fallback when no OACK is received */ + if(need_blksize < TFTP_BLKSIZE_DEFAULT) + need_blksize = TFTP_BLKSIZE_DEFAULT; + + if(!state->rpacket.data) { + state->rpacket.data = calloc(1, need_blksize + 2 + 2); + + if(!state->rpacket.data) + return CURLE_OUT_OF_MEMORY; + } + + if(!state->spacket.data) { + state->spacket.data = calloc(1, need_blksize + 2 + 2); + + if(!state->spacket.data) + return CURLE_OUT_OF_MEMORY; + } + + /* we do not keep TFTP connections up basically because there is none or very + * little gain for UDP */ + connclose(conn, "TFTP"); + + state->data = data; + state->sockfd = conn->sock[FIRSTSOCKET]; + state->state = TFTP_STATE_START; + state->error = TFTP_ERR_NONE; + state->blksize = TFTP_BLKSIZE_DEFAULT; /* Unless updated by OACK response */ + state->requested_blksize = blksize; + + ((struct sockaddr *)&state->local_addr)->sa_family = + (CURL_SA_FAMILY_T)(conn->remote_addr->family); + + tftp_set_timeouts(state); + + if(!conn->bits.bound) { + /* If not already bound, bind to any interface, random UDP port. If it is + * reused or a custom local port was desired, this has already been done! + * + * We once used the size of the local_addr struct as the third argument + * for bind() to better work with IPv6 or whatever size the struct could + * have, but we learned that at least Tru64, AIX and IRIX *requires* the + * size of that argument to match the exact size of a 'sockaddr_in' struct + * when running IPv4-only. + * + * Therefore we use the size from the address we connected to, which we + * assume uses the same IP version and thus hopefully this works for both + * IPv4 and IPv6... + */ + int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr, + (curl_socklen_t)conn->remote_addr->addrlen); + if(rc) { + char buffer[STRERROR_LEN]; + failf(data, "bind() failed; %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_COULDNT_CONNECT; + } + conn->bits.bound = TRUE; + } + + Curl_pgrsStartNow(data); + + *done = TRUE; + + return CURLE_OK; +} + +/********************************************************** + * + * tftp_done + * + * The done callback + * + **********************************************************/ +static CURLcode tftp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct tftp_state_data *state = conn->proto.tftpc; + + (void)status; /* unused */ + (void)premature; /* not used */ + + if(Curl_pgrsDone(data)) + return CURLE_ABORTED_BY_CALLBACK; + + /* If we have encountered an error */ + if(state) + result = tftp_translate_code(state->error); + + return result; +} + +/********************************************************** + * + * tftp_getsock + * + * The getsock callback + * + **********************************************************/ +static int tftp_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks) +{ + (void)data; + socks[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_READSOCK(0); +} + +/********************************************************** + * + * tftp_receive_packet + * + * Called once select fires and data is ready on the socket + * + **********************************************************/ +static CURLcode tftp_receive_packet(struct Curl_easy *data) +{ + struct Curl_sockaddr_storage fromaddr; + curl_socklen_t fromlen; + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct tftp_state_data *state = conn->proto.tftpc; + + /* Receive the packet */ + fromlen = sizeof(fromaddr); + state->rbytes = (int)recvfrom(state->sockfd, + (void *)state->rpacket.data, + (RECV_TYPE_ARG3)state->blksize + 4, + 0, + (struct sockaddr *)&fromaddr, + &fromlen); + if(state->remote_addrlen == 0) { + memcpy(&state->remote_addr, &fromaddr, fromlen); + state->remote_addrlen = fromlen; + } + + /* Sanity check packet length */ + if(state->rbytes < 4) { + failf(data, "Received too short packet"); + /* Not a timeout, but how best to handle it? */ + state->event = TFTP_EVENT_TIMEOUT; + } + else { + /* The event is given by the TFTP packet time */ + unsigned short event = getrpacketevent(&state->rpacket); + state->event = (tftp_event_t)event; + + switch(state->event) { + case TFTP_EVENT_DATA: + /* Do not pass to the client empty or retransmitted packets */ + if(state->rbytes > 4 && + (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) { + result = Curl_client_write(data, CLIENTWRITE_BODY, + (char *)state->rpacket.data + 4, + state->rbytes-4); + if(result) { + tftp_state_machine(state, TFTP_EVENT_ERROR); + return result; + } + } + break; + case TFTP_EVENT_ERROR: + { + unsigned short error = getrpacketblock(&state->rpacket); + char *str = (char *)state->rpacket.data + 4; + size_t strn = state->rbytes - 4; + state->error = (tftp_error_t)error; + if(tftp_strnlen(str, strn) < strn) + infof(data, "TFTP error: %s", str); + break; + } + case TFTP_EVENT_ACK: + break; + case TFTP_EVENT_OACK: + result = tftp_parse_option_ack(state, + (const char *)state->rpacket.data + 2, + state->rbytes-2); + if(result) + return result; + break; + case TFTP_EVENT_RRQ: + case TFTP_EVENT_WRQ: + default: + failf(data, "%s", "Internal error: Unexpected packet"); + break; + } + + /* Update the progress meter */ + if(Curl_pgrsUpdate(data)) { + tftp_state_machine(state, TFTP_EVENT_ERROR); + return CURLE_ABORTED_BY_CALLBACK; + } + } + return result; +} + +/********************************************************** + * + * tftp_state_timeout + * + * Check if timeouts have been reached + * + **********************************************************/ +static timediff_t tftp_state_timeout(struct Curl_easy *data, + tftp_event_t *event) +{ + time_t current; + struct connectdata *conn = data->conn; + struct tftp_state_data *state = conn->proto.tftpc; + timediff_t timeout_ms; + + if(event) + *event = TFTP_EVENT_NONE; + + timeout_ms = Curl_timeleft(state->data, NULL, + (state->state == TFTP_STATE_START)); + if(timeout_ms < 0) { + state->error = TFTP_ERR_TIMEOUT; + state->state = TFTP_STATE_FIN; + return 0; + } + current = time(NULL); + if(current > state->rx_time + state->retry_time) { + if(event) + *event = TFTP_EVENT_TIMEOUT; + state->rx_time = time(NULL); /* update even though we received nothing */ + } + + return timeout_ms; +} + +/********************************************************** + * + * tftp_multi_statemach + * + * Handle single RX socket event and return + * + **********************************************************/ +static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done) +{ + tftp_event_t event; + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct tftp_state_data *state = conn->proto.tftpc; + timediff_t timeout_ms = tftp_state_timeout(data, &event); + + *done = FALSE; + + if(timeout_ms < 0) { + failf(data, "TFTP response timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + if(event != TFTP_EVENT_NONE) { + result = tftp_state_machine(state, event); + if(result) + return result; + *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; + if(*done) + /* Tell curl we are done */ + Curl_xfer_setup_nop(data); + } + else { + /* no timeouts to handle, check our socket */ + int rc = SOCKET_READABLE(state->sockfd, 0); + + if(rc == -1) { + /* bail out */ + int error = SOCKERRNO; + char buffer[STRERROR_LEN]; + failf(data, "%s", Curl_strerror(error, buffer, sizeof(buffer))); + state->event = TFTP_EVENT_ERROR; + } + else if(rc) { + result = tftp_receive_packet(data); + if(result) + return result; + result = tftp_state_machine(state, state->event); + if(result) + return result; + *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; + if(*done) + /* Tell curl we are done */ + Curl_xfer_setup_nop(data); + } + /* if rc == 0, then select() timed out */ + } + + return result; +} + +/********************************************************** + * + * tftp_doing + * + * Called from multi.c while DOing + * + **********************************************************/ +static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done) +{ + CURLcode result; + result = tftp_multi_statemach(data, dophase_done); + + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + else if(!result) { + /* The multi code does not have this logic for the DOING state so we + provide it for TFTP since it may do the entire transfer in this + state. */ + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, Curl_now()); + } + return result; +} + +/********************************************************** + * + * tftp_perform + * + * Entry point for transfer from tftp_do, starts state mach + * + **********************************************************/ +static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct tftp_state_data *state = conn->proto.tftpc; + + *dophase_done = FALSE; + + result = tftp_state_machine(state, TFTP_EVENT_INIT); + + if((state->state == TFTP_STATE_FIN) || result) + return result; + + tftp_multi_statemach(data, dophase_done); + + if(*dophase_done) + DEBUGF(infof(data, "DO phase is complete")); + + return result; +} + + +/********************************************************** + * + * tftp_do + * + * The do callback + * + * This callback initiates the TFTP transfer + * + **********************************************************/ + +static CURLcode tftp_do(struct Curl_easy *data, bool *done) +{ + struct tftp_state_data *state; + CURLcode result; + struct connectdata *conn = data->conn; + + *done = FALSE; + + if(!conn->proto.tftpc) { + result = tftp_connect(data, done); + if(result) + return result; + } + + state = conn->proto.tftpc; + if(!state) + return CURLE_TFTP_ILLEGAL; + + result = tftp_perform(data, done); + + /* If tftp_perform() returned an error, use that for return code. If it + was OK, see if tftp_translate_code() has an error. */ + if(!result) + /* If we have encountered an internal tftp error, translate it. */ + result = tftp_translate_code(state->error); + + return result; +} + +static CURLcode tftp_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + char *type; + + conn->transport = TRNSPRT_UDP; + + /* TFTP URLs support an extension like ";mode=" that + * we will try to get now! */ + type = strstr(data->state.up.path, ";mode="); + + if(!type) + type = strstr(conn->host.rawalloc, ";mode="); + + if(type) { + char command; + *type = 0; /* it was in the middle of the hostname */ + command = Curl_raw_toupper(type[6]); + + switch(command) { + case 'A': /* ASCII mode */ + case 'N': /* NETASCII mode */ + data->state.prefer_ascii = TRUE; + break; + + case 'O': /* octet mode */ + case 'I': /* binary mode */ + default: + /* switch off ASCII */ + data->state.prefer_ascii = FALSE; + break; + } + } + + return CURLE_OK; +} +#endif diff --git a/third_party/curl/lib/tftp.h b/third_party/curl/lib/tftp.h new file mode 100644 index 0000000000..12404bf6d2 --- /dev/null +++ b/third_party/curl/lib/tftp.h @@ -0,0 +1,33 @@ +#ifndef HEADER_CURL_TFTP_H +#define HEADER_CURL_TFTP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#ifndef CURL_DISABLE_TFTP +extern const struct Curl_handler Curl_handler_tftp; + +#define TFTP_BLKSIZE_MIN 8 +#define TFTP_BLKSIZE_MAX 65464 +#endif + +#endif /* HEADER_CURL_TFTP_H */ diff --git a/third_party/curl/lib/timediff.c b/third_party/curl/lib/timediff.c new file mode 100644 index 0000000000..d0824d1448 --- /dev/null +++ b/third_party/curl/lib/timediff.c @@ -0,0 +1,88 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "timediff.h" + +#include + +/* + * Converts number of milliseconds into a timeval structure. + * + * Return values: + * NULL IF tv is NULL or ms < 0 (eg. no timeout -> blocking select) + * tv with 0 in both fields IF ms == 0 (eg. 0ms timeout -> polling select) + * tv with converted fields IF ms > 0 (eg. >0ms timeout -> waiting select) + */ +struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms) +{ + if(!tv) + return NULL; + + if(ms < 0) + return NULL; + + if(ms > 0) { + timediff_t tv_sec = ms / 1000; + timediff_t tv_usec = (ms % 1000) * 1000; /* max=999999 */ +#ifdef HAVE_SUSECONDS_T +#if TIMEDIFF_T_MAX > TIME_T_MAX + /* tv_sec overflow check in case time_t is signed */ + if(tv_sec > TIME_T_MAX) + tv_sec = TIME_T_MAX; +#endif + tv->tv_sec = (time_t)tv_sec; + tv->tv_usec = (suseconds_t)tv_usec; +#elif defined(_WIN32) /* maybe also others in the future */ +#if TIMEDIFF_T_MAX > LONG_MAX + /* tv_sec overflow check on Windows there we know it is long */ + if(tv_sec > LONG_MAX) + tv_sec = LONG_MAX; +#endif + tv->tv_sec = (long)tv_sec; + tv->tv_usec = (long)tv_usec; +#else +#if TIMEDIFF_T_MAX > INT_MAX + /* tv_sec overflow check in case time_t is signed */ + if(tv_sec > INT_MAX) + tv_sec = INT_MAX; +#endif + tv->tv_sec = (int)tv_sec; + tv->tv_usec = (int)tv_usec; +#endif + } + else { + tv->tv_sec = 0; + tv->tv_usec = 0; + } + + return tv; +} + +/* + * Converts a timeval structure into number of milliseconds. + */ +timediff_t curlx_tvtoms(struct timeval *tv) +{ + return (tv->tv_sec*1000) + (timediff_t)(((double)tv->tv_usec)/1000.0); +} diff --git a/third_party/curl/lib/timediff.h b/third_party/curl/lib/timediff.h new file mode 100644 index 0000000000..1ffa59e77f --- /dev/null +++ b/third_party/curl/lib/timediff.h @@ -0,0 +1,52 @@ +#ifndef HEADER_CURL_TIMEDIFF_H +#define HEADER_CURL_TIMEDIFF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +/* Use a larger type even for 32-bit time_t systems so that we can keep + microsecond accuracy in it */ +typedef curl_off_t timediff_t; +#define CURL_FORMAT_TIMEDIFF_T CURL_FORMAT_CURL_OFF_T + +#define TIMEDIFF_T_MAX CURL_OFF_T_MAX +#define TIMEDIFF_T_MIN CURL_OFF_T_MIN + +/* + * Converts number of milliseconds into a timeval structure. + * + * Return values: + * NULL IF tv is NULL or ms < 0 (eg. no timeout -> blocking select) + * tv with 0 in both fields IF ms == 0 (eg. 0ms timeout -> polling select) + * tv with converted fields IF ms > 0 (eg. >0ms timeout -> waiting select) + */ +struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms); + +/* + * Converts a timeval structure into number of milliseconds. + */ +timediff_t curlx_tvtoms(struct timeval *tv); + +#endif /* HEADER_CURL_TIMEDIFF_H */ diff --git a/third_party/curl/lib/timeval.c b/third_party/curl/lib/timeval.c new file mode 100644 index 0000000000..10dbaf02c1 --- /dev/null +++ b/third_party/curl/lib/timeval.c @@ -0,0 +1,237 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "timeval.h" + +#if defined(_WIN32) + +#include +#include "system_win32.h" + +/* In case of bug fix this function has a counterpart in tool_util.c */ +struct curltime Curl_now(void) +{ + struct curltime now; + if(Curl_isVistaOrGreater) { /* QPC timer might have issues pre-Vista */ + LARGE_INTEGER count; + QueryPerformanceCounter(&count); + now.tv_sec = (time_t)(count.QuadPart / Curl_freq.QuadPart); + now.tv_usec = (int)((count.QuadPart % Curl_freq.QuadPart) * 1000000 / + Curl_freq.QuadPart); + } + else { + /* Disable /analyze warning that GetTickCount64 is preferred */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:28159) +#endif + DWORD milliseconds = GetTickCount(); +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + now.tv_sec = (time_t)(milliseconds / 1000); + now.tv_usec = (int)((milliseconds % 1000) * 1000); + } + return now; +} + +#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) || \ + defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) + +struct curltime Curl_now(void) +{ + /* + ** clock_gettime() is granted to be increased monotonically when the + ** monotonic clock is queried. Time starting point is unspecified, it + ** could be the system start-up time, the Epoch, or something else, + ** in any case the time starting point does not change once that the + ** system has started up. + */ +#ifdef HAVE_GETTIMEOFDAY + struct timeval now; +#endif + struct curltime cnow; + struct timespec tsnow; + + /* + ** clock_gettime() may be defined by Apple's SDK as weak symbol thus + ** code compiles but fails during runtime if clock_gettime() is + ** called on unsupported OS version. + */ +#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ + (HAVE_BUILTIN_AVAILABLE == 1) + bool have_clock_gettime = FALSE; + if(__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) + have_clock_gettime = TRUE; +#endif + +#ifdef HAVE_CLOCK_GETTIME_MONOTONIC_RAW + if( +#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ + (HAVE_BUILTIN_AVAILABLE == 1) + have_clock_gettime && +#endif + (0 == clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow))) { + cnow.tv_sec = tsnow.tv_sec; + cnow.tv_usec = (int)(tsnow.tv_nsec / 1000); + } + else +#endif + + if( +#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ + (HAVE_BUILTIN_AVAILABLE == 1) + have_clock_gettime && +#endif + (0 == clock_gettime(CLOCK_MONOTONIC, &tsnow))) { + cnow.tv_sec = tsnow.tv_sec; + cnow.tv_usec = (int)(tsnow.tv_nsec / 1000); + } + /* + ** Even when the configure process has truly detected monotonic clock + ** availability, it might happen that it is not actually available at + ** runtime. When this occurs simply fallback to other time source. + */ +#ifdef HAVE_GETTIMEOFDAY + else { + (void)gettimeofday(&now, NULL); + cnow.tv_sec = now.tv_sec; + cnow.tv_usec = (int)now.tv_usec; + } +#else + else { + cnow.tv_sec = time(NULL); + cnow.tv_usec = 0; + } +#endif + return cnow; +} + +#elif defined(HAVE_MACH_ABSOLUTE_TIME) + +#include +#include + +struct curltime Curl_now(void) +{ + /* + ** Monotonic timer on Mac OS is provided by mach_absolute_time(), which + ** returns time in Mach "absolute time units," which are platform-dependent. + ** To convert to nanoseconds, one must use conversion factors specified by + ** mach_timebase_info(). + */ + static mach_timebase_info_data_t timebase; + struct curltime cnow; + uint64_t usecs; + + if(0 == timebase.denom) + (void) mach_timebase_info(&timebase); + + usecs = mach_absolute_time(); + usecs *= timebase.numer; + usecs /= timebase.denom; + usecs /= 1000; + + cnow.tv_sec = usecs / 1000000; + cnow.tv_usec = (int)(usecs % 1000000); + + return cnow; +} + +#elif defined(HAVE_GETTIMEOFDAY) + +struct curltime Curl_now(void) +{ + /* + ** gettimeofday() is not granted to be increased monotonically, due to + ** clock drifting and external source time synchronization it can jump + ** forward or backward in time. + */ + struct timeval now; + struct curltime ret; + (void)gettimeofday(&now, NULL); + ret.tv_sec = now.tv_sec; + ret.tv_usec = (int)now.tv_usec; + return ret; +} + +#else + +struct curltime Curl_now(void) +{ + /* + ** time() returns the value of time in seconds since the Epoch. + */ + struct curltime now; + now.tv_sec = time(NULL); + now.tv_usec = 0; + return now; +} + +#endif + +/* + * Returns: time difference in number of milliseconds. For too large diffs it + * returns max value. + * + * @unittest: 1323 + */ +timediff_t Curl_timediff(struct curltime newer, struct curltime older) +{ + timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; + if(diff >= (TIMEDIFF_T_MAX/1000)) + return TIMEDIFF_T_MAX; + else if(diff <= (TIMEDIFF_T_MIN/1000)) + return TIMEDIFF_T_MIN; + return diff * 1000 + (newer.tv_usec-older.tv_usec)/1000; +} + +/* + * Returns: time difference in number of milliseconds, rounded up. + * For too large diffs it returns max value. + */ +timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older) +{ + timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; + if(diff >= (TIMEDIFF_T_MAX/1000)) + return TIMEDIFF_T_MAX; + else if(diff <= (TIMEDIFF_T_MIN/1000)) + return TIMEDIFF_T_MIN; + return diff * 1000 + (newer.tv_usec - older.tv_usec + 999)/1000; +} + +/* + * Returns: time difference in number of microseconds. For too large diffs it + * returns max value. + */ +timediff_t Curl_timediff_us(struct curltime newer, struct curltime older) +{ + timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; + if(diff >= (TIMEDIFF_T_MAX/1000000)) + return TIMEDIFF_T_MAX; + else if(diff <= (TIMEDIFF_T_MIN/1000000)) + return TIMEDIFF_T_MIN; + return diff * 1000000 + newer.tv_usec-older.tv_usec; +} diff --git a/third_party/curl/lib/timeval.h b/third_party/curl/lib/timeval.h new file mode 100644 index 0000000000..33dfb5b10d --- /dev/null +++ b/third_party/curl/lib/timeval.h @@ -0,0 +1,62 @@ +#ifndef HEADER_CURL_TIMEVAL_H +#define HEADER_CURL_TIMEVAL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "timediff.h" + +struct curltime { + time_t tv_sec; /* seconds */ + int tv_usec; /* microseconds */ +}; + +struct curltime Curl_now(void); + +/* + * Make sure that the first argument (newer) is the more recent time and older + * is the older time, as otherwise you get a weird negative time-diff back... + * + * Returns: the time difference in number of milliseconds. + */ +timediff_t Curl_timediff(struct curltime newer, struct curltime older); + +/* + * Make sure that the first argument (newer) is the more recent time and older + * is the older time, as otherwise you get a weird negative time-diff back... + * + * Returns: the time difference in number of milliseconds, rounded up. + */ +timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older); + +/* + * Make sure that the first argument (newer) is the more recent time and older + * is the older time, as otherwise you get a weird negative time-diff back... + * + * Returns: the time difference in number of microseconds. + */ +timediff_t Curl_timediff_us(struct curltime newer, struct curltime older); + +#endif /* HEADER_CURL_TIMEVAL_H */ diff --git a/third_party/curl/lib/transfer.c b/third_party/curl/lib/transfer.c new file mode 100644 index 0000000000..6c95a47196 --- /dev/null +++ b/third_party/curl/lib/transfer.c @@ -0,0 +1,1315 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "strtoofft.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#include + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#elif defined(HAVE_UNISTD_H) +#include +#endif + +#ifndef HAVE_SOCKET +#error "We cannot compile without socket() support!" +#endif + +#include "urldata.h" +#include +#include "netrc.h" + +#include "content_encoding.h" +#include "hostip.h" +#include "cfilters.h" +#include "cw-out.h" +#include "transfer.h" +#include "sendf.h" +#include "speedcheck.h" +#include "progress.h" +#include "http.h" +#include "url.h" +#include "getinfo.h" +#include "vtls/vtls.h" +#include "vquic/vquic.h" +#include "select.h" +#include "multiif.h" +#include "connect.h" +#include "http2.h" +#include "mime.h" +#include "strcase.h" +#include "urlapi-int.h" +#include "hsts.h" +#include "setopt.h" +#include "headers.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_IMAP) +/* + * checkheaders() checks the linked list of custom headers for a + * particular header (prefix). Provide the prefix without colon! + * + * Returns a pointer to the first matching header or NULL if none matched. + */ +char *Curl_checkheaders(const struct Curl_easy *data, + const char *thisheader, + const size_t thislen) +{ + struct curl_slist *head; + DEBUGASSERT(thislen); + DEBUGASSERT(thisheader[thislen-1] != ':'); + + for(head = data->set.headers; head; head = head->next) { + if(strncasecompare(head->data, thisheader, thislen) && + Curl_headersep(head->data[thislen]) ) + return head->data; + } + + return NULL; +} +#endif + +static int data_pending(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + + if(conn->handler->protocol&PROTO_FAMILY_FTP) + return Curl_conn_data_pending(data, SECONDARYSOCKET); + + /* in the case of libssh2, we can never be really sure that we have emptied + its internal buffers so we MUST always try until we get EAGAIN back */ + return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) || + Curl_conn_data_pending(data, FIRSTSOCKET); +} + +/* + * Check to see if CURLOPT_TIMECONDITION was met by comparing the time of the + * remote document with the time provided by CURLOPT_TIMEVAL + */ +bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc) +{ + if((timeofdoc == 0) || (data->set.timevalue == 0)) + return TRUE; + + switch(data->set.timecondition) { + case CURL_TIMECOND_IFMODSINCE: + default: + if(timeofdoc <= data->set.timevalue) { + infof(data, + "The requested document is not new enough"); + data->info.timecond = TRUE; + return FALSE; + } + break; + case CURL_TIMECOND_IFUNMODSINCE: + if(timeofdoc >= data->set.timevalue) { + infof(data, + "The requested document is not old enough"); + data->info.timecond = TRUE; + return FALSE; + } + break; + } + + return TRUE; +} + +static CURLcode xfer_recv_shutdown(struct Curl_easy *data, bool *done) +{ + int sockindex; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + if(data->conn->sockfd == CURL_SOCKET_BAD) + return CURLE_FAILED_INIT; + sockindex = (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET]); + return Curl_conn_shutdown(data, sockindex, done); +} + +static bool xfer_recv_shutdown_started(struct Curl_easy *data) +{ + int sockindex; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + if(data->conn->sockfd == CURL_SOCKET_BAD) + return CURLE_FAILED_INIT; + sockindex = (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET]); + return Curl_shutdown_started(data, sockindex); +} + +/** + * Receive raw response data for the transfer. + * @param data the transfer + * @param buf buffer to keep response data received + * @param blen length of `buf` + * @param eos_reliable if EOS detection in underlying connection is reliable + * @param err error code in case of -1 return + * @return number of bytes read or -1 for error + */ +static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data, + char *buf, size_t blen, + bool eos_reliable, + CURLcode *err) +{ + ssize_t nread; + + DEBUGASSERT(blen > 0); + /* If we are reading BODY data and the connection does NOT handle EOF + * and we know the size of the BODY data, limit the read amount */ + if(!eos_reliable && !data->req.header && data->req.size != -1) { + curl_off_t totalleft = data->req.size - data->req.bytecount; + if(totalleft <= 0) + blen = 0; + else if(totalleft < (curl_off_t)blen) + blen = (size_t)totalleft; + } + else if(xfer_recv_shutdown_started(data)) { + /* we already reveived everything. Do not try more. */ + blen = 0; + } + + if(!blen) { + /* want nothing more */ + *err = CURLE_OK; + nread = 0; + } + else { + *err = Curl_xfer_recv(data, buf, blen, &nread); + } + + if(*err) + return -1; + if(nread == 0) { + if(data->req.shutdown) { + bool done; + *err = xfer_recv_shutdown(data, &done); + if(*err) + return -1; + if(!done) { + *err = CURLE_AGAIN; + return -1; + } + } + DEBUGF(infof(data, "readwrite_data: we are done")); + } + DEBUGASSERT(nread >= 0); + return nread; +} + +/* + * Go ahead and do a read if we have a readable socket or if + * the stream was rewound (in which case we have data in a + * buffer) + */ +static CURLcode readwrite_data(struct Curl_easy *data, + struct SingleRequest *k, + int *didwhat) +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + char *buf, *xfer_buf; + size_t blen, xfer_blen; + int maxloops = 10; + curl_off_t total_received = 0; + bool is_multiplex = FALSE; + + result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen); + if(result) + goto out; + + /* This is where we loop until we have read everything there is to + read or we get a CURLE_AGAIN */ + do { + bool is_eos = FALSE; + size_t bytestoread; + ssize_t nread; + + if(!is_multiplex) { + /* Multiplexed connection have inherent handling of EOF and we do not + * have to carefully restrict the amount we try to read. + * Multiplexed changes only in one direction. */ + is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET); + } + + buf = xfer_buf; + bytestoread = xfer_blen; + + if(bytestoread && data->set.max_recv_speed > 0) { + /* In case of speed limit on receiving: if this loop already got + * data, break out. If not, limit the amount of bytes to receive. + * The overall, timed, speed limiting is done in multi.c */ + if(total_received) + break; + if(data->set.max_recv_speed < (curl_off_t)bytestoread) + bytestoread = (size_t)data->set.max_recv_speed; + } + + nread = Curl_xfer_recv_resp(data, buf, bytestoread, + is_multiplex, &result); + if(nread < 0) { + if(CURLE_AGAIN == result) { + result = CURLE_OK; + break; /* get out of loop */ + } + goto out; /* real error */ + } + + /* We only get a 0-length read on EndOfStream */ + blen = (size_t)nread; + is_eos = (blen == 0); + *didwhat |= KEEP_RECV; + + if(!blen) { + /* if we receive 0 or less here, either the data transfer is done or the + server closed the connection and we bail out from this! */ + if(is_multiplex) + DEBUGF(infof(data, "nread == 0, stream closed, bailing")); + else + DEBUGF(infof(data, "nread <= 0, server closed connection, bailing")); + /* stop receiving and ALL sending as well, including PAUSE and HOLD. + * We might still be paused on receive client writes though, so + * keep those bits around. */ + k->keepon &= ~(KEEP_RECV|KEEP_SENDBITS); + if(k->eos_written) /* already did write this to client, leave */ + break; + } + total_received += blen; + + result = Curl_xfer_write_resp(data, buf, blen, is_eos); + if(result || data->req.done) + goto out; + + /* if we are done, we stop receiving. On multiplexed connections, + * we should read the EOS. Which may arrive as meta data after + * the bytes. Not taking it in might lead to RST of streams. */ + if((!is_multiplex && data->req.download_done) || is_eos) { + data->req.keepon &= ~KEEP_RECV; + } + /* if we are PAUSEd or stopped receiving, leave the loop */ + if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) + break; + + } while(maxloops--); + + if((maxloops <= 0) || data_pending(data)) { + /* did not read until EAGAIN or there is still pending data, mark as + read-again-please */ + data->state.select_bits = CURL_CSELECT_IN; + if((k->keepon & KEEP_SENDBITS) == KEEP_SEND) + data->state.select_bits |= CURL_CSELECT_OUT; + } + + if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) && + (conn->bits.close || is_multiplex)) { + /* When we have read the entire thing and the close bit is set, the server + may now close the connection. If there is now any kind of sending going + on from our side, we need to stop that immediately. */ + infof(data, "we are done reading and this is set to close, stop send"); + k->keepon &= ~KEEP_SEND; /* no writing anymore either */ + k->keepon &= ~KEEP_SEND_PAUSE; /* no pausing anymore either */ + } + +out: + Curl_multi_xfer_buf_release(data, xfer_buf); + if(result) + DEBUGF(infof(data, "readwrite_data() -> %d", result)); + return result; +} + +/* + * Send data to upload to the server, when the socket is writable. + */ +static CURLcode readwrite_upload(struct Curl_easy *data, int *didwhat) +{ + if((data->req.keepon & KEEP_SEND_PAUSE)) + return CURLE_OK; + + /* We should not get here when the sending is already done. It + * probably means that someone set `data-req.keepon |= KEEP_SEND` + * when it should not. */ + DEBUGASSERT(!Curl_req_done_sending(data)); + + if(!Curl_req_done_sending(data)) { + *didwhat |= KEEP_SEND; + return Curl_req_send_more(data); + } + return CURLE_OK; +} + +static int select_bits_paused(struct Curl_easy *data, int select_bits) +{ + /* See issue #11982: we really need to be careful not to progress + * a transfer direction when that direction is paused. Not all parts + * of our state machine are handling PAUSED transfers correctly. So, we + * do not want to go there. + * NOTE: we are only interested in PAUSE, not HOLD. */ + + /* if there is data in a direction not paused, return false */ + if(((select_bits & CURL_CSELECT_IN) && + !(data->req.keepon & KEEP_RECV_PAUSE)) || + ((select_bits & CURL_CSELECT_OUT) && + !(data->req.keepon & KEEP_SEND_PAUSE))) + return FALSE; + + return (data->req.keepon & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)); +} + +/* + * Curl_readwrite() is the low-level function to be called when data is to + * be read and written to/from the connection. + */ +CURLcode Curl_readwrite(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct SingleRequest *k = &data->req; + CURLcode result; + struct curltime now; + int didwhat = 0; + int select_bits; + + if(data->state.select_bits) { + if(select_bits_paused(data, data->state.select_bits)) { + /* leave the bits unchanged, so they'll tell us what to do when + * this transfer gets unpaused. */ + DEBUGF(infof(data, "readwrite, select_bits, early return on PAUSED")); + result = CURLE_OK; + goto out; + } + select_bits = data->state.select_bits; + data->state.select_bits = 0; + } + else { + curl_socket_t fd_read; + curl_socket_t fd_write; + /* only use the proper socket if the *_HOLD bit is not set simultaneously + as then we are in rate limiting state in that transfer direction */ + if((k->keepon & KEEP_RECVBITS) == KEEP_RECV) + fd_read = conn->sockfd; + else + fd_read = CURL_SOCKET_BAD; + + if((k->keepon & KEEP_SENDBITS) == KEEP_SEND) + fd_write = conn->writesockfd; + else + fd_write = CURL_SOCKET_BAD; + + select_bits = Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, 0); + } + + if(select_bits == CURL_CSELECT_ERR) { + failf(data, "select/poll returned error"); + result = CURLE_SEND_ERROR; + goto out; + } + +#ifdef USE_HYPER + if(conn->datastream) { + result = conn->datastream(data, conn, &didwhat, select_bits); + if(result || data->req.done) + goto out; + } + else { +#endif + /* We go ahead and do a read if we have a readable socket or if + the stream was rewound (in which case we have data in a + buffer) */ + if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) { + result = readwrite_data(data, k, &didwhat); + if(result || data->req.done) + goto out; + } + + /* If we still have writing to do, we check if we have a writable socket. */ + if(((k->keepon & KEEP_SEND) && (select_bits & CURL_CSELECT_OUT)) || + (k->keepon & KEEP_SEND_TIMED)) { + /* write */ + + result = readwrite_upload(data, &didwhat); + if(result) + goto out; + } +#ifdef USE_HYPER + } +#endif + + now = Curl_now(); + if(!didwhat) { + result = Curl_conn_ev_data_idle(data); + if(result) + goto out; + } + + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, now); + if(result) + goto out; + + if(k->keepon) { + if(0 > Curl_timeleft(data, &now, FALSE)) { + if(k->size != -1) { + failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" + CURL_FORMAT_CURL_OFF_T " bytes received", + Curl_timediff(now, data->progress.t_startsingle), + k->bytecount, k->size); + } + else { + failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received", + Curl_timediff(now, data->progress.t_startsingle), + k->bytecount); + } + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + } + else { + /* + * The transfer has been performed. Just make some general checks before + * returning. + */ + if(!(data->req.no_body) && (k->size != -1) && + (k->bytecount != k->size) && +#ifdef CURL_DO_LINEEND_CONV + /* Most FTP servers do not adjust their file SIZE response for CRLFs, + so we will check to see if the discrepancy can be explained + by the number of CRLFs we have changed to LFs. + */ + (k->bytecount != (k->size + data->state.crlf_conversions)) && +#endif /* CURL_DO_LINEEND_CONV */ + !k->newurl) { + failf(data, "transfer closed with %" CURL_FORMAT_CURL_OFF_T + " bytes remaining to read", k->size - k->bytecount); + result = CURLE_PARTIAL_FILE; + goto out; + } + if(Curl_pgrsUpdate(data)) { + result = CURLE_ABORTED_BY_CALLBACK; + goto out; + } + } + + /* If there is nothing more to send/recv, the request is done */ + if(0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS))) + data->req.done = TRUE; + +out: + if(result) + DEBUGF(infof(data, "Curl_readwrite() -> %d", result)); + return result; +} + +/* Curl_init_CONNECT() gets called each time the handle switches to CONNECT + which means this gets called once for each subsequent redirect etc */ +void Curl_init_CONNECT(struct Curl_easy *data) +{ + data->state.fread_func = data->set.fread_func_set; + data->state.in = data->set.in_set; + data->state.upload = (data->state.httpreq == HTTPREQ_PUT); +} + +/* + * Curl_pretransfer() is called immediately before a transfer starts, and only + * once for one transfer no matter if it has redirects or do multi-pass + * authentication etc. + */ +CURLcode Curl_pretransfer(struct Curl_easy *data) +{ + CURLcode result; + + if(!data->state.url && !data->set.uh) { + /* we cannot do anything without URL */ + failf(data, "No URL set"); + return CURLE_URL_MALFORMAT; + } + + /* since the URL may have been redirected in a previous use of this handle */ + if(data->state.url_alloc) { + /* the already set URL is allocated, free it first! */ + Curl_safefree(data->state.url); + data->state.url_alloc = FALSE; + } + + if(!data->state.url && data->set.uh) { + CURLUcode uc; + free(data->set.str[STRING_SET_URL]); + uc = curl_url_get(data->set.uh, + CURLUPART_URL, &data->set.str[STRING_SET_URL], 0); + if(uc) { + failf(data, "No URL set"); + return CURLE_URL_MALFORMAT; + } + } + + if(data->set.postfields && data->set.set_resume_from) { + /* we cannot */ + failf(data, "cannot mix POSTFIELDS with RESUME_FROM"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + data->state.prefer_ascii = data->set.prefer_ascii; +#ifdef CURL_LIST_ONLY_PROTOCOL + data->state.list_only = data->set.list_only; +#endif + data->state.httpreq = data->set.method; + data->state.url = data->set.str[STRING_SET_URL]; + + /* Init the SSL session ID cache here. We do it here since we want to do it + after the *_setopt() calls (that could specify the size of the cache) but + before any transfer takes place. */ + result = Curl_ssl_initsessions(data, data->set.general_ssl.max_ssl_sessions); + if(result) + return result; + + data->state.requests = 0; + data->state.followlocation = 0; /* reset the location-follow counter */ + data->state.this_is_a_follow = FALSE; /* reset this */ + data->state.errorbuf = FALSE; /* no error has occurred */ + data->state.httpwant = data->set.httpwant; + data->state.httpversion = 0; + data->state.authproblem = FALSE; + data->state.authhost.want = data->set.httpauth; + data->state.authproxy.want = data->set.proxyauth; + Curl_safefree(data->info.wouldredirect); + Curl_data_priority_clear_state(data); + + if(data->state.httpreq == HTTPREQ_PUT) + data->state.infilesize = data->set.filesize; + else if((data->state.httpreq != HTTPREQ_GET) && + (data->state.httpreq != HTTPREQ_HEAD)) { + data->state.infilesize = data->set.postfieldsize; + if(data->set.postfields && (data->state.infilesize == -1)) + data->state.infilesize = (curl_off_t)strlen(data->set.postfields); + } + else + data->state.infilesize = 0; + + /* If there is a list of cookie files to read, do it now! */ + Curl_cookie_loadfiles(data); + + /* If there is a list of host pairs to deal with */ + if(data->state.resolve) + result = Curl_loadhostpairs(data); + + /* If there is a list of hsts files to read */ + Curl_hsts_loadfiles(data); + + if(!result) { + /* Allow data->set.use_port to set which port to use. This needs to be + * disabled for example when we follow Location: headers to URLs using + * different ports! */ + data->state.allow_port = TRUE; + +#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) + /************************************************************* + * Tell signal handler to ignore SIGPIPE + *************************************************************/ + if(!data->set.no_signal) + data->state.prev_signal = signal(SIGPIPE, SIG_IGN); +#endif + + Curl_initinfo(data); /* reset session-specific information "variables" */ + Curl_pgrsResetTransferSizes(data); + Curl_pgrsStartNow(data); + + /* In case the handle is reused and an authentication method was picked + in the session we need to make sure we only use the one(s) we now + consider to be fine */ + data->state.authhost.picked &= data->state.authhost.want; + data->state.authproxy.picked &= data->state.authproxy.want; + +#ifndef CURL_DISABLE_FTP + data->state.wildcardmatch = data->set.wildcard_enabled; + if(data->state.wildcardmatch) { + struct WildcardData *wc; + if(!data->wildcard) { + data->wildcard = calloc(1, sizeof(struct WildcardData)); + if(!data->wildcard) + return CURLE_OUT_OF_MEMORY; + } + wc = data->wildcard; + if(wc->state < CURLWC_INIT) { + if(wc->ftpwc) + wc->dtor(wc->ftpwc); + Curl_safefree(wc->pattern); + Curl_safefree(wc->path); + result = Curl_wildcard_init(wc); /* init wildcard structures */ + if(result) + return CURLE_OUT_OF_MEMORY; + } + } +#endif + result = Curl_hsts_loadcb(data, data->hsts); + } + + /* + * Set user-agent. Used for HTTP, but since we can attempt to tunnel + * basically anything through an HTTP proxy we cannot limit this based on + * protocol. + */ + if(data->set.str[STRING_USERAGENT]) { + Curl_safefree(data->state.aptr.uagent); + data->state.aptr.uagent = + aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]); + if(!data->state.aptr.uagent) + return CURLE_OUT_OF_MEMORY; + } + + if(!result) + result = Curl_setstropt(&data->state.aptr.user, + data->set.str[STRING_USERNAME]); + if(!result) + result = Curl_setstropt(&data->state.aptr.passwd, + data->set.str[STRING_PASSWORD]); +#ifndef CURL_DISABLE_PROXY + if(!result) + result = Curl_setstropt(&data->state.aptr.proxyuser, + data->set.str[STRING_PROXYUSERNAME]); + if(!result) + result = Curl_setstropt(&data->state.aptr.proxypasswd, + data->set.str[STRING_PROXYPASSWORD]); +#endif + + data->req.headerbytecount = 0; + Curl_headers_cleanup(data); + return result; +} + +/* + * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string + * as given by the remote server and set up the new URL to request. + * + * This function DOES NOT FREE the given url. + */ +CURLcode Curl_follow(struct Curl_easy *data, + char *newurl, /* the Location: string */ + followtype type) /* see transfer.h */ +{ +#ifdef CURL_DISABLE_HTTP + (void)data; + (void)newurl; + (void)type; + /* Location: following will not happen when HTTP is disabled */ + return CURLE_TOO_MANY_REDIRECTS; +#else + + /* Location: redirect */ + bool disallowport = FALSE; + bool reachedmax = FALSE; + CURLUcode uc; + + DEBUGASSERT(type != FOLLOW_NONE); + + if(type != FOLLOW_FAKE) + data->state.requests++; /* count all real follows */ + if(type == FOLLOW_REDIR) { + if((data->set.maxredirs != -1) && + (data->state.followlocation >= data->set.maxredirs)) { + reachedmax = TRUE; + type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected + to URL */ + } + else { + data->state.followlocation++; /* count redirect-followings, including + auth reloads */ + + if(data->set.http_auto_referer) { + CURLU *u; + char *referer = NULL; + + /* We are asked to automatically set the previous URL as the referer + when we get the next URL. We pick the ->url field, which may or may + not be 100% correct */ + + if(data->state.referer_alloc) { + Curl_safefree(data->state.referer); + data->state.referer_alloc = FALSE; + } + + /* Make a copy of the URL without credentials and fragment */ + u = curl_url(); + if(!u) + return CURLE_OUT_OF_MEMORY; + + uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0); + if(!uc) + uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0); + if(!uc) + uc = curl_url_set(u, CURLUPART_USER, NULL, 0); + if(!uc) + uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0); + if(!uc) + uc = curl_url_get(u, CURLUPART_URL, &referer, 0); + + curl_url_cleanup(u); + + if(uc || !referer) + return CURLE_OUT_OF_MEMORY; + + data->state.referer = referer; + data->state.referer_alloc = TRUE; /* yes, free this later */ + } + } + } + + if((type != FOLLOW_RETRY) && + (data->req.httpcode != 401) && (data->req.httpcode != 407) && + Curl_is_absolute_url(newurl, NULL, 0, FALSE)) { + /* If this is not redirect due to a 401 or 407 response and an absolute + URL: do not allow a custom port number */ + disallowport = TRUE; + } + + DEBUGASSERT(data->state.uh); + uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int) + ((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : + ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) | + CURLU_ALLOW_SPACE | + (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); + if(uc) { + if(type != FOLLOW_FAKE) { + failf(data, "The redirect target URL could not be parsed: %s", + curl_url_strerror(uc)); + return Curl_uc_to_curlcode(uc); + } + + /* the URL could not be parsed for some reason, but since this is FAKE + mode, just duplicate the field as-is */ + newurl = strdup(newurl); + if(!newurl) + return CURLE_OUT_OF_MEMORY; + } + else { + uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + + /* Clear auth if this redirects to a different port number or protocol, + unless permitted */ + if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { + char *portnum; + int port; + bool clear = FALSE; + + if(data->set.use_port && data->state.allow_port) + /* a custom port is used */ + port = (int)data->set.use_port; + else { + uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, + CURLU_DEFAULT_PORT); + if(uc) { + free(newurl); + return Curl_uc_to_curlcode(uc); + } + port = atoi(portnum); + free(portnum); + } + if(port != data->info.conn_remote_port) { + infof(data, "Clear auth, redirects to port from %u to %u", + data->info.conn_remote_port, port); + clear = TRUE; + } + else { + char *scheme; + const struct Curl_handler *p; + uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); + if(uc) { + free(newurl); + return Curl_uc_to_curlcode(uc); + } + + p = Curl_get_scheme_handler(scheme); + if(p && (p->protocol != data->info.conn_protocol)) { + infof(data, "Clear auth, redirects scheme from %s to %s", + data->info.conn_scheme, scheme); + clear = TRUE; + } + free(scheme); + } + if(clear) { + Curl_safefree(data->state.aptr.user); + Curl_safefree(data->state.aptr.passwd); + } + } + } + + if(type == FOLLOW_FAKE) { + /* we are only figuring out the new URL if we would have followed locations + but now we are done so we can get out! */ + data->info.wouldredirect = newurl; + + if(reachedmax) { + failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs); + return CURLE_TOO_MANY_REDIRECTS; + } + return CURLE_OK; + } + + if(disallowport) + data->state.allow_port = FALSE; + + if(data->state.url_alloc) + Curl_safefree(data->state.url); + + data->state.url = newurl; + data->state.url_alloc = TRUE; + Curl_req_soft_reset(&data->req, data); + infof(data, "Issue another request to this URL: '%s'", data->state.url); + + /* + * We get here when the HTTP code is 300-399 (and 401). We need to perform + * differently based on exactly what return code there was. + * + * News from 7.10.6: we can also get here on a 401 or 407, in case we act on + * an HTTP (proxy-) authentication scheme other than Basic. + */ + switch(data->info.httpcode) { + /* 401 - Act on a WWW-Authenticate, we keep on moving and do the + Authorization: XXXX header in the HTTP request code snippet */ + /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the + Proxy-Authorization: XXXX header in the HTTP request code snippet */ + /* 300 - Multiple Choices */ + /* 306 - Not used */ + /* 307 - Temporary Redirect */ + default: /* for all above (and the unknown ones) */ + /* Some codes are explicitly mentioned since I have checked RFC2616 and + * they seem to be OK to POST to. + */ + break; + case 301: /* Moved Permanently */ + /* (quote from RFC7231, section 6.4.2) + * + * Note: For historical reasons, a user agent MAY change the request + * method from POST to GET for the subsequent request. If this + * behavior is undesired, the 307 (Temporary Redirect) status code + * can be used instead. + * + * ---- + * + * Many webservers expect this, so these servers often answers to a POST + * request with an error page. To be sure that libcurl gets the page that + * most user agents would get, libcurl has to force GET. + * + * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and + * can be overridden with CURLOPT_POSTREDIR. + */ + if((data->state.httpreq == HTTPREQ_POST + || data->state.httpreq == HTTPREQ_POST_FORM + || data->state.httpreq == HTTPREQ_POST_MIME) + && !(data->set.keep_post & CURL_REDIR_POST_301)) { + infof(data, "Switch from POST to GET"); + data->state.httpreq = HTTPREQ_GET; + Curl_creader_set_rewind(data, FALSE); + } + break; + case 302: /* Found */ + /* (quote from RFC7231, section 6.4.3) + * + * Note: For historical reasons, a user agent MAY change the request + * method from POST to GET for the subsequent request. If this + * behavior is undesired, the 307 (Temporary Redirect) status code + * can be used instead. + * + * ---- + * + * Many webservers expect this, so these servers often answers to a POST + * request with an error page. To be sure that libcurl gets the page that + * most user agents would get, libcurl has to force GET. + * + * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and + * can be overridden with CURLOPT_POSTREDIR. + */ + if((data->state.httpreq == HTTPREQ_POST + || data->state.httpreq == HTTPREQ_POST_FORM + || data->state.httpreq == HTTPREQ_POST_MIME) + && !(data->set.keep_post & CURL_REDIR_POST_302)) { + infof(data, "Switch from POST to GET"); + data->state.httpreq = HTTPREQ_GET; + Curl_creader_set_rewind(data, FALSE); + } + break; + + case 303: /* See Other */ + /* 'See Other' location is not the resource but a substitute for the + * resource. In this case we switch the method to GET/HEAD, unless the + * method is POST and the user specified to keep it as POST. + * https://github.com/curl/curl/issues/5237#issuecomment-614641049 + */ + if(data->state.httpreq != HTTPREQ_GET && + ((data->state.httpreq != HTTPREQ_POST && + data->state.httpreq != HTTPREQ_POST_FORM && + data->state.httpreq != HTTPREQ_POST_MIME) || + !(data->set.keep_post & CURL_REDIR_POST_303))) { + data->state.httpreq = HTTPREQ_GET; + infof(data, "Switch to %s", + data->req.no_body?"HEAD":"GET"); + } + break; + case 304: /* Not Modified */ + /* 304 means we did a conditional request and it was "Not modified". + * We should not get any Location: header in this response! + */ + break; + case 305: /* Use Proxy */ + /* (quote from RFC2616, section 10.3.6): + * "The requested resource MUST be accessed through the proxy given + * by the Location field. The Location field gives the URI of the + * proxy. The recipient is expected to repeat this single request + * via the proxy. 305 responses MUST only be generated by origin + * servers." + */ + break; + } + Curl_pgrsTime(data, TIMER_REDIRECT); + Curl_pgrsResetTransferSizes(data); + + return CURLE_OK; +#endif /* CURL_DISABLE_HTTP */ +} + +/* Returns CURLE_OK *and* sets '*url' if a request retry is wanted. + + NOTE: that the *url is malloc()ed. */ +CURLcode Curl_retry_request(struct Curl_easy *data, char **url) +{ + struct connectdata *conn = data->conn; + bool retry = FALSE; + *url = NULL; + + /* if we are talking upload, we cannot do the checks below, unless the + protocol is HTTP as when uploading over HTTP we will still get a + response */ + if(data->state.upload && + !(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP))) + return CURLE_OK; + + if((data->req.bytecount + data->req.headerbytecount == 0) && + conn->bits.reuse && + (!data->req.no_body || (conn->handler->protocol & PROTO_FAMILY_HTTP)) +#ifndef CURL_DISABLE_RTSP + && (data->set.rtspreq != RTSPREQ_RECEIVE) +#endif + ) + /* We got no data, we attempted to reuse a connection. For HTTP this + can be a retry so we try again regardless if we expected a body. + For other protocols we only try again only if we expected a body. + + This might happen if the connection was left alive when we were + done using it before, but that was closed when we wanted to read from + it again. Bad luck. Retry the same request on a fresh connect! */ + retry = TRUE; + else if(data->state.refused_stream && + (data->req.bytecount + data->req.headerbytecount == 0) ) { + /* This was sent on a refused stream, safe to rerun. A refused stream + error can typically only happen on HTTP/2 level if the stream is safe + to issue again, but the nghttp2 API can deliver the message to other + streams as well, which is why this adds the check the data counters + too. */ + infof(data, "REFUSED_STREAM, retrying a fresh connect"); + data->state.refused_stream = FALSE; /* clear again */ + retry = TRUE; + } + if(retry) { +#define CONN_MAX_RETRIES 5 + if(data->state.retrycount++ >= CONN_MAX_RETRIES) { + failf(data, "Connection died, tried %d times before giving up", + CONN_MAX_RETRIES); + data->state.retrycount = 0; + return CURLE_SEND_ERROR; + } + infof(data, "Connection died, retrying a fresh connect (retry count: %d)", + data->state.retrycount); + *url = strdup(data->state.url); + if(!*url) + return CURLE_OUT_OF_MEMORY; + + connclose(conn, "retry"); /* close this connection */ + conn->bits.retry = TRUE; /* mark this as a connection we are about + to retry. Marking it this way should + prevent i.e HTTP transfers to return + error just because nothing has been + transferred! */ + Curl_creader_set_rewind(data, TRUE); + } + return CURLE_OK; +} + +/* + * xfer_setup() is called to setup basic properties for the transfer. + */ +static void xfer_setup( + struct Curl_easy *data, /* transfer */ + int sockindex, /* socket index to read from or -1 */ + curl_off_t size, /* -1 if unknown at this point */ + bool getheader, /* TRUE if header parsing is wanted */ + int writesockindex, /* socket index to write to, it may very well be + the same we read from. -1 disables */ + bool shutdown /* shutdown connection at transfer end. Only + * supported when sending OR receiving. */ + ) +{ + struct SingleRequest *k = &data->req; + struct connectdata *conn = data->conn; + bool want_send = Curl_req_want_send(data); + + DEBUGASSERT(conn != NULL); + DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); + DEBUGASSERT((writesockindex <= 1) && (writesockindex >= -1)); + DEBUGASSERT(!shutdown || (sockindex == -1) || (writesockindex == -1)); + + if(conn->bits.multiplex || conn->httpversion >= 20 || want_send) { + /* when multiplexing, the read/write sockets need to be the same! */ + conn->sockfd = sockindex == -1 ? + ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) : + conn->sock[sockindex]; + conn->writesockfd = conn->sockfd; + if(want_send) + /* special and very HTTP-specific */ + writesockindex = FIRSTSOCKET; + } + else { + conn->sockfd = sockindex == -1 ? + CURL_SOCKET_BAD : conn->sock[sockindex]; + conn->writesockfd = writesockindex == -1 ? + CURL_SOCKET_BAD:conn->sock[writesockindex]; + } + + k->getheader = getheader; + k->size = size; + k->shutdown = shutdown; + + /* The code sequence below is placed in this function just because all + necessary input is not always known in do_complete() as this function may + be called after that */ + + if(!k->getheader) { + k->header = FALSE; + if(size > 0) + Curl_pgrsSetDownloadSize(data, size); + } + /* we want header and/or body, if neither then do not do this! */ + if(k->getheader || !data->req.no_body) { + + if(sockindex != -1) + k->keepon |= KEEP_RECV; + + if(writesockindex != -1) + k->keepon |= KEEP_SEND; + } /* if(k->getheader || !data->req.no_body) */ + +} + +void Curl_xfer_setup_nop(struct Curl_easy *data) +{ + xfer_setup(data, -1, -1, FALSE, -1, FALSE); +} + +void Curl_xfer_setup1(struct Curl_easy *data, + int send_recv, + curl_off_t recv_size, + bool getheader) +{ + int recv_index = (send_recv & CURL_XFER_RECV)? FIRSTSOCKET : -1; + int send_index = (send_recv & CURL_XFER_SEND)? FIRSTSOCKET : -1; + DEBUGASSERT((recv_index >= 0) || (recv_size == -1)); + xfer_setup(data, recv_index, recv_size, getheader, send_index, FALSE); +} + +void Curl_xfer_setup2(struct Curl_easy *data, + int send_recv, + curl_off_t recv_size, + bool shutdown) +{ + int recv_index = (send_recv & CURL_XFER_RECV)? SECONDARYSOCKET : -1; + int send_index = (send_recv & CURL_XFER_SEND)? SECONDARYSOCKET : -1; + DEBUGASSERT((recv_index >= 0) || (recv_size == -1)); + xfer_setup(data, recv_index, recv_size, FALSE, send_index, shutdown); +} + +CURLcode Curl_xfer_write_resp(struct Curl_easy *data, + const char *buf, size_t blen, + bool is_eos) +{ + CURLcode result = CURLE_OK; + + if(data->conn->handler->write_resp) { + /* protocol handlers offering this function take full responsibility + * for writing all received download data to the client. */ + result = data->conn->handler->write_resp(data, buf, blen, is_eos); + } + else { + /* No special handling by protocol handler, write all received data + * as BODY to the client. */ + if(blen || is_eos) { + int cwtype = CLIENTWRITE_BODY; + if(is_eos) + cwtype |= CLIENTWRITE_EOS; + +#ifndef CURL_DISABLE_POP3 + if(blen && data->conn->handler->protocol & PROTO_FAMILY_POP3) { + result = data->req.ignorebody? CURLE_OK : + Curl_pop3_write(data, buf, blen); + } + else +#endif /* CURL_DISABLE_POP3 */ + result = Curl_client_write(data, cwtype, buf, blen); + } + } + + if(!result && is_eos) { + /* If we wrote the EOS, we are definitely done */ + data->req.eos_written = TRUE; + data->req.download_done = TRUE; + } + CURL_TRC_WRITE(data, "xfer_write_resp(len=%zu, eos=%d) -> %d", + blen, is_eos, result); + return result; +} + +CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data, + const char *hd0, size_t hdlen, bool is_eos) +{ + if(data->conn->handler->write_resp_hd) { + /* protocol handlers offering this function take full responsibility + * for writing all received download data to the client. */ + return data->conn->handler->write_resp_hd(data, hd0, hdlen, is_eos); + } + /* No special handling by protocol handler, write as response bytes */ + return Curl_xfer_write_resp(data, hd0, hdlen, is_eos); +} + +CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature) +{ + (void)premature; + return Curl_cw_out_done(data); +} + +CURLcode Curl_xfer_send(struct Curl_easy *data, + const void *buf, size_t blen, + size_t *pnwritten) +{ + CURLcode result; + int sockindex; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + /* FIXME: would like to enable this, but some protocols (MQTT) do not + * setup the transfer correctly, it seems + if(data->conn->writesockfd == CURL_SOCKET_BAD) { + failf(data, "transfer not setup for sending"); + DEBUGASSERT(0); + return CURLE_SEND_ERROR; + } */ + sockindex = ((data->conn->writesockfd != CURL_SOCKET_BAD) && + (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET])); + result = Curl_conn_send(data, sockindex, buf, blen, pnwritten); + if(result == CURLE_AGAIN) { + result = CURLE_OK; + *pnwritten = 0; + } + else if(!result && *pnwritten) + data->info.request_size += *pnwritten; + + return result; +} + +CURLcode Curl_xfer_recv(struct Curl_easy *data, + char *buf, size_t blen, + ssize_t *pnrcvd) +{ + int sockindex; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + /* FIXME: would like to enable this, but some protocols (MQTT) do not + * setup the transfer correctly, it seems + if(data->conn->sockfd == CURL_SOCKET_BAD) { + failf(data, "transfer not setup for receiving"); + DEBUGASSERT(0); + return CURLE_RECV_ERROR; + } */ + sockindex = ((data->conn->sockfd != CURL_SOCKET_BAD) && + (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET])); + if(data->set.buffer_size > 0 && (size_t)data->set.buffer_size < blen) + blen = (size_t)data->set.buffer_size; + return Curl_conn_recv(data, sockindex, buf, blen, pnrcvd); +} + +CURLcode Curl_xfer_send_close(struct Curl_easy *data) +{ + Curl_conn_ev_data_done_send(data); + return CURLE_OK; +} + +CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done) +{ + int sockindex; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + if(data->conn->writesockfd == CURL_SOCKET_BAD) + return CURLE_FAILED_INIT; + sockindex = (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]); + return Curl_conn_shutdown(data, sockindex, done); +} + +bool Curl_xfer_is_blocked(struct Curl_easy *data) +{ + bool want_send = ((data)->req.keepon & KEEP_SEND); + bool want_recv = ((data)->req.keepon & KEEP_RECV); + if(!want_send) + return (want_recv && Curl_cwriter_is_paused(data)); + else if(!want_recv) + return (want_send && Curl_creader_is_paused(data)); + else + return Curl_creader_is_paused(data) && Curl_cwriter_is_paused(data); +} diff --git a/third_party/curl/lib/transfer.h b/third_party/curl/lib/transfer.h new file mode 100644 index 0000000000..21ad259629 --- /dev/null +++ b/third_party/curl/lib/transfer.h @@ -0,0 +1,144 @@ +#ifndef HEADER_CURL_TRANSFER_H +#define HEADER_CURL_TRANSFER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#define Curl_headersep(x) ((((x)==':') || ((x)==';'))) +char *Curl_checkheaders(const struct Curl_easy *data, + const char *thisheader, + const size_t thislen); + +void Curl_init_CONNECT(struct Curl_easy *data); + +CURLcode Curl_pretransfer(struct Curl_easy *data); + +typedef enum { + FOLLOW_NONE, /* not used within the function, just a placeholder to + allow initing to this */ + FOLLOW_FAKE, /* only records stuff, not actually following */ + FOLLOW_RETRY, /* set if this is a request retry as opposed to a real + redirect following */ + FOLLOW_REDIR /* a full true redirect */ +} followtype; + +CURLcode Curl_follow(struct Curl_easy *data, char *newurl, + followtype type); +CURLcode Curl_readwrite(struct Curl_easy *data); +int Curl_single_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); +CURLcode Curl_retry_request(struct Curl_easy *data, char **url); +bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc); + +/** + * Write the transfer raw response bytes, as received from the connection. + * Will handle all passed bytes or return an error. By default, this will + * write the bytes as BODY to the client. Protocols may provide a + * "write_resp" callback in their handler to add specific treatment. E.g. + * HTTP parses response headers and passes them differently to the client. + * @param data the transfer + * @param buf the raw response bytes + * @param blen the amount of bytes in `buf` + * @param is_eos TRUE iff the connection indicates this to be the last + * bytes of the response + */ +CURLcode Curl_xfer_write_resp(struct Curl_easy *data, + const char *buf, size_t blen, + bool is_eos); + +/** + * Write a single "header" line from a server response. + * @param hd0 the 0-terminated, single header line + * @param hdlen the length of the header line + * @param is_eos TRUE iff this is the end of the response + */ +CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data, + const char *hd0, size_t hdlen, bool is_eos); + +#define CURL_XFER_NOP (0) +#define CURL_XFER_RECV (1<<(0)) +#define CURL_XFER_SEND (1<<(1)) +#define CURL_XFER_SENDRECV (CURL_XFER_RECV|CURL_XFER_SEND) + +/** + * The transfer is neither receiving nor sending now. + */ +void Curl_xfer_setup_nop(struct Curl_easy *data); + +/** + * The transfer will use socket 1 to send/recv. `recv_size` is + * the amount to receive or -1 if unknown. `getheader` indicates + * response header processing is expected. + */ +void Curl_xfer_setup1(struct Curl_easy *data, + int send_recv, + curl_off_t recv_size, + bool getheader); + +/** + * The transfer will use socket 2 to send/recv. `recv_size` is + * the amount to receive or -1 if unknown. With `shutdown` being + * set, the transfer is only allowed to either send OR receive + * and the socket 2 connection will be shutdown at the end of + * the transfer. An unclean shutdown will fail the transfer. + */ +void Curl_xfer_setup2(struct Curl_easy *data, + int send_recv, + curl_off_t recv_size, + bool shutdown); + +/** + * Multi has set transfer to DONE. Last chance to trigger + * missing response things like writing an EOS to the client. + */ +CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature); + +/** + * Send data on the socket/connection filter designated + * for transfer's outgoing data. + * Will return CURLE_OK on blocking with (*pnwritten == 0). + */ +CURLcode Curl_xfer_send(struct Curl_easy *data, + const void *buf, size_t blen, + size_t *pnwritten); + +/** + * Receive data on the socket/connection filter designated + * for transfer's incoming data. + * Will return CURLE_AGAIN on blocking with (*pnrcvd == 0). + */ +CURLcode Curl_xfer_recv(struct Curl_easy *data, + char *buf, size_t blen, + ssize_t *pnrcvd); + +CURLcode Curl_xfer_send_close(struct Curl_easy *data); +CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done); + +/** + * Return TRUE iff the transfer is not done, but further progress + * is blocked. For example when it is only receiving and its writer + * is PAUSED. + */ +bool Curl_xfer_is_blocked(struct Curl_easy *data); + +#endif /* HEADER_CURL_TRANSFER_H */ diff --git a/third_party/curl/lib/url.c b/third_party/curl/lib/url.c new file mode 100644 index 0000000000..914b5b653c --- /dev/null +++ b/third_party/curl/lib/url.c @@ -0,0 +1,3949 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_IPHLPAPI_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef __VMS +#include +#include +#endif + +#ifdef HAVE_SYS_UN_H +#include +#endif + +#ifndef HAVE_SOCKET +#error "We cannot compile without socket() support!" +#endif + +#include + +#include "doh.h" +#include "urldata.h" +#include "netrc.h" +#include "formdata.h" +#include "mime.h" +#include "vtls/vtls.h" +#include "hostip.h" +#include "transfer.h" +#include "sendf.h" +#include "progress.h" +#include "cookie.h" +#include "strcase.h" +#include "strerror.h" +#include "escape.h" +#include "strtok.h" +#include "share.h" +#include "content_encoding.h" +#include "http_digest.h" +#include "http_negotiate.h" +#include "select.h" +#include "multiif.h" +#include "easyif.h" +#include "speedcheck.h" +#include "warnless.h" +#include "getinfo.h" +#include "urlapi-int.h" +#include "system_win32.h" +#include "hsts.h" +#include "noproxy.h" +#include "cfilters.h" +#include "idn.h" + +/* And now for the protocols */ +#include "ftp.h" +#include "dict.h" +#include "telnet.h" +#include "tftp.h" +#include "http.h" +#include "http2.h" +#include "file.h" +#include "curl_ldap.h" +#include "vssh/ssh.h" +#include "imap.h" +#include "url.h" +#include "connect.h" +#include "inet_ntop.h" +#include "http_ntlm.h" +#include "curl_rtmp.h" +#include "gopher.h" +#include "mqtt.h" +#include "http_proxy.h" +#include "conncache.h" +#include "multihandle.h" +#include "strdup.h" +#include "setopt.h" +#include "altsvc.h" +#include "dynbuf.h" +#include "headers.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#ifdef USE_NGHTTP2 +static void data_priority_cleanup(struct Curl_easy *data); +#else +#define data_priority_cleanup(x) +#endif + +/* Some parts of the code (e.g. chunked encoding) assume this buffer has at + * more than just a few bytes to play with. Do not let it become too small or + * bad things will happen. + */ +#if READBUFFER_SIZE < READBUFFER_MIN +# error READBUFFER_SIZE is too small +#endif + +#ifdef USE_UNIX_SOCKETS +#define UNIX_SOCKET_PREFIX "localhost" +#endif + +/* Reject URLs exceeding this length */ +#define MAX_URL_LEN 0xffff + +/* +* get_protocol_family() +* +* This is used to return the protocol family for a given protocol. +* +* Parameters: +* +* 'h' [in] - struct Curl_handler pointer. +* +* Returns the family as a single bit protocol identifier. +*/ +static curl_prot_t get_protocol_family(const struct Curl_handler *h) +{ + DEBUGASSERT(h); + DEBUGASSERT(h->family); + return h->family; +} + +void Curl_freeset(struct Curl_easy *data) +{ + /* Free all dynamic strings stored in the data->set substructure. */ + enum dupstring i; + enum dupblob j; + + for(i = (enum dupstring)0; i < STRING_LAST; i++) { + Curl_safefree(data->set.str[i]); + } + + for(j = (enum dupblob)0; j < BLOB_LAST; j++) { + Curl_safefree(data->set.blobs[j]); + } + + if(data->state.referer_alloc) { + Curl_safefree(data->state.referer); + data->state.referer_alloc = FALSE; + } + data->state.referer = NULL; + if(data->state.url_alloc) { + Curl_safefree(data->state.url); + data->state.url_alloc = FALSE; + } + data->state.url = NULL; + + Curl_mime_cleanpart(&data->set.mimepost); + +#ifndef CURL_DISABLE_COOKIES + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; +#endif +} + +/* free the URL pieces */ +static void up_free(struct Curl_easy *data) +{ + struct urlpieces *up = &data->state.up; + Curl_safefree(up->scheme); + Curl_safefree(up->hostname); + Curl_safefree(up->port); + Curl_safefree(up->user); + Curl_safefree(up->password); + Curl_safefree(up->options); + Curl_safefree(up->path); + Curl_safefree(up->query); + curl_url_cleanup(data->state.uh); + data->state.uh = NULL; +} + +/* + * This is the internal function curl_easy_cleanup() calls. This should + * cleanup and free all resources associated with this sessionhandle. + * + * We ignore SIGPIPE when this is called from curl_easy_cleanup. + */ + +CURLcode Curl_close(struct Curl_easy **datap) +{ + struct Curl_easy *data; + + if(!datap || !*datap) + return CURLE_OK; + + data = *datap; + *datap = NULL; + + Curl_expire_clear(data); /* shut off timers */ + + /* Detach connection if any is left. This should not be normal, but can be + the case for example with CONNECT_ONLY + recv/send (test 556) */ + Curl_detach_connection(data); + if(!data->state.internal) { + if(data->multi) + /* This handle is still part of a multi handle, take care of this first + and detach this handle from there. */ + curl_multi_remove_handle(data->multi, data); + + if(data->multi_easy) { + /* when curl_easy_perform() is used, it creates its own multi handle to + use and this is the one */ + curl_multi_cleanup(data->multi_easy); + data->multi_easy = NULL; + } + } + + data->magic = 0; /* force a clear AFTER the possibly enforced removal from + the multi handle, since that function uses the magic + field! */ + + if(data->state.rangestringalloc) + free(data->state.range); + + /* freed here just in case DONE was not called */ + Curl_req_free(&data->req, data); + + /* Close down all open SSL info and sessions */ + Curl_ssl_close_all(data); + Curl_safefree(data->state.first_host); + Curl_safefree(data->state.scratch); + Curl_ssl_free_certinfo(data); + + if(data->state.referer_alloc) { + Curl_safefree(data->state.referer); + data->state.referer_alloc = FALSE; + } + data->state.referer = NULL; + + up_free(data); + Curl_dyn_free(&data->state.headerb); + Curl_flush_cookies(data, TRUE); +#ifndef CURL_DISABLE_ALTSVC + Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); + Curl_altsvc_cleanup(&data->asi); +#endif +#ifndef CURL_DISABLE_HSTS + Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]); + if(!data->share || !data->share->hsts) + Curl_hsts_cleanup(&data->hsts); + curl_slist_free_all(data->state.hstslist); /* clean up list */ +#endif +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) + Curl_http_auth_cleanup_digest(data); +#endif + Curl_safefree(data->info.contenttype); + Curl_safefree(data->info.wouldredirect); + + /* this destroys the channel and we cannot use it anymore after this */ + Curl_resolver_cancel(data); + Curl_resolver_cleanup(data->state.async.resolver); + + data_priority_cleanup(data); + + /* No longer a dirty share, if it exists */ + if(data->share) { + Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + data->share->dirty--; + Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); + } + +#ifndef CURL_DISABLE_PROXY + Curl_safefree(data->state.aptr.proxyuserpwd); +#endif + Curl_safefree(data->state.aptr.uagent); + Curl_safefree(data->state.aptr.userpwd); + Curl_safefree(data->state.aptr.accept_encoding); + Curl_safefree(data->state.aptr.te); + Curl_safefree(data->state.aptr.rangeline); + Curl_safefree(data->state.aptr.ref); + Curl_safefree(data->state.aptr.host); +#ifndef CURL_DISABLE_COOKIES + Curl_safefree(data->state.aptr.cookiehost); +#endif +#ifndef CURL_DISABLE_RTSP + Curl_safefree(data->state.aptr.rtsp_transport); +#endif + Curl_safefree(data->state.aptr.user); + Curl_safefree(data->state.aptr.passwd); +#ifndef CURL_DISABLE_PROXY + Curl_safefree(data->state.aptr.proxyuser); + Curl_safefree(data->state.aptr.proxypasswd); +#endif + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) + Curl_mime_cleanpart(data->state.formp); + Curl_safefree(data->state.formp); +#endif + + /* destruct wildcard structures if it is needed */ + Curl_wildcard_dtor(&data->wildcard); + Curl_freeset(data); + Curl_headers_cleanup(data); + free(data); + return CURLE_OK; +} + +/* + * Initialize the UserDefined fields within a Curl_easy. + * This may be safely called on a new or existing Curl_easy. + */ +CURLcode Curl_init_userdefined(struct Curl_easy *data) +{ + struct UserDefined *set = &data->set; + CURLcode result = CURLE_OK; + + set->out = stdout; /* default output to stdout */ + set->in_set = stdin; /* default input from stdin */ + set->err = stderr; /* default stderr to stderr */ + + /* use fwrite as default function to store output */ + set->fwrite_func = (curl_write_callback)fwrite; + + /* use fread as default function to read input */ + set->fread_func_set = (curl_read_callback)fread; + set->is_fread_set = 0; + + set->seek_client = ZERO_NULL; + + set->filesize = -1; /* we do not know the size */ + set->postfieldsize = -1; /* unknown size */ + set->maxredirs = 30; /* sensible default */ + + set->method = HTTPREQ_GET; /* Default HTTP request */ +#ifndef CURL_DISABLE_RTSP + set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */ +#endif +#ifndef CURL_DISABLE_FTP + set->ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ + set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */ + set->ftp_use_pret = FALSE; /* mainly useful for drftpd servers */ + set->ftp_filemethod = FTPFILE_MULTICWD; + set->ftp_skip_ip = TRUE; /* skip PASV IP by default */ +#endif + set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */ + + /* Set the default size of the SSL session ID cache */ + set->general_ssl.max_ssl_sessions = 5; + /* Timeout every 24 hours by default */ + set->general_ssl.ca_cache_timeout = 24 * 60 * 60; + + set->httpauth = CURLAUTH_BASIC; /* defaults to basic */ + +#ifndef CURL_DISABLE_PROXY + set->proxyport = 0; + set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ + set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */ + /* SOCKS5 proxy auth defaults to username/password + GSS-API */ + set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI; +#endif + + /* make libcurl quiet by default: */ + set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */ + + Curl_mime_initpart(&set->mimepost); + + Curl_ssl_easy_config_init(data); +#ifndef CURL_DISABLE_DOH + set->doh_verifyhost = TRUE; + set->doh_verifypeer = TRUE; +#endif +#ifdef USE_SSH + /* defaults to any auth type */ + set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; + set->new_directory_perms = 0755; /* Default permissions */ +#endif + + set->new_file_perms = 0644; /* Default permissions */ + set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL; + set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | + CURLPROTO_FTPS; + +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + /* + * disallow unprotected protection negotiation NEC reference implementation + * seem not to follow rfc1961 section 4.3/4.4 + */ + set->socks5_gssapi_nec = FALSE; +#endif + + /* Set the default CA cert bundle/path detected/specified at build time. + * + * If Schannel or SecureTransport is the selected SSL backend then these + * locations are ignored. We allow setting CA location for schannel and + * securetransport when explicitly specified by the user via + * CURLOPT_CAINFO / --cacert. + */ + if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL && + Curl_ssl_backend() != CURLSSLBACKEND_SECURETRANSPORT) { +#if defined(CURL_CA_BUNDLE) + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); + if(result) + return result; +#ifndef CURL_DISABLE_PROXY + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], + CURL_CA_BUNDLE); + if(result) + return result; +#endif +#endif +#if defined(CURL_CA_PATH) + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH); + if(result) + return result; +#ifndef CURL_DISABLE_PROXY + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH); + if(result) + return result; +#endif +#endif + } + +#ifndef CURL_DISABLE_FTP + set->wildcard_enabled = FALSE; + set->chunk_bgn = ZERO_NULL; + set->chunk_end = ZERO_NULL; + set->fnmatch = ZERO_NULL; +#endif + set->tcp_keepalive = FALSE; + set->tcp_keepintvl = 60; + set->tcp_keepidle = 60; + set->tcp_keepcnt = 9; + set->tcp_fastopen = FALSE; + set->tcp_nodelay = TRUE; + set->ssl_enable_alpn = TRUE; + set->expect_100_timeout = 1000L; /* Wait for a second by default. */ + set->sep_headers = TRUE; /* separated header lists by default */ + set->buffer_size = READBUFFER_SIZE; + set->upload_buffer_size = UPLOADBUFFER_DEFAULT; + set->happy_eyeballs_timeout = CURL_HET_DEFAULT; + set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; + set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ + set->maxage_conn = 118; + set->maxlifetime_conn = 0; + set->http09_allowed = FALSE; +#ifdef USE_HTTP2 + set->httpwant = CURL_HTTP_VERSION_2TLS +#else + set->httpwant = CURL_HTTP_VERSION_1_1 +#endif + ; +#if defined(USE_HTTP2) || defined(USE_HTTP3) + memset(&set->priority, 0, sizeof(set->priority)); +#endif + set->quick_exit = 0L; + return result; +} + +/** + * Curl_open() + * + * @param curl is a pointer to a sessionhandle pointer that gets set by this + * function. + * @return CURLcode + */ + +CURLcode Curl_open(struct Curl_easy **curl) +{ + CURLcode result; + struct Curl_easy *data; + + /* Very simple start-up: alloc the struct, init it with zeroes and return */ + data = calloc(1, sizeof(struct Curl_easy)); + if(!data) { + /* this is a very serious error */ + DEBUGF(fprintf(stderr, "Error: calloc of Curl_easy failed\n")); + return CURLE_OUT_OF_MEMORY; + } + + data->magic = CURLEASY_MAGIC_NUMBER; + + Curl_req_init(&data->req); + + result = Curl_resolver_init(data, &data->state.async.resolver); + if(result) { + DEBUGF(fprintf(stderr, "Error: resolver_init failed\n")); + Curl_req_free(&data->req, data); + free(data); + return result; + } + + result = Curl_init_userdefined(data); + if(!result) { + Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER); + Curl_initinfo(data); + + /* most recent connection is not yet defined */ + data->state.lastconnect_id = -1; + data->state.recent_conn_id = -1; + /* and not assigned an id yet */ + data->id = -1; + + data->progress.flags |= PGRS_HIDE; + data->state.current_speed = -1; /* init to negative == impossible */ + } + + if(result) { + Curl_resolver_cleanup(data->state.async.resolver); + Curl_dyn_free(&data->state.headerb); + Curl_freeset(data); + Curl_req_free(&data->req, data); + free(data); + data = NULL; + } + else + *curl = data; + + return result; +} + +void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) +{ + size_t i; + + DEBUGASSERT(conn); + + for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) { + Curl_conn_cf_discard_all(data, conn, (int)i); + } + + Curl_free_idnconverted_hostname(&conn->host); + Curl_free_idnconverted_hostname(&conn->conn_to_host); +#ifndef CURL_DISABLE_PROXY + Curl_free_idnconverted_hostname(&conn->http_proxy.host); + Curl_free_idnconverted_hostname(&conn->socks_proxy.host); + Curl_safefree(conn->http_proxy.user); + Curl_safefree(conn->socks_proxy.user); + Curl_safefree(conn->http_proxy.passwd); + Curl_safefree(conn->socks_proxy.passwd); + Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ + Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ +#endif + Curl_safefree(conn->user); + Curl_safefree(conn->passwd); + Curl_safefree(conn->sasl_authzid); + Curl_safefree(conn->options); + Curl_safefree(conn->oauth_bearer); + Curl_safefree(conn->host.rawalloc); /* hostname buffer */ + Curl_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ + Curl_safefree(conn->hostname_resolve); + Curl_safefree(conn->secondaryhostname); + Curl_safefree(conn->localdev); + Curl_ssl_conn_config_cleanup(conn); + +#ifdef USE_UNIX_SOCKETS + Curl_safefree(conn->unix_domain_socket); +#endif + + free(conn); /* free all the connection oriented data */ +} + +/* + * Disconnects the given connection. Note the connection may not be the + * primary connection, like when freeing room in the connection cache or + * killing of a dead old connection. + * + * A connection needs an easy handle when closing down. We support this passed + * in separately since the connection to get closed here is often already + * disassociated from an easy handle. + * + * This function MUST NOT reset state in the Curl_easy struct if that + * is not strictly bound to the life-time of *this* particular connection. + */ +void Curl_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool aborted) +{ + /* there must be a connection to close */ + DEBUGASSERT(conn); + + /* it must be removed from the connection cache */ + DEBUGASSERT(!conn->bundle); + + /* there must be an associated transfer */ + DEBUGASSERT(data); + + /* the transfer must be detached from the connection */ + DEBUGASSERT(!data->conn); + + DEBUGF(infof(data, "Curl_disconnect(conn #%" + CURL_FORMAT_CURL_OFF_T ", aborted=%d)", + conn->connection_id, aborted)); + + /* + * If this connection is not marked to force-close, leave it open if there + * are other users of it + */ + if(CONN_INUSE(conn) && !aborted) { + DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn))); + return; + } + + if(conn->dns_entry) { + Curl_resolv_unlock(data, conn->dns_entry); + conn->dns_entry = NULL; + } + + /* Cleanup NTLM connection-related data */ + Curl_http_auth_cleanup_ntlm(conn); + + /* Cleanup NEGOTIATE connection-related data */ + Curl_http_auth_cleanup_negotiate(conn); + + if(conn->connect_only) + /* treat the connection as aborted in CONNECT_ONLY situations */ + aborted = TRUE; + + Curl_conncache_disconnect(data, conn, aborted); +} + +/* + * IsMultiplexingPossible() + * + * Return a bitmask with the available multiplexing options for the given + * requested connection. + */ +static int IsMultiplexingPossible(const struct Curl_easy *handle, + const struct connectdata *conn) +{ + int avail = 0; + + /* If an HTTP protocol and multiplexing is enabled */ + if((conn->handler->protocol & PROTO_FAMILY_HTTP) && + (!conn->bits.protoconnstart || !conn->bits.close)) { + + if(Curl_multiplex_wanted(handle->multi) && + (handle->state.httpwant >= CURL_HTTP_VERSION_2)) + /* allows HTTP/2 */ + avail |= CURLPIPE_MULTIPLEX; + } + return avail; +} + +#ifndef CURL_DISABLE_PROXY +static bool +proxy_info_matches(const struct proxy_info *data, + const struct proxy_info *needle) +{ + if((data->proxytype == needle->proxytype) && + (data->port == needle->port) && + strcasecompare(data->host.name, needle->host.name)) + return TRUE; + + return FALSE; +} + +static bool +socks_proxy_info_matches(const struct proxy_info *data, + const struct proxy_info *needle) +{ + if(!proxy_info_matches(data, needle)) + return FALSE; + + /* the user information is case-sensitive + or at least it is not defined as case-insensitive + see https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 */ + + /* curl_strequal does a case insensitive comparison, + so do not use it here! */ + if(Curl_timestrcmp(data->user, needle->user) || + Curl_timestrcmp(data->passwd, needle->passwd)) + return FALSE; + return TRUE; +} +#else +/* disabled, will not get called */ +#define proxy_info_matches(x,y) FALSE +#define socks_proxy_info_matches(x,y) FALSE +#endif + +/* A connection has to have been idle for a shorter time than 'maxage_conn' + (the success rate is just too low after this), or created less than + 'maxlifetime_conn' ago, to be subject for reuse. */ + +static bool conn_maxage(struct Curl_easy *data, + struct connectdata *conn, + struct curltime now) +{ + timediff_t idletime, lifetime; + + idletime = Curl_timediff(now, conn->lastused); + idletime /= 1000; /* integer seconds is fine */ + + if(idletime > data->set.maxage_conn) { + infof(data, "Too old connection (%" CURL_FORMAT_TIMEDIFF_T + " seconds idle), disconnect it", idletime); + return TRUE; + } + + lifetime = Curl_timediff(now, conn->created); + lifetime /= 1000; /* integer seconds is fine */ + + if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) { + infof(data, + "Too old connection (%" CURL_FORMAT_TIMEDIFF_T + " seconds since creation), disconnect it", lifetime); + return TRUE; + } + + + return FALSE; +} + +/* + * This function checks if the given connection is dead and prunes it from + * the connection cache if so. + * + * When this is called as a Curl_conncache_foreach() callback, the connection + * cache lock is held! + * + * Returns TRUE if the connection was dead and pruned. + */ +static bool prune_if_dead(struct connectdata *conn, + struct Curl_easy *data) +{ + if(!CONN_INUSE(conn)) { + /* The check for a dead socket makes sense only if the connection is not in + use */ + bool dead; + struct curltime now = Curl_now(); + if(conn_maxage(data, conn, now)) { + /* avoid check if already too old */ + dead = TRUE; + } + else if(conn->handler->connection_check) { + /* The protocol has a special method for checking the state of the + connection. Use it to check if the connection is dead. */ + unsigned int state; + + /* briefly attach the connection to this transfer for the purpose of + checking it */ + Curl_attach_connection(data, conn); + + state = conn->handler->connection_check(data, conn, CONNCHECK_ISDEAD); + dead = (state & CONNRESULT_DEAD); + /* detach the connection again */ + Curl_detach_connection(data); + + } + else { + bool input_pending = FALSE; + + Curl_attach_connection(data, conn); + dead = !Curl_conn_is_alive(data, conn, &input_pending); + if(input_pending) { + /* For reuse, we want a "clean" connection state. The includes + * that we expect - in general - no waiting input data. Input + * waiting might be a TLS Notify Close, for example. We reject + * that. + * For protocols where data from other end may arrive at + * any time (HTTP/2 PING for example), the protocol handler needs + * to install its own `connection_check` callback. + */ + DEBUGF(infof(data, "connection has input pending, not reusable")); + dead = TRUE; + } + Curl_detach_connection(data); + } + + if(dead) { + /* remove connection from cache */ + infof(data, "Connection %" CURL_FORMAT_CURL_OFF_T " seems to be dead", + conn->connection_id); + Curl_conncache_remove_conn(data, conn, FALSE); + return TRUE; + } + } + return FALSE; +} + +/* + * Wrapper to use prune_if_dead() function in Curl_conncache_foreach() + * + */ +static int call_prune_if_dead(struct Curl_easy *data, + struct connectdata *conn, void *param) +{ + struct connectdata **pruned = (struct connectdata **)param; + if(prune_if_dead(conn, data)) { + /* stop the iteration here, pass back the connection that was pruned */ + *pruned = conn; + return 1; + } + return 0; /* continue iteration */ +} + +/* + * This function scans the connection cache for half-open/dead connections, + * closes and removes them. The cleanup is done at most once per second. + * + * When called, this transfer has no connection attached. + */ +static void prune_dead_connections(struct Curl_easy *data) +{ + struct curltime now = Curl_now(); + timediff_t elapsed; + + DEBUGASSERT(!data->conn); /* no connection */ + CONNCACHE_LOCK(data); + elapsed = + Curl_timediff(now, data->state.conn_cache->last_cleanup); + CONNCACHE_UNLOCK(data); + + if(elapsed >= 1000L) { + struct connectdata *pruned = NULL; + while(Curl_conncache_foreach(data, data->state.conn_cache, &pruned, + call_prune_if_dead)) { + /* unlocked */ + + /* connection previously removed from cache in prune_if_dead() */ + + /* disconnect it, do not treat as aborted */ + Curl_disconnect(data, pruned, FALSE); + } + CONNCACHE_LOCK(data); + data->state.conn_cache->last_cleanup = now; + CONNCACHE_UNLOCK(data); + } +} + +#ifdef USE_SSH +static bool ssh_config_matches(struct connectdata *one, + struct connectdata *two) +{ + return (Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) && + Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub)); +} +#else +#define ssh_config_matches(x,y) FALSE +#endif + +/* + * Given one filled in connection struct (named needle), this function should + * detect if there already is one that has all the significant details + * exactly the same and thus should be used instead. + * + * If there is a match, this function returns TRUE - and has marked the + * connection as 'in-use'. It must later be called with ConnectionDone() to + * return back to 'idle' (unused) state. + * + * The force_reuse flag is set if the connection must be used. + */ +static bool +ConnectionExists(struct Curl_easy *data, + struct connectdata *needle, + struct connectdata **usethis, + bool *force_reuse, + bool *waitpipe) +{ + struct connectdata *chosen = NULL; + bool foundPendingCandidate = FALSE; + bool canmultiplex = FALSE; + struct connectbundle *bundle; + struct Curl_llist_element *curr; + +#ifdef USE_NTLM + bool wantNTLMhttp = ((data->state.authhost.want & CURLAUTH_NTLM) && + (needle->handler->protocol & PROTO_FAMILY_HTTP)); +#ifndef CURL_DISABLE_PROXY + bool wantProxyNTLMhttp = (needle->bits.proxy_user_passwd && + ((data->state.authproxy.want & + CURLAUTH_NTLM) && + (needle->handler->protocol & PROTO_FAMILY_HTTP))); +#else + bool wantProxyNTLMhttp = FALSE; +#endif +#endif + /* plain HTTP with upgrade */ + bool h2upgrade = (data->state.httpwant == CURL_HTTP_VERSION_2_0) && + (needle->handler->protocol & CURLPROTO_HTTP); + + *usethis = NULL; + *force_reuse = FALSE; + *waitpipe = FALSE; + + /* Look up the bundle with all the connections to this particular host. + Locks the connection cache, beware of early returns! */ + bundle = Curl_conncache_find_bundle(data, needle, data->state.conn_cache); + if(!bundle) { + CONNCACHE_UNLOCK(data); + return FALSE; + } + infof(data, "Found bundle for host: %p [%s]", + (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? + "can multiplex" : "serially")); + + /* We can only multiplex iff the transfer allows it AND we know + * that the server we want to talk to supports it as well. */ + canmultiplex = FALSE; + if(IsMultiplexingPossible(data, needle)) { + if(bundle->multiuse == BUNDLE_UNKNOWN) { + if(data->set.pipewait) { + infof(data, "Server does not support multiplex yet, wait"); + *waitpipe = TRUE; + CONNCACHE_UNLOCK(data); + return FALSE; /* no reuse */ + } + infof(data, "Server does not support multiplex (yet)"); + } + else if(bundle->multiuse == BUNDLE_MULTIPLEX) { + if(Curl_multiplex_wanted(data->multi)) + canmultiplex = TRUE; + else + infof(data, "Could multiplex, but not asked to"); + } + else if(bundle->multiuse == BUNDLE_NO_MULTIUSE) { + infof(data, "Can not multiplex, even if we wanted to"); + } + } + + curr = bundle->conn_list.head; + while(curr) { + struct connectdata *check = curr->ptr; + /* Get next node now. We might remove a dead `check` connection which + * would invalidate `curr` as well. */ + curr = curr->next; + + /* Note that if we use an HTTP proxy in normal mode (no tunneling), we + * check connections to that proxy and not to the actual remote server. + */ + if(check->connect_only || check->bits.close) + /* connect-only or to-be-closed connections will not be reused */ + continue; + + if(data->set.ipver != CURL_IPRESOLVE_WHATEVER + && data->set.ipver != check->ip_version) { + /* skip because the connection is not via the requested IP version */ + continue; + } + + if(!canmultiplex) { + if(Curl_resolver_asynch() && + /* remote_ip[0] is NUL only if the resolving of the name has not + completed yet and until then we do not reuse this connection */ + !check->primary.remote_ip[0]) + continue; + } + + if(CONN_INUSE(check)) { + if(!canmultiplex) { + /* transfer cannot be multiplexed and check is in use */ + continue; + } + else { + /* Could multiplex, but not when check belongs to another multi */ + struct Curl_llist_element *e = check->easyq.head; + struct Curl_easy *entry = e->ptr; + if(entry->multi != data->multi) + continue; + } + } + + if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { + foundPendingCandidate = TRUE; + /* Do not pick a connection that has not connected yet */ + infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T + " is not open enough, cannot reuse", check->connection_id); + continue; + } + + /* `check` is connected. if it is in use and does not support multiplex, + * we cannot use it. */ + if(!check->bits.multiplex && CONN_INUSE(check)) + continue; + +#ifdef USE_UNIX_SOCKETS + if(needle->unix_domain_socket) { + if(!check->unix_domain_socket) + continue; + if(strcmp(needle->unix_domain_socket, check->unix_domain_socket)) + continue; + if(needle->bits.abstract_unix_socket != + check->bits.abstract_unix_socket) + continue; + } + else if(check->unix_domain_socket) + continue; +#endif + + if((needle->handler->flags&PROTOPT_SSL) != + (check->handler->flags&PROTOPT_SSL)) + /* do not do mixed SSL and non-SSL connections */ + if(get_protocol_family(check->handler) != + needle->handler->protocol || !check->bits.tls_upgraded) + /* except protocols that have been upgraded via TLS */ + continue; + + if(needle->bits.conn_to_host != check->bits.conn_to_host) + /* do not mix connections that use the "connect to host" feature and + * connections that do not use this feature */ + continue; + + if(needle->bits.conn_to_port != check->bits.conn_to_port) + /* do not mix connections that use the "connect to port" feature and + * connections that do not use this feature */ + continue; + +#ifndef CURL_DISABLE_PROXY + if(needle->bits.httpproxy != check->bits.httpproxy || + needle->bits.socksproxy != check->bits.socksproxy) + continue; + + if(needle->bits.socksproxy && + !socks_proxy_info_matches(&needle->socks_proxy, + &check->socks_proxy)) + continue; + + if(needle->bits.httpproxy) { + if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy) + continue; + + if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy)) + continue; + + if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) { + /* https proxies come in different types, http/1.1, h2, ... */ + if(needle->http_proxy.proxytype != check->http_proxy.proxytype) + continue; + /* match SSL config to proxy */ + if(!Curl_ssl_conn_config_match(data, check, TRUE)) { + DEBUGF(infof(data, + "Connection #%" CURL_FORMAT_CURL_OFF_T + " has different SSL proxy parameters, cannot reuse", + check->connection_id)); + continue; + } + /* the SSL config to the server, which may apply here is checked + * further below */ + } + } +#endif + + if(h2upgrade && !check->httpversion && canmultiplex) { + if(data->set.pipewait) { + infof(data, "Server upgrade does not support multiplex yet, wait"); + *waitpipe = TRUE; + CONNCACHE_UNLOCK(data); + return FALSE; /* no reuse */ + } + infof(data, "Server upgrade cannot be used"); + continue; /* cannot be used atm */ + } + + if(needle->localdev || needle->localport) { + /* If we are bound to a specific local end (IP+port), we must not + reuse a random other one, although if we did not ask for a + particular one we can reuse one that was bound. + + This comparison is a bit rough and too strict. Since the input + parameters can be specified in numerous ways and still end up the + same it would take a lot of processing to make it really accurate. + Instead, this matching will assume that reuses of bound connections + will most likely also reuse the exact same binding parameters and + missing out a few edge cases should not hurt anyone very much. + */ + if((check->localport != needle->localport) || + (check->localportrange != needle->localportrange) || + (needle->localdev && + (!check->localdev || strcmp(check->localdev, needle->localdev)))) + continue; + } + + if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) { + /* This protocol requires credentials per connection, + so verify that we are using the same name and password as well */ + if(Curl_timestrcmp(needle->user, check->user) || + Curl_timestrcmp(needle->passwd, check->passwd) || + Curl_timestrcmp(needle->sasl_authzid, check->sasl_authzid) || + Curl_timestrcmp(needle->oauth_bearer, check->oauth_bearer)) { + /* one of them was different */ + continue; + } + } + + /* GSS delegation differences do not actually affect every connection + and auth method, but this check takes precaution before efficiency */ + if(needle->gssapi_delegation != check->gssapi_delegation) + continue; + + /* If looking for HTTP and the HTTP version we want is less + * than the HTTP version of the check connection, continue looking */ + if((needle->handler->protocol & PROTO_FAMILY_HTTP) && + (((check->httpversion >= 20) && + (data->state.httpwant < CURL_HTTP_VERSION_2_0)) + || ((check->httpversion >= 30) && + (data->state.httpwant < CURL_HTTP_VERSION_3)))) + continue; +#ifdef USE_SSH + else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { + if(!ssh_config_matches(needle, check)) + continue; + } +#endif +#ifndef CURL_DISABLE_FTP + else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) { + /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ + if(Curl_timestrcmp(needle->proto.ftpc.account, + check->proto.ftpc.account) || + Curl_timestrcmp(needle->proto.ftpc.alternative_to_user, + check->proto.ftpc.alternative_to_user) || + (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) || + (needle->proto.ftpc.ccc != check->proto.ftpc.ccc)) + continue; + } +#endif + + /* Additional match requirements if talking TLS OR + * not talking to an HTTP proxy OR using a tunnel through a proxy */ + if((needle->handler->flags&PROTOPT_SSL) +#ifndef CURL_DISABLE_PROXY + || !needle->bits.httpproxy || needle->bits.tunnel_proxy +#endif + ) { + /* Talking the same protocol scheme or a TLS upgraded protocol in the + * same protocol family? */ + if(!strcasecompare(needle->handler->scheme, check->handler->scheme) && + (get_protocol_family(check->handler) != + needle->handler->protocol || !check->bits.tls_upgraded)) + continue; + + /* If needle has "conn_to_*" set, check must match this */ + if((needle->bits.conn_to_host && !strcasecompare( + needle->conn_to_host.name, check->conn_to_host.name)) || + (needle->bits.conn_to_port && + needle->conn_to_port != check->conn_to_port)) + continue; + + /* hostname and port must match */ + if(!strcasecompare(needle->host.name, check->host.name) || + needle->remote_port != check->remote_port) + continue; + + /* If talking TLS, check needs to use the same SSL options. */ + if((needle->handler->flags & PROTOPT_SSL) && + !Curl_ssl_conn_config_match(data, check, FALSE)) { + DEBUGF(infof(data, + "Connection #%" CURL_FORMAT_CURL_OFF_T + " has different SSL parameters, cannot reuse", + check->connection_id)); + continue; + } + } + +#if defined(USE_NTLM) + /* If we are looking for an HTTP+NTLM connection, check if this is + already authenticating with the right credentials. If not, keep + looking so that we can reuse NTLM connections if + possible. (Especially we must not reuse the same connection if + partway through a handshake!) */ + if(wantNTLMhttp) { + if(Curl_timestrcmp(needle->user, check->user) || + Curl_timestrcmp(needle->passwd, check->passwd)) { + + /* we prefer a credential match, but this is at least a connection + that can be reused and "upgraded" to NTLM */ + if(check->http_ntlm_state == NTLMSTATE_NONE) + chosen = check; + continue; + } + } + else if(check->http_ntlm_state != NTLMSTATE_NONE) { + /* Connection is using NTLM auth but we do not want NTLM */ + continue; + } + +#ifndef CURL_DISABLE_PROXY + /* Same for Proxy NTLM authentication */ + if(wantProxyNTLMhttp) { + /* Both check->http_proxy.user and check->http_proxy.passwd can be + * NULL */ + if(!check->http_proxy.user || !check->http_proxy.passwd) + continue; + + if(Curl_timestrcmp(needle->http_proxy.user, + check->http_proxy.user) || + Curl_timestrcmp(needle->http_proxy.passwd, + check->http_proxy.passwd)) + continue; + } + else if(check->proxy_ntlm_state != NTLMSTATE_NONE) { + /* Proxy connection is using NTLM auth but we do not want NTLM */ + continue; + } +#endif + if(wantNTLMhttp || wantProxyNTLMhttp) { + /* Credentials are already checked, we may use this connection. + * With NTLM being weird as it is, we MUST use a + * connection where it has already been fully negotiated. + * If it has not, we keep on looking for a better one. */ + chosen = check; + + if((wantNTLMhttp && + (check->http_ntlm_state != NTLMSTATE_NONE)) || + (wantProxyNTLMhttp && + (check->proxy_ntlm_state != NTLMSTATE_NONE))) { + /* We must use this connection, no other */ + *force_reuse = TRUE; + break; + } + /* Continue look up for a better connection */ + continue; + } +#endif + + if(CONN_INUSE(check)) { + DEBUGASSERT(canmultiplex); + DEBUGASSERT(check->bits.multiplex); + /* If multiplexed, make sure we do not go over concurrency limit */ + if(CONN_INUSE(check) >= + Curl_multi_max_concurrent_streams(data->multi)) { + infof(data, "client side MAX_CONCURRENT_STREAMS reached" + ", skip (%zu)", CONN_INUSE(check)); + continue; + } + if(CONN_INUSE(check) >= + Curl_conn_get_max_concurrent(data, check, FIRSTSOCKET)) { + infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)", + CONN_INUSE(check)); + continue; + } + /* When not multiplexed, we have a match here! */ + infof(data, "Multiplexed connection found"); + } + else if(prune_if_dead(check, data)) { + /* disconnect it, do not treat as aborted */ + Curl_disconnect(data, check, FALSE); + continue; + } + + /* We have found a connection. Let's stop searching. */ + chosen = check; + break; + } /* loop over connection bundle */ + + if(chosen) { + /* mark it as used before releasing the lock */ + Curl_attach_connection(data, chosen); + CONNCACHE_UNLOCK(data); + *usethis = chosen; + return TRUE; /* yes, we found one to use! */ + } + CONNCACHE_UNLOCK(data); + + if(foundPendingCandidate && data->set.pipewait) { + infof(data, + "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set"); + *waitpipe = TRUE; + } + + return FALSE; /* no matching connecting exists */ +} + +/* + * verboseconnect() displays verbose information after a connect + */ +#ifndef CURL_DISABLE_VERBOSE_STRINGS +void Curl_verboseconnect(struct Curl_easy *data, + struct connectdata *conn, int sockindex) +{ + if(data->set.verbose && sockindex == SECONDARYSOCKET) + infof(data, "Connected 2nd connection to %s port %u", + conn->secondary.remote_ip, conn->secondary.remote_port); + else + infof(data, "Connected to %s (%s) port %u", + CURL_CONN_HOST_DISPNAME(conn), conn->primary.remote_ip, + conn->primary.remote_port); +} +#endif + +/* + * Allocate and initialize a new connectdata object. + */ +static struct connectdata *allocate_conn(struct Curl_easy *data) +{ + struct connectdata *conn = calloc(1, sizeof(struct connectdata)); + if(!conn) + return NULL; + + /* and we setup a few fields in case we end up actually using this struct */ + + conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ + conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ + conn->sockfd = CURL_SOCKET_BAD; + conn->writesockfd = CURL_SOCKET_BAD; + conn->connection_id = -1; /* no ID */ + conn->primary.remote_port = -1; /* unknown at this point */ + conn->remote_port = -1; /* unknown at this point */ + + /* Default protocol-independent behavior does not support persistent + connections, so we set this to force-close. Protocols that support + this need to set this to FALSE in their "curl_do" functions. */ + connclose(conn, "Default to force-close"); + + /* Store creation time to help future close decision making */ + conn->created = Curl_now(); + + /* Store current time to give a baseline to keepalive connection times. */ + conn->keepalive = conn->created; + +#ifndef CURL_DISABLE_PROXY + conn->http_proxy.proxytype = data->set.proxytype; + conn->socks_proxy.proxytype = CURLPROXY_SOCKS4; + + /* note that these two proxy bits are now just on what looks to be + requested, they may be altered down the road */ + conn->bits.proxy = (data->set.str[STRING_PROXY] && + *data->set.str[STRING_PROXY]) ? TRUE : FALSE; + conn->bits.httpproxy = (conn->bits.proxy && + (conn->http_proxy.proxytype == CURLPROXY_HTTP || + conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 || + IS_HTTPS_PROXY(conn->http_proxy.proxytype))) ? + TRUE : FALSE; + conn->bits.socksproxy = (conn->bits.proxy && + !conn->bits.httpproxy) ? TRUE : FALSE; + + if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) { + conn->bits.proxy = TRUE; + conn->bits.socksproxy = TRUE; + } + + conn->bits.proxy_user_passwd = + (data->state.aptr.proxyuser) ? TRUE : FALSE; + conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; +#endif /* CURL_DISABLE_PROXY */ + +#ifndef CURL_DISABLE_FTP + conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; + conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; +#endif + conn->ip_version = data->set.ipver; + conn->connect_only = data->set.connect_only; + conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */ + + /* Initialize the easy handle list */ + Curl_llist_init(&conn->easyq, NULL); + +#ifdef HAVE_GSSAPI + conn->data_prot = PROT_CLEAR; +#endif + + /* Store the local bind parameters that will be used for this connection */ + if(data->set.str[STRING_DEVICE]) { + conn->localdev = strdup(data->set.str[STRING_DEVICE]); + if(!conn->localdev) + goto error; + } +#ifndef CURL_DISABLE_BINDLOCAL + conn->localportrange = data->set.localportrange; + conn->localport = data->set.localport; +#endif + + /* the close socket stuff needs to be copied to the connection struct as + it may live on without (this specific) Curl_easy */ + conn->fclosesocket = data->set.fclosesocket; + conn->closesocket_client = data->set.closesocket_client; + conn->lastused = conn->created; + conn->gssapi_delegation = data->set.gssapi_delegation; + + return conn; +error: + + free(conn->localdev); + free(conn); + return NULL; +} + +const struct Curl_handler *Curl_get_scheme_handler(const char *scheme) +{ + return Curl_getn_scheme_handler(scheme, strlen(scheme)); +} + +/* returns the handler if the given scheme is built-in */ +const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, + size_t len) +{ + /* table generated by schemetable.c: + 1. gcc schemetable.c && ./a.out + 2. check how small the table gets + 3. tweak the hash algorithm, then rerun from 1 + 4. when the table is good enough + 5. copy the table into this source code + 6. make sure this function uses the same hash function that worked for + schemetable.c + 7. if needed, adjust the #ifdefs in schemetable.c and rerun + */ + static const struct Curl_handler * const protocols[67] = { +#ifndef CURL_DISABLE_FILE + &Curl_handler_file, +#else + NULL, +#endif + NULL, NULL, +#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER) + &Curl_handler_gophers, +#else + NULL, +#endif + NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmpe, +#else + NULL, +#endif +#ifndef CURL_DISABLE_SMTP + &Curl_handler_smtp, +#else + NULL, +#endif +#if defined(USE_SSH) + &Curl_handler_sftp, +#else + NULL, +#endif +#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ + (SIZEOF_CURL_OFF_T > 4) + &Curl_handler_smb, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP) + &Curl_handler_smtps, +#else + NULL, +#endif +#ifndef CURL_DISABLE_TELNET + &Curl_handler_telnet, +#else + NULL, +#endif +#ifndef CURL_DISABLE_GOPHER + &Curl_handler_gopher, +#else + NULL, +#endif +#ifndef CURL_DISABLE_TFTP + &Curl_handler_tftp, +#else + NULL, +#endif + NULL, NULL, NULL, +#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) + &Curl_handler_ftps, +#else + NULL, +#endif +#ifndef CURL_DISABLE_HTTP + &Curl_handler_http, +#else + NULL, +#endif +#ifndef CURL_DISABLE_IMAP + &Curl_handler_imap, +#else + NULL, +#endif +#ifdef USE_LIBRTMP + &Curl_handler_rtmps, +#else + NULL, +#endif +#ifdef USE_LIBRTMP + &Curl_handler_rtmpt, +#else + NULL, +#endif + NULL, NULL, NULL, +#if !defined(CURL_DISABLE_LDAP) && \ + !defined(CURL_DISABLE_LDAPS) && \ + ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ + (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) + &Curl_handler_ldaps, +#else + NULL, +#endif +#if defined(USE_WEBSOCKETS) && \ + defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) + &Curl_handler_wss, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) + &Curl_handler_https, +#else + NULL, +#endif + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +#ifndef CURL_DISABLE_RTSP + &Curl_handler_rtsp, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_SMB) && \ + defined(USE_CURL_NTLM_CORE) && (SIZEOF_CURL_OFF_T > 4) + &Curl_handler_smbs, +#else + NULL, +#endif +#if defined(USE_SSH) && !defined(USE_WOLFSSH) + &Curl_handler_scp, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifndef CURL_DISABLE_POP3 + &Curl_handler_pop3, +#else + NULL, +#endif + NULL, NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmp, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmpte, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifndef CURL_DISABLE_DICT + &Curl_handler_dict, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifndef CURL_DISABLE_MQTT + &Curl_handler_mqtt, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) + &Curl_handler_pop3s, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP) + &Curl_handler_imaps, +#else + NULL, +#endif + NULL, +#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) + &Curl_handler_ws, +#else + NULL, +#endif + NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmpts, +#else + NULL, +#endif +#ifndef CURL_DISABLE_LDAP + &Curl_handler_ldap, +#else + NULL, +#endif + NULL, NULL, +#ifndef CURL_DISABLE_FTP + &Curl_handler_ftp, +#else + NULL, +#endif + }; + + if(len && (len <= 7)) { + const char *s = scheme; + size_t l = len; + const struct Curl_handler *h; + unsigned int c = 978; + while(l) { + c <<= 5; + c += (unsigned int)Curl_raw_tolower(*s); + s++; + l--; + } + + h = protocols[c % 67]; + if(h && strncasecompare(scheme, h->scheme, len) && !h->scheme[len]) + return h; + } + return NULL; +} + +static CURLcode findprotocol(struct Curl_easy *data, + struct connectdata *conn, + const char *protostr) +{ + const struct Curl_handler *p = Curl_get_scheme_handler(protostr); + + if(p && /* Protocol found in table. Check if allowed */ + (data->set.allowed_protocols & p->protocol)) { + + /* it is allowed for "normal" request, now do an extra check if this is + the result of a redirect */ + if(data->state.this_is_a_follow && + !(data->set.redir_protocols & p->protocol)) + /* nope, get out */ + ; + else { + /* Perform setup complement if some. */ + conn->handler = conn->given = p; + /* 'port' and 'remote_port' are set in setup_connection_internals() */ + return CURLE_OK; + } + } + + /* The protocol was not found in the table, but we do not have to assign it + to anything since it is already assigned to a dummy-struct in the + create_conn() function when the connectdata struct is allocated. */ + failf(data, "Protocol \"%s\" %s%s", protostr, + p ? "disabled" : "not supported", + data->state.this_is_a_follow ? " (in redirect)":""); + + return CURLE_UNSUPPORTED_PROTOCOL; +} + + +CURLcode Curl_uc_to_curlcode(CURLUcode uc) +{ + switch(uc) { + default: + return CURLE_URL_MALFORMAT; + case CURLUE_UNSUPPORTED_SCHEME: + return CURLE_UNSUPPORTED_PROTOCOL; + case CURLUE_OUT_OF_MEMORY: + return CURLE_OUT_OF_MEMORY; + case CURLUE_USER_NOT_ALLOWED: + return CURLE_LOGIN_DENIED; + } +} + +#ifdef USE_IPV6 +/* + * If the URL was set with an IPv6 numerical address with a zone id part, set + * the scope_id based on that! + */ + +static void zonefrom_url(CURLU *uh, struct Curl_easy *data, + struct connectdata *conn) +{ + char *zoneid; + CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)data; +#endif + + if(!uc && zoneid) { + char *endp; + unsigned long scope = strtoul(zoneid, &endp, 10); + if(!*endp && (scope < UINT_MAX)) + /* A plain number, use it directly as a scope id. */ + conn->scope_id = (unsigned int)scope; +#if defined(HAVE_IF_NAMETOINDEX) + else { +#elif defined(_WIN32) + else if(Curl_if_nametoindex) { +#endif + +#if defined(HAVE_IF_NAMETOINDEX) || defined(_WIN32) + /* Zone identifier is not numeric */ + unsigned int scopeidx = 0; +#if defined(_WIN32) + scopeidx = Curl_if_nametoindex(zoneid); +#else + scopeidx = if_nametoindex(zoneid); +#endif + if(!scopeidx) { +#ifndef CURL_DISABLE_VERBOSE_STRINGS + char buffer[STRERROR_LEN]; + infof(data, "Invalid zoneid: %s; %s", zoneid, + Curl_strerror(errno, buffer, sizeof(buffer))); +#endif + } + else + conn->scope_id = scopeidx; + } +#endif /* HAVE_IF_NAMETOINDEX || _WIN32 */ + + free(zoneid); + } +} +#else +#define zonefrom_url(a,b,c) Curl_nop_stmt +#endif + +/* + * Parse URL and fill in the relevant members of the connection struct. + */ +static CURLcode parseurlandfillconn(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result; + CURLU *uh; + CURLUcode uc; + char *hostname; + bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); + + up_free(data); /* cleanup previous leftovers first */ + + /* parse the URL */ + if(use_set_uh) { + uh = data->state.uh = curl_url_dup(data->set.uh); + } + else { + uh = data->state.uh = curl_url(); + } + + if(!uh) + return CURLE_OUT_OF_MEMORY; + + if(data->set.str[STRING_DEFAULT_PROTOCOL] && + !Curl_is_absolute_url(data->state.url, NULL, 0, TRUE)) { + char *url = aprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL], + data->state.url); + if(!url) + return CURLE_OUT_OF_MEMORY; + if(data->state.url_alloc) + free(data->state.url); + data->state.url = url; + data->state.url_alloc = TRUE; + } + + if(!use_set_uh) { + char *newurl; + uc = curl_url_set(uh, CURLUPART_URL, data->state.url, (unsigned int) + (CURLU_GUESS_SCHEME | + CURLU_NON_SUPPORT_SCHEME | + (data->set.disallow_username_in_url ? + CURLU_DISALLOW_USER : 0) | + (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); + if(uc) { + failf(data, "URL rejected: %s", curl_url_strerror(uc)); + return Curl_uc_to_curlcode(uc); + } + + /* after it was parsed, get the generated normalized version */ + uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + if(data->state.url_alloc) + free(data->state.url); + data->state.url = newurl; + data->state.url_alloc = TRUE; + } + + uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + + uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0); + if(uc) { + if(!strcasecompare("file", data->state.up.scheme)) + return CURLE_OUT_OF_MEMORY; + } + else if(strlen(data->state.up.hostname) > MAX_URL_LEN) { + failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); + return CURLE_URL_MALFORMAT; + } + hostname = data->state.up.hostname; + + if(hostname && hostname[0] == '[') { + /* This looks like an IPv6 address literal. See if there is an address + scope. */ + size_t hlen; + conn->bits.ipv6_ip = TRUE; + /* cut off the brackets! */ + hostname++; + hlen = strlen(hostname); + hostname[hlen - 1] = 0; + + zonefrom_url(uh, data, conn); + } + + /* make sure the connect struct gets its own copy of the hostname */ + conn->host.rawalloc = strdup(hostname ? hostname : ""); + if(!conn->host.rawalloc) + return CURLE_OUT_OF_MEMORY; + conn->host.name = conn->host.rawalloc; + + /************************************************************* + * IDN-convert the hostnames + *************************************************************/ + result = Curl_idnconvert_hostname(&conn->host); + if(result) + return result; + +#ifndef CURL_DISABLE_HSTS + /* HSTS upgrade */ + if(data->hsts && strcasecompare("http", data->state.up.scheme)) { + /* This MUST use the IDN decoded name */ + if(Curl_hsts(data->hsts, conn->host.name, TRUE)) { + char *url; + Curl_safefree(data->state.up.scheme); + uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); + if(uc) + return Curl_uc_to_curlcode(uc); + if(data->state.url_alloc) + Curl_safefree(data->state.url); + /* after update, get the updated version */ + uc = curl_url_get(uh, CURLUPART_URL, &url, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); + if(uc) { + free(url); + return Curl_uc_to_curlcode(uc); + } + data->state.url = url; + data->state.url_alloc = TRUE; + infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", + data->state.url); + } + } +#endif + + result = findprotocol(data, conn, data->state.up.scheme); + if(result) + return result; + + /* + * username and password set with their own options override the + * credentials possibly set in the URL. + */ + if(!data->set.str[STRING_PASSWORD]) { + uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); + if(!uc) { + char *decoded; + result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL, + conn->handler->flags&PROTOPT_USERPWDCTRL ? + REJECT_ZERO : REJECT_CTRL); + if(result) + return result; + conn->passwd = decoded; + result = Curl_setstropt(&data->state.aptr.passwd, decoded); + if(result) + return result; + } + else if(uc != CURLUE_NO_PASSWORD) + return Curl_uc_to_curlcode(uc); + } + + if(!data->set.str[STRING_USERNAME]) { + /* we do not use the URL API's URL decoder option here since it rejects + control codes and we want to allow them for some schemes in the user + and password fields */ + uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); + if(!uc) { + char *decoded; + result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL, + conn->handler->flags&PROTOPT_USERPWDCTRL ? + REJECT_ZERO : REJECT_CTRL); + if(result) + return result; + conn->user = decoded; + result = Curl_setstropt(&data->state.aptr.user, decoded); + } + else if(uc != CURLUE_NO_USER) + return Curl_uc_to_curlcode(uc); + else if(data->state.aptr.passwd) { + /* no user was set but a password, set a blank user */ + result = Curl_setstropt(&data->state.aptr.user, ""); + } + if(result) + return result; + } + + uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, + CURLU_URLDECODE); + if(!uc) { + conn->options = strdup(data->state.up.options); + if(!conn->options) + return CURLE_OUT_OF_MEMORY; + } + else if(uc != CURLUE_NO_OPTIONS) + return Curl_uc_to_curlcode(uc); + + uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, + CURLU_URLENCODE); + if(uc) + return Curl_uc_to_curlcode(uc); + + uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port, + CURLU_DEFAULT_PORT); + if(uc) { + if(!strcasecompare("file", data->state.up.scheme)) + return CURLE_OUT_OF_MEMORY; + } + else { + unsigned long port = strtoul(data->state.up.port, NULL, 10); + conn->primary.remote_port = conn->remote_port = + (data->set.use_port && data->state.allow_port) ? + data->set.use_port : curlx_ultous(port); + } + + (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); + +#ifdef USE_IPV6 + if(data->set.scope_id) + /* Override any scope that was set above. */ + conn->scope_id = data->set.scope_id; +#endif + + return CURLE_OK; +} + + +/* + * If we are doing a resumed transfer, we need to setup our stuff + * properly. + */ +static CURLcode setup_range(struct Curl_easy *data) +{ + struct UrlState *s = &data->state; + s->resume_from = data->set.set_resume_from; + if(s->resume_from || data->set.str[STRING_SET_RANGE]) { + if(s->rangestringalloc) + free(s->range); + + if(s->resume_from) + s->range = aprintf("%" CURL_FORMAT_CURL_OFF_T "-", s->resume_from); + else + s->range = strdup(data->set.str[STRING_SET_RANGE]); + + s->rangestringalloc = (s->range) ? TRUE : FALSE; + + if(!s->range) + return CURLE_OUT_OF_MEMORY; + + /* tell ourselves to fetch this range */ + s->use_range = TRUE; /* enable range download */ + } + else + s->use_range = FALSE; /* disable range download */ + + return CURLE_OK; +} + + +/* + * setup_connection_internals() - + * + * Setup connection internals specific to the requested protocol in the + * Curl_easy. This is inited and setup before the connection is made but + * is about the particular protocol that is to be used. + * + * This MUST get called after proxy magic has been figured out. + */ +static CURLcode setup_connection_internals(struct Curl_easy *data, + struct connectdata *conn) +{ + const struct Curl_handler *p; + CURLcode result; + + /* Perform setup complement if some. */ + p = conn->handler; + + if(p->setup_connection) { + result = (*p->setup_connection)(data, conn); + + if(result) + return result; + + p = conn->handler; /* May have changed. */ + } + + if(conn->primary.remote_port < 0) + /* we check for -1 here since if proxy was detected already, this + was very likely already set to the proxy port */ + conn->primary.remote_port = p->defport; + + return CURLE_OK; +} + + +#ifndef CURL_DISABLE_PROXY + +#ifndef CURL_DISABLE_HTTP +/**************************************************************** +* Detect what (if any) proxy to use. Remember that this selects a host +* name and is not limited to HTTP proxies only. +* The returned pointer must be freed by the caller (unless NULL) +****************************************************************/ +static char *detect_proxy(struct Curl_easy *data, + struct connectdata *conn) +{ + char *proxy = NULL; + + /* If proxy was not specified, we check for default proxy environment + * variables, to enable i.e Lynx compliance: + * + * http_proxy=http://some.server.dom:port/ + * https_proxy=http://some.server.dom:port/ + * ftp_proxy=http://some.server.dom:port/ + * no_proxy=domain1.dom,host.domain2.dom + * (a comma-separated list of hosts which should + * not be proxied, or an asterisk to override + * all proxy variables) + * all_proxy=http://some.server.dom:port/ + * (seems to exist for the CERN www lib. Probably + * the first to check for.) + * + * For compatibility, the all-uppercase versions of these variables are + * checked if the lowercase versions do not exist. + */ + char proxy_env[20]; + char *envp = proxy_env; +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)data; +#endif + + msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", conn->handler->scheme); + + /* read the protocol proxy: */ + proxy = curl_getenv(proxy_env); + + /* + * We do not try the uppercase version of HTTP_PROXY because of + * security reasons: + * + * When curl is used in a webserver application + * environment (cgi or php), this environment variable can + * be controlled by the web server user by setting the + * http header 'Proxy:' to some value. + * + * This can cause 'internal' http/ftp requests to be + * arbitrarily redirected by any external attacker. + */ + if(!proxy && !strcasecompare("http_proxy", proxy_env)) { + /* There was no lowercase variable, try the uppercase version: */ + Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); + proxy = curl_getenv(proxy_env); + } + + if(!proxy) { +#ifdef USE_WEBSOCKETS + /* websocket proxy fallbacks */ + if(strcasecompare("ws_proxy", proxy_env)) { + proxy = curl_getenv("http_proxy"); + } + else if(strcasecompare("wss_proxy", proxy_env)) { + proxy = curl_getenv("https_proxy"); + if(!proxy) + proxy = curl_getenv("HTTPS_PROXY"); + } + if(!proxy) { +#endif + envp = (char *)"all_proxy"; + proxy = curl_getenv(envp); /* default proxy to use */ + if(!proxy) { + envp = (char *)"ALL_PROXY"; + proxy = curl_getenv(envp); + } +#ifdef USE_WEBSOCKETS + } +#endif + } + if(proxy) + infof(data, "Uses proxy env variable %s == '%s'", envp, proxy); + + return proxy; +} +#endif /* CURL_DISABLE_HTTP */ + +/* + * If this is supposed to use a proxy, we need to figure out the proxy + * hostname, so that we can reuse an existing connection + * that may exist registered to the same proxy host. + */ +static CURLcode parse_proxy(struct Curl_easy *data, + struct connectdata *conn, char *proxy, + curl_proxytype proxytype) +{ + char *portptr = NULL; + int port = -1; + char *proxyuser = NULL; + char *proxypasswd = NULL; + char *host = NULL; + bool sockstype; + CURLUcode uc; + struct proxy_info *proxyinfo; + CURLU *uhp = curl_url(); + CURLcode result = CURLE_OK; + char *scheme = NULL; +#ifdef USE_UNIX_SOCKETS + char *path = NULL; + bool is_unix_proxy = FALSE; +#endif + + + if(!uhp) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + /* When parsing the proxy, allowing non-supported schemes since we have + these made up ones for proxies. Guess scheme for URLs without it. */ + uc = curl_url_set(uhp, CURLUPART_URL, proxy, + CURLU_NON_SUPPORT_SCHEME|CURLU_GUESS_SCHEME); + if(!uc) { + /* parsed okay as a URL */ + uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + if(strcasecompare("https", scheme)) { + if(proxytype != CURLPROXY_HTTPS2) + proxytype = CURLPROXY_HTTPS; + else + proxytype = CURLPROXY_HTTPS2; + } + else if(strcasecompare("socks5h", scheme)) + proxytype = CURLPROXY_SOCKS5_HOSTNAME; + else if(strcasecompare("socks5", scheme)) + proxytype = CURLPROXY_SOCKS5; + else if(strcasecompare("socks4a", scheme)) + proxytype = CURLPROXY_SOCKS4A; + else if(strcasecompare("socks4", scheme) || + strcasecompare("socks", scheme)) + proxytype = CURLPROXY_SOCKS4; + else if(strcasecompare("http", scheme)) + ; /* leave it as HTTP or HTTP/1.0 */ + else { + /* Any other xxx:// reject! */ + failf(data, "Unsupported proxy scheme for \'%s\'", proxy); + result = CURLE_COULDNT_CONNECT; + goto error; + } + } + else { + failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, + curl_url_strerror(uc)); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } + +#ifdef USE_SSL + if(!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) +#endif + if(IS_HTTPS_PROXY(proxytype)) { + failf(data, "Unsupported proxy \'%s\', libcurl is built without the " + "HTTPS-proxy support.", proxy); + result = CURLE_NOT_BUILT_IN; + goto error; + } + + sockstype = + proxytype == CURLPROXY_SOCKS5_HOSTNAME || + proxytype == CURLPROXY_SOCKS5 || + proxytype == CURLPROXY_SOCKS4A || + proxytype == CURLPROXY_SOCKS4; + + proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy; + proxyinfo->proxytype = (unsigned char)proxytype; + + /* Is there a username and password given in this proxy url? */ + uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); + if(uc && (uc != CURLUE_NO_USER)) + goto error; + uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE); + if(uc && (uc != CURLUE_NO_PASSWORD)) + goto error; + + if(proxyuser || proxypasswd) { + Curl_safefree(proxyinfo->user); + proxyinfo->user = proxyuser; + result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser); + proxyuser = NULL; + if(result) + goto error; + Curl_safefree(proxyinfo->passwd); + if(!proxypasswd) { + proxypasswd = strdup(""); + if(!proxypasswd) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + } + proxyinfo->passwd = proxypasswd; + result = Curl_setstropt(&data->state.aptr.proxypasswd, proxypasswd); + proxypasswd = NULL; + if(result) + goto error; + conn->bits.proxy_user_passwd = TRUE; /* enable it */ + } + + (void)curl_url_get(uhp, CURLUPART_PORT, &portptr, 0); + + if(portptr) { + port = (int)strtol(portptr, NULL, 10); + free(portptr); + } + else { + if(data->set.proxyport) + /* None given in the proxy string, then get the default one if it is + given */ + port = (int)data->set.proxyport; + else { + if(IS_HTTPS_PROXY(proxytype)) + port = CURL_DEFAULT_HTTPS_PROXY_PORT; + else + port = CURL_DEFAULT_PROXY_PORT; + } + } + if(port >= 0) { + proxyinfo->port = port; + if(conn->primary.remote_port < 0 || sockstype || + !conn->socks_proxy.host.rawalloc) + conn->primary.remote_port = port; + } + + /* now, clone the proxy hostname */ + uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } +#ifdef USE_UNIX_SOCKETS + if(sockstype && strcasecompare(UNIX_SOCKET_PREFIX, host)) { + uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + /* path will be "/", if no path was found */ + if(strcmp("/", path)) { + is_unix_proxy = TRUE; + free(host); + host = aprintf(UNIX_SOCKET_PREFIX"%s", path); + if(!host) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + Curl_safefree(proxyinfo->host.rawalloc); + proxyinfo->host.rawalloc = host; + proxyinfo->host.name = host; + host = NULL; + } + } + + if(!is_unix_proxy) { +#endif + Curl_safefree(proxyinfo->host.rawalloc); + proxyinfo->host.rawalloc = host; + if(host[0] == '[') { + /* this is a numerical IPv6, strip off the brackets */ + size_t len = strlen(host); + host[len-1] = 0; /* clear the trailing bracket */ + host++; + zonefrom_url(uhp, data, conn); + } + proxyinfo->host.name = host; + host = NULL; +#ifdef USE_UNIX_SOCKETS + } +#endif + +error: + free(proxyuser); + free(proxypasswd); + free(host); + free(scheme); +#ifdef USE_UNIX_SOCKETS + free(path); +#endif + curl_url_cleanup(uhp); + return result; +} + +/* + * Extract the user and password from the authentication string + */ +static CURLcode parse_proxy_auth(struct Curl_easy *data, + struct connectdata *conn) +{ + const char *proxyuser = data->state.aptr.proxyuser ? + data->state.aptr.proxyuser : ""; + const char *proxypasswd = data->state.aptr.proxypasswd ? + data->state.aptr.proxypasswd : ""; + CURLcode result = CURLE_OUT_OF_MEMORY; + + conn->http_proxy.user = strdup(proxyuser); + if(conn->http_proxy.user) { + conn->http_proxy.passwd = strdup(proxypasswd); + if(conn->http_proxy.passwd) + result = CURLE_OK; + else + Curl_safefree(conn->http_proxy.user); + } + return result; +} + +/* create_conn helper to parse and init proxy values. to be called after unix + socket init but before any proxy vars are evaluated. */ +static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, + struct connectdata *conn) +{ + char *proxy = NULL; + char *socksproxy = NULL; + char *no_proxy = NULL; + CURLcode result = CURLE_OK; + + /************************************************************* + * Extract the user and password from the authentication string + *************************************************************/ + if(conn->bits.proxy_user_passwd) { + result = parse_proxy_auth(data, conn); + if(result) + goto out; + } + + /************************************************************* + * Detect what (if any) proxy to use + *************************************************************/ + if(data->set.str[STRING_PROXY]) { + proxy = strdup(data->set.str[STRING_PROXY]); + /* if global proxy is set, this is it */ + if(!proxy) { + failf(data, "memory shortage"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + + if(data->set.str[STRING_PRE_PROXY]) { + socksproxy = strdup(data->set.str[STRING_PRE_PROXY]); + /* if global socks proxy is set, this is it */ + if(!socksproxy) { + failf(data, "memory shortage"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + + if(!data->set.str[STRING_NOPROXY]) { + const char *p = "no_proxy"; + no_proxy = curl_getenv(p); + if(!no_proxy) { + p = "NO_PROXY"; + no_proxy = curl_getenv(p); + } + if(no_proxy) { + infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy); + } + } + + if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? + data->set.str[STRING_NOPROXY] : no_proxy)) { + Curl_safefree(proxy); + Curl_safefree(socksproxy); + } +#ifndef CURL_DISABLE_HTTP + else if(!proxy && !socksproxy) + /* if the host is not in the noproxy list, detect proxy. */ + proxy = detect_proxy(data, conn); +#endif /* CURL_DISABLE_HTTP */ + Curl_safefree(no_proxy); + +#ifdef USE_UNIX_SOCKETS + /* For the time being do not mix proxy and unix domain sockets. See #1274 */ + if(proxy && conn->unix_domain_socket) { + free(proxy); + proxy = NULL; + } +#endif + + if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) { + free(proxy); /* Do not bother with an empty proxy string or if the + protocol does not work with network */ + proxy = NULL; + } + if(socksproxy && (!*socksproxy || + (conn->handler->flags & PROTOPT_NONETWORK))) { + free(socksproxy); /* Do not bother with an empty socks proxy string or if + the protocol does not work with network */ + socksproxy = NULL; + } + + /*********************************************************************** + * If this is supposed to use a proxy, we need to figure out the proxy host + * name, proxy type and port number, so that we can reuse an existing + * connection that may exist registered to the same proxy host. + ***********************************************************************/ + if(proxy || socksproxy) { + curl_proxytype ptype = (curl_proxytype)conn->http_proxy.proxytype; + if(proxy) { + result = parse_proxy(data, conn, proxy, ptype); + Curl_safefree(proxy); /* parse_proxy copies the proxy string */ + if(result) + goto out; + } + + if(socksproxy) { + result = parse_proxy(data, conn, socksproxy, ptype); + /* parse_proxy copies the socks proxy string */ + Curl_safefree(socksproxy); + if(result) + goto out; + } + + if(conn->http_proxy.host.rawalloc) { +#ifdef CURL_DISABLE_HTTP + /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */ + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; +#else + /* force this connection's protocol to become HTTP if compatible */ + if(!(conn->handler->protocol & PROTO_FAMILY_HTTP)) { + if((conn->handler->flags & PROTOPT_PROXY_AS_HTTP) && + !conn->bits.tunnel_proxy) + conn->handler = &Curl_handler_http; + else + /* if not converting to HTTP over the proxy, enforce tunneling */ + conn->bits.tunnel_proxy = TRUE; + } + conn->bits.httpproxy = TRUE; +#endif + } + else { + conn->bits.httpproxy = FALSE; /* not an HTTP proxy */ + conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ + } + + if(conn->socks_proxy.host.rawalloc) { + if(!conn->http_proxy.host.rawalloc) { + /* once a socks proxy */ + if(!conn->socks_proxy.user) { + conn->socks_proxy.user = conn->http_proxy.user; + conn->http_proxy.user = NULL; + Curl_safefree(conn->socks_proxy.passwd); + conn->socks_proxy.passwd = conn->http_proxy.passwd; + conn->http_proxy.passwd = NULL; + } + } + conn->bits.socksproxy = TRUE; + } + else + conn->bits.socksproxy = FALSE; /* not a socks proxy */ + } + else { + conn->bits.socksproxy = FALSE; + conn->bits.httpproxy = FALSE; + } + conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy; + + if(!conn->bits.proxy) { + /* we are not using the proxy after all... */ + conn->bits.proxy = FALSE; + conn->bits.httpproxy = FALSE; + conn->bits.socksproxy = FALSE; + conn->bits.proxy_user_passwd = FALSE; + conn->bits.tunnel_proxy = FALSE; + /* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need + to signal that CURLPROXY_HTTPS is not used for this connection */ + conn->http_proxy.proxytype = CURLPROXY_HTTP; + } + +out: + + free(socksproxy); + free(proxy); + return result; +} +#endif /* CURL_DISABLE_PROXY */ + +/* + * Curl_parse_login_details() + * + * This is used to parse a login string for username, password and options in + * the following formats: + * + * user + * user:password + * user:password;options + * user;options + * user;options:password + * :password + * :password;options + * ;options + * ;options:password + * + * Parameters: + * + * login [in] - login string. + * len [in] - length of the login string. + * userp [in/out] - address where a pointer to newly allocated memory + * holding the user will be stored upon completion. + * passwdp [in/out] - address where a pointer to newly allocated memory + * holding the password will be stored upon completion. + * optionsp [in/out] - OPTIONAL address where a pointer to newly allocated + * memory holding the options will be stored upon + * completion. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_parse_login_details(const char *login, const size_t len, + char **userp, char **passwdp, + char **optionsp) +{ + char *ubuf = NULL; + char *pbuf = NULL; + const char *psep = NULL; + const char *osep = NULL; + size_t ulen; + size_t plen; + size_t olen; + + DEBUGASSERT(userp); + DEBUGASSERT(passwdp); + + /* Attempt to find the password separator */ + psep = memchr(login, ':', len); + + /* Attempt to find the options separator */ + if(optionsp) + osep = memchr(login, ';', len); + + /* Calculate the portion lengths */ + ulen = (psep ? + (size_t)(osep && psep > osep ? osep - login : psep - login) : + (osep ? (size_t)(osep - login) : len)); + plen = (psep ? + (osep && osep > psep ? (size_t)(osep - psep) : + (size_t)(login + len - psep)) - 1 : 0); + olen = (osep ? + (psep && psep > osep ? (size_t)(psep - osep) : + (size_t)(login + len - osep)) - 1 : 0); + + /* Clone the user portion buffer, which can be zero length */ + ubuf = Curl_memdup0(login, ulen); + if(!ubuf) + goto error; + + /* Clone the password portion buffer */ + if(psep) { + pbuf = Curl_memdup0(&psep[1], plen); + if(!pbuf) + goto error; + } + + /* Allocate the options portion buffer */ + if(optionsp) { + char *obuf = NULL; + if(olen) { + obuf = Curl_memdup0(&osep[1], olen); + if(!obuf) + goto error; + } + *optionsp = obuf; + } + *userp = ubuf; + *passwdp = pbuf; + return CURLE_OK; +error: + free(ubuf); + free(pbuf); + return CURLE_OUT_OF_MEMORY; +} + +/************************************************************* + * Figure out the remote port number and fix it in the URL + * + * No matter if we use a proxy or not, we have to figure out the remote + * port number of various reasons. + * + * The port number embedded in the URL is replaced, if necessary. + *************************************************************/ +static CURLcode parse_remote_port(struct Curl_easy *data, + struct connectdata *conn) +{ + + if(data->set.use_port && data->state.allow_port) { + /* if set, we use this instead of the port possibly given in the URL */ + char portbuf[16]; + CURLUcode uc; + conn->remote_port = data->set.use_port; + msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port); + uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0); + if(uc) + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +/* + * Override the login details from the URL with that in the CURLOPT_USERPWD + * option or a .netrc file, if applicable. + */ +static CURLcode override_login(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLUcode uc; + char **userp = &conn->user; + char **passwdp = &conn->passwd; + char **optionsp = &conn->options; + + if(data->set.str[STRING_OPTIONS]) { + free(*optionsp); + *optionsp = strdup(data->set.str[STRING_OPTIONS]); + if(!*optionsp) + return CURLE_OUT_OF_MEMORY; + } + +#ifndef CURL_DISABLE_NETRC + if(data->set.use_netrc == CURL_NETRC_REQUIRED) { + Curl_safefree(*userp); + Curl_safefree(*passwdp); + } + conn->bits.netrc = FALSE; + if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) { + int ret; + bool url_provided = FALSE; + + if(data->state.aptr.user) { + /* there was a username in the URL. Use the URL decoded version */ + userp = &data->state.aptr.user; + url_provided = TRUE; + } + + ret = Curl_parsenetrc(conn->host.name, + userp, passwdp, + data->set.str[STRING_NETRC_FILE]); + if(ret > 0) { + infof(data, "Couldn't find host %s in the %s file; using defaults", + conn->host.name, + (data->set.str[STRING_NETRC_FILE] ? + data->set.str[STRING_NETRC_FILE] : ".netrc")); + } + else if(ret < 0) { + failf(data, ".netrc parser error"); + return CURLE_READ_ERROR; + } + else { + /* set bits.netrc TRUE to remember that we got the name from a .netrc + file, so that it is safe to use even if we followed a Location: to a + different host or similar. */ + conn->bits.netrc = TRUE; + } + if(url_provided) { + Curl_safefree(conn->user); + conn->user = strdup(*userp); + if(!conn->user) + return CURLE_OUT_OF_MEMORY; + } + /* no user was set but a password, set a blank user */ + if(!*userp && *passwdp) { + *userp = strdup(""); + if(!*userp) + return CURLE_OUT_OF_MEMORY; + } + } +#endif + + /* for updated strings, we update them in the URL */ + if(*userp) { + CURLcode result; + if(data->state.aptr.user != *userp) { + /* nothing to do then */ + result = Curl_setstropt(&data->state.aptr.user, *userp); + if(result) + return result; + } + } + if(data->state.aptr.user) { + uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user, + CURLU_URLENCODE); + if(uc) + return Curl_uc_to_curlcode(uc); + if(!*userp) { + *userp = strdup(data->state.aptr.user); + if(!*userp) + return CURLE_OUT_OF_MEMORY; + } + } + if(*passwdp) { + CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp); + if(result) + return result; + } + if(data->state.aptr.passwd) { + uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, + data->state.aptr.passwd, CURLU_URLENCODE); + if(uc) + return Curl_uc_to_curlcode(uc); + if(!*passwdp) { + *passwdp = strdup(data->state.aptr.passwd); + if(!*passwdp) + return CURLE_OUT_OF_MEMORY; + } + } + + return CURLE_OK; +} + +/* + * Set the login details so they are available in the connection + */ +static CURLcode set_login(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + const char *setuser = CURL_DEFAULT_USER; + const char *setpasswd = CURL_DEFAULT_PASSWORD; + + /* If our protocol needs a password and we have none, use the defaults */ + if((conn->handler->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) + ; + else { + setuser = ""; + setpasswd = ""; + } + /* Store the default user */ + if(!conn->user) { + conn->user = strdup(setuser); + if(!conn->user) + return CURLE_OUT_OF_MEMORY; + } + + /* Store the default password */ + if(!conn->passwd) { + conn->passwd = strdup(setpasswd); + if(!conn->passwd) + result = CURLE_OUT_OF_MEMORY; + } + + return result; +} + +/* + * Parses a "host:port" string to connect to. + * The hostname and the port may be empty; in this case, NULL is returned for + * the hostname and -1 for the port. + */ +static CURLcode parse_connect_to_host_port(struct Curl_easy *data, + const char *host, + char **hostname_result, + int *port_result) +{ + char *host_dup; + char *hostptr; + char *host_portno; + char *portptr; + int port = -1; + CURLcode result = CURLE_OK; + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) data; +#endif + + *hostname_result = NULL; + *port_result = -1; + + if(!host || !*host) + return CURLE_OK; + + host_dup = strdup(host); + if(!host_dup) + return CURLE_OUT_OF_MEMORY; + + hostptr = host_dup; + + /* start scanning for port number at this point */ + portptr = hostptr; + + /* detect and extract RFC6874-style IPv6-addresses */ + if(*hostptr == '[') { +#ifdef USE_IPV6 + char *ptr = ++hostptr; /* advance beyond the initial bracket */ + while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.'))) + ptr++; + if(*ptr == '%') { + /* There might be a zone identifier */ + if(strncmp("%25", ptr, 3)) + infof(data, "Please URL encode %% as %%25, see RFC 6874."); + ptr++; + /* Allow unreserved characters as defined in RFC 3986 */ + while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') || + (*ptr == '.') || (*ptr == '_') || (*ptr == '~'))) + ptr++; + } + if(*ptr == ']') + /* yeps, it ended nicely with a bracket as well */ + *ptr++ = '\0'; + else + infof(data, "Invalid IPv6 address format"); + portptr = ptr; + /* Note that if this did not end with a bracket, we still advanced the + * hostptr first, but I cannot see anything wrong with that as no host + * name nor a numeric can legally start with a bracket. + */ +#else + failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); + result = CURLE_NOT_BUILT_IN; + goto error; +#endif + } + + /* Get port number off server.com:1080 */ + host_portno = strchr(portptr, ':'); + if(host_portno) { + char *endp = NULL; + *host_portno = '\0'; /* cut off number from hostname */ + host_portno++; + if(*host_portno) { + long portparse = strtol(host_portno, &endp, 10); + if((endp && *endp) || (portparse < 0) || (portparse > 65535)) { + failf(data, "No valid port number in connect to host string (%s)", + host_portno); + result = CURLE_SETOPT_OPTION_SYNTAX; + goto error; + } + else + port = (int)portparse; /* we know it will fit */ + } + } + + /* now, clone the cleaned hostname */ + DEBUGASSERT(hostptr); + *hostname_result = strdup(hostptr); + if(!*hostname_result) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + *port_result = port; + +error: + free(host_dup); + return result; +} + +/* + * Parses one "connect to" string in the form: + * "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT". + */ +static CURLcode parse_connect_to_string(struct Curl_easy *data, + struct connectdata *conn, + const char *conn_to_host, + char **host_result, + int *port_result) +{ + CURLcode result = CURLE_OK; + const char *ptr = conn_to_host; + int host_match = FALSE; + int port_match = FALSE; + + *host_result = NULL; + *port_result = -1; + + if(*ptr == ':') { + /* an empty hostname always matches */ + host_match = TRUE; + ptr++; + } + else { + /* check whether the URL's hostname matches */ + size_t hostname_to_match_len; + char *hostname_to_match = aprintf("%s%s%s", + conn->bits.ipv6_ip ? "[" : "", + conn->host.name, + conn->bits.ipv6_ip ? "]" : ""); + if(!hostname_to_match) + return CURLE_OUT_OF_MEMORY; + hostname_to_match_len = strlen(hostname_to_match); + host_match = strncasecompare(ptr, hostname_to_match, + hostname_to_match_len); + free(hostname_to_match); + ptr += hostname_to_match_len; + + host_match = host_match && *ptr == ':'; + ptr++; + } + + if(host_match) { + if(*ptr == ':') { + /* an empty port always matches */ + port_match = TRUE; + ptr++; + } + else { + /* check whether the URL's port matches */ + char *ptr_next = strchr(ptr, ':'); + if(ptr_next) { + char *endp = NULL; + long port_to_match = strtol(ptr, &endp, 10); + if((endp == ptr_next) && (port_to_match == conn->remote_port)) { + port_match = TRUE; + ptr = ptr_next + 1; + } + } + } + } + + if(host_match && port_match) { + /* parse the hostname and port to connect to */ + result = parse_connect_to_host_port(data, ptr, host_result, port_result); + } + + return result; +} + +/* + * Processes all strings in the "connect to" slist, and uses the "connect + * to host" and "connect to port" of the first string that matches. + */ +static CURLcode parse_connect_to_slist(struct Curl_easy *data, + struct connectdata *conn, + struct curl_slist *conn_to_host) +{ + CURLcode result = CURLE_OK; + char *host = NULL; + int port = -1; + + while(conn_to_host && !host && port == -1) { + result = parse_connect_to_string(data, conn, conn_to_host->data, + &host, &port); + if(result) + return result; + + if(host && *host) { + conn->conn_to_host.rawalloc = host; + conn->conn_to_host.name = host; + conn->bits.conn_to_host = TRUE; + + infof(data, "Connecting to hostname: %s", host); + } + else { + /* no "connect to host" */ + conn->bits.conn_to_host = FALSE; + Curl_safefree(host); + } + + if(port >= 0) { + conn->conn_to_port = port; + conn->bits.conn_to_port = TRUE; + infof(data, "Connecting to port: %d", port); + } + else { + /* no "connect to port" */ + conn->bits.conn_to_port = FALSE; + port = -1; + } + + conn_to_host = conn_to_host->next; + } + +#ifndef CURL_DISABLE_ALTSVC + if(data->asi && !host && (port == -1) && + ((conn->handler->protocol == CURLPROTO_HTTPS) || +#ifdef DEBUGBUILD + /* allow debug builds to circumvent the HTTPS restriction */ + getenv("CURL_ALTSVC_HTTP") +#else + 0 +#endif + )) { + /* no connect_to match, try alt-svc! */ + enum alpnid srcalpnid; + bool hit; + struct altsvc *as; + const int allowed_versions = ( ALPN_h1 +#ifdef USE_HTTP2 + | ALPN_h2 +#endif +#ifdef USE_HTTP3 + | ALPN_h3 +#endif + ) & data->asi->flags; + + host = conn->host.rawalloc; +#ifdef USE_HTTP2 + /* with h2 support, check that first */ + srcalpnid = ALPN_h2; + hit = Curl_altsvc_lookup(data->asi, + srcalpnid, host, conn->remote_port, /* from */ + &as /* to */, + allowed_versions); + if(!hit) +#endif + { + srcalpnid = ALPN_h1; + hit = Curl_altsvc_lookup(data->asi, + srcalpnid, host, conn->remote_port, /* from */ + &as /* to */, + allowed_versions); + } + if(hit) { + char *hostd = strdup((char *)as->dst.host); + if(!hostd) + return CURLE_OUT_OF_MEMORY; + conn->conn_to_host.rawalloc = hostd; + conn->conn_to_host.name = hostd; + conn->bits.conn_to_host = TRUE; + conn->conn_to_port = as->dst.port; + conn->bits.conn_to_port = TRUE; + conn->bits.altused = TRUE; + infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d", + Curl_alpnid2str(srcalpnid), host, conn->remote_port, + Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); + if(srcalpnid != as->dst.alpnid) { + /* protocol version switch */ + switch(as->dst.alpnid) { + case ALPN_h1: + conn->httpversion = 11; + break; + case ALPN_h2: + conn->httpversion = 20; + break; + case ALPN_h3: + conn->transport = TRNSPRT_QUIC; + conn->httpversion = 30; + break; + default: /* should not be possible */ + break; + } + } + } + } +#endif + + return result; +} + +#ifdef USE_UNIX_SOCKETS +static CURLcode resolve_unix(struct Curl_easy *data, + struct connectdata *conn, + char *unix_path) +{ + struct Curl_dns_entry *hostaddr = NULL; + bool longpath = FALSE; + + DEBUGASSERT(unix_path); + DEBUGASSERT(conn->dns_entry == NULL); + + /* Unix domain sockets are local. The host gets ignored, just use the + * specified domain socket address. Do not cache "DNS entries". There is + * no DNS involved and we already have the filesystem path available. */ + hostaddr = calloc(1, sizeof(struct Curl_dns_entry)); + if(!hostaddr) + return CURLE_OUT_OF_MEMORY; + + hostaddr->addr = Curl_unix2addr(unix_path, &longpath, + conn->bits.abstract_unix_socket); + if(!hostaddr->addr) { + if(longpath) + /* Long paths are not supported for now */ + failf(data, "Unix socket path too long: '%s'", unix_path); + free(hostaddr); + return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY; + } + + hostaddr->inuse++; + conn->dns_entry = hostaddr; + return CURLE_OK; +} +#endif + +#ifndef CURL_DISABLE_PROXY +static CURLcode resolve_proxy(struct Curl_easy *data, + struct connectdata *conn, + bool *async) +{ + struct Curl_dns_entry *hostaddr = NULL; + struct hostname *host; + timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + int rc; + + DEBUGASSERT(conn->dns_entry == NULL); + + host = conn->bits.socksproxy ? &conn->socks_proxy.host : + &conn->http_proxy.host; + + conn->hostname_resolve = strdup(host->name); + if(!conn->hostname_resolve) + return CURLE_OUT_OF_MEMORY; + + rc = Curl_resolv_timeout(data, conn->hostname_resolve, + conn->primary.remote_port, &hostaddr, timeout_ms); + conn->dns_entry = hostaddr; + if(rc == CURLRESOLV_PENDING) + *async = TRUE; + else if(rc == CURLRESOLV_TIMEDOUT) + return CURLE_OPERATION_TIMEDOUT; + else if(!hostaddr) { + failf(data, "Couldn't resolve proxy '%s'", host->dispname); + return CURLE_COULDNT_RESOLVE_PROXY; + } + + return CURLE_OK; +} +#endif + +static CURLcode resolve_host(struct Curl_easy *data, + struct connectdata *conn, + bool *async) +{ + struct Curl_dns_entry *hostaddr = NULL; + struct hostname *connhost; + timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + int rc; + + DEBUGASSERT(conn->dns_entry == NULL); + + connhost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; + + /* If not connecting via a proxy, extract the port from the URL, if it is + * there, thus overriding any defaults that might have been set above. */ + conn->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port : + conn->remote_port; + + /* Resolve target host right on */ + conn->hostname_resolve = strdup(connhost->name); + if(!conn->hostname_resolve) + return CURLE_OUT_OF_MEMORY; + + rc = Curl_resolv_timeout(data, conn->hostname_resolve, + conn->primary.remote_port, &hostaddr, timeout_ms); + conn->dns_entry = hostaddr; + if(rc == CURLRESOLV_PENDING) + *async = TRUE; + else if(rc == CURLRESOLV_TIMEDOUT) { + failf(data, "Failed to resolve host '%s' with timeout after %" + CURL_FORMAT_TIMEDIFF_T " ms", connhost->dispname, + Curl_timediff(Curl_now(), data->progress.t_startsingle)); + return CURLE_OPERATION_TIMEDOUT; + } + else if(!hostaddr) { + failf(data, "Could not resolve host: %s", connhost->dispname); + return CURLE_COULDNT_RESOLVE_HOST; + } + + return CURLE_OK; +} + +/* Perform a fresh resolve */ +static CURLcode resolve_fresh(struct Curl_easy *data, + struct connectdata *conn, + bool *async) +{ +#ifdef USE_UNIX_SOCKETS + char *unix_path = conn->unix_domain_socket; + +#ifndef CURL_DISABLE_PROXY + if(!unix_path && conn->socks_proxy.host.name && + !strncmp(UNIX_SOCKET_PREFIX"/", + conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) + unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; +#endif + + if(unix_path) { + conn->transport = TRNSPRT_UNIX; + return resolve_unix(data, conn, unix_path); + } +#endif + +#ifndef CURL_DISABLE_PROXY + if(CONN_IS_PROXIED(conn)) + return resolve_proxy(data, conn, async); +#endif + + return resolve_host(data, conn, async); +} + +/************************************************************* + * Resolve the address of the server or proxy + *************************************************************/ +static CURLcode resolve_server(struct Curl_easy *data, + struct connectdata *conn, + bool *async) +{ + DEBUGASSERT(conn); + DEBUGASSERT(data); + + /* Resolve the name of the server or proxy */ + if(conn->bits.reuse) { + /* We are reusing the connection - no need to resolve anything, and + idnconvert_hostname() was called already in create_conn() for the reuse + case. */ + *async = FALSE; + return CURLE_OK; + } + + return resolve_fresh(data, conn, async); +} + +/* + * 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 *temp, + struct connectdata *existing) +{ + /* get the user+password information from the temp struct since it may + * be new for this request even when we reuse an existing connection */ + if(temp->user) { + /* use the new username and password though */ + 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 + existing->bits.proxy_user_passwd = temp->bits.proxy_user_passwd; + if(existing->bits.proxy_user_passwd) { + /* use the new proxy username and proxy password though */ + 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 + + /* Finding a connection for reuse in the cache matches, among other + * things on the "remote-relevant" hostname. This is not necessarily + * the authority of the URL, e.g. conn->host. For example: + * - we use a proxy (not tunneling). we want to send all requests + * that use the same proxy on this connection. + * - we have a "connect-to" setting that may redirect the hostname of + * a new request to the same remote endpoint of an existing conn. + * We want to reuse an existing conn to the remote endpoint. + * Since connection reuse does not match on conn->host necessarily, we + * switch `existing` conn to `temp` conn's host settings. + * TODO: is this correct in the case of TLS connections that have + * used the original hostname in SNI to negotiate? Do we send + * requests for another host through the different SNI? + */ + Curl_free_idnconverted_hostname(&existing->host); + Curl_free_idnconverted_hostname(&existing->conn_to_host); + Curl_safefree(existing->host.rawalloc); + 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; + + /* reuse init */ + existing->bits.reuse = TRUE; /* yes, we are reusing here */ + + Curl_conn_free(data, temp); +} + +/** + * create_conn() sets up a new connectdata struct, or reuses an already + * existing one, and resolves hostname. + * + * if this function returns CURLE_OK and *async is set to TRUE, the resolve + * response will be coming asynchronously. If *async is FALSE, the name is + * already resolved. + * + * @param data The sessionhandle pointer + * @param in_connect is set to the next connection data pointer + * @param async is set TRUE when an async DNS resolution is pending + * @see Curl_setup_conn() + * + */ + +static CURLcode create_conn(struct Curl_easy *data, + struct connectdata **in_connect, + bool *async) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn; + struct connectdata *existing = NULL; + bool reuse; + bool connections_available = TRUE; + bool force_reuse = FALSE; + bool waitpipe = FALSE; + size_t max_host_connections = Curl_multi_max_host_connections(data->multi); + size_t max_total_connections = Curl_multi_max_total_connections(data->multi); + + *async = FALSE; + *in_connect = NULL; + + /************************************************************* + * Check input data + *************************************************************/ + if(!data->state.url) { + result = CURLE_URL_MALFORMAT; + goto out; + } + + /* First, split up the current URL in parts so that we can use the + parts for checking against the already present connections. In order + to not have to modify everything at once, we allocate a temporary + connection data struct and fill in for comparison purposes. */ + conn = allocate_conn(data); + + if(!conn) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + /* We must set the return variable as soon as possible, so that our + parent can cleanup any possible allocs we may have done before + any failure */ + *in_connect = conn; + + result = parseurlandfillconn(data, conn); + if(result) + goto out; + + if(data->set.str[STRING_SASL_AUTHZID]) { + conn->sasl_authzid = strdup(data->set.str[STRING_SASL_AUTHZID]); + if(!conn->sasl_authzid) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + + if(data->set.str[STRING_BEARER]) { + conn->oauth_bearer = strdup(data->set.str[STRING_BEARER]); + if(!conn->oauth_bearer) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + +#ifdef USE_UNIX_SOCKETS + if(data->set.str[STRING_UNIX_SOCKET_PATH]) { + conn->unix_domain_socket = strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); + if(!conn->unix_domain_socket) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + conn->bits.abstract_unix_socket = data->set.abstract_unix_socket; + } +#endif + + /* After the unix socket init but before the proxy vars are used, parse and + initialize the proxy vars */ +#ifndef CURL_DISABLE_PROXY + result = create_conn_helper_init_proxy(data, conn); + if(result) + goto out; + + /************************************************************* + * If the protocol is using SSL and HTTP proxy is used, we set + * the tunnel_proxy bit. + *************************************************************/ + if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy) + conn->bits.tunnel_proxy = TRUE; +#endif + + /************************************************************* + * Figure out the remote port number and fix it in the URL + *************************************************************/ + result = parse_remote_port(data, conn); + if(result) + goto out; + + /* Check for overridden login details and set them accordingly so that + they are known when protocol->setup_connection is called! */ + result = override_login(data, conn); + if(result) + goto out; + + result = set_login(data, conn); /* default credentials */ + if(result) + goto out; + + /************************************************************* + * Process the "connect to" linked list of hostname/port mappings. + * Do this after the remote port number has been fixed in the URL. + *************************************************************/ + result = parse_connect_to_slist(data, conn, data->set.connect_to); + if(result) + goto out; + + /************************************************************* + * IDN-convert the proxy hostnames + *************************************************************/ +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy) { + result = Curl_idnconvert_hostname(&conn->http_proxy.host); + if(result) + return result; + } + if(conn->bits.socksproxy) { + result = Curl_idnconvert_hostname(&conn->socks_proxy.host); + if(result) + return result; + } +#endif + if(conn->bits.conn_to_host) { + result = Curl_idnconvert_hostname(&conn->conn_to_host); + if(result) + return result; + } + + /************************************************************* + * Check whether the host and the "connect to host" are equal. + * Do this after the hostnames have been IDN-converted. + *************************************************************/ + if(conn->bits.conn_to_host && + strcasecompare(conn->conn_to_host.name, conn->host.name)) { + conn->bits.conn_to_host = FALSE; + } + + /************************************************************* + * Check whether the port and the "connect to port" are equal. + * Do this after the remote port number has been fixed in the URL. + *************************************************************/ + if(conn->bits.conn_to_port && conn->conn_to_port == conn->remote_port) { + conn->bits.conn_to_port = FALSE; + } + +#ifndef CURL_DISABLE_PROXY + /************************************************************* + * If the "connect to" feature is used with an HTTP proxy, + * we set the tunnel_proxy bit. + *************************************************************/ + if((conn->bits.conn_to_host || conn->bits.conn_to_port) && + conn->bits.httpproxy) + conn->bits.tunnel_proxy = TRUE; +#endif + + /************************************************************* + * Setup internals depending on protocol. Needs to be done after + * we figured out what/if proxy to use. + *************************************************************/ + result = setup_connection_internals(data, conn); + if(result) + goto out; + + /*********************************************************************** + * file: is a special case in that it does not need a network connection + ***********************************************************************/ +#ifndef CURL_DISABLE_FILE + if(conn->handler->flags & PROTOPT_NONETWORK) { + bool done; + /* this is supposed to be the connect function so we better at least check + that the file is present here! */ + DEBUGASSERT(conn->handler->connect_it); + Curl_persistconninfo(data, conn, NULL); + result = conn->handler->connect_it(data, &done); + + /* Setup a "faked" transfer that will do nothing */ + if(!result) { + Curl_attach_connection(data, conn); + result = Curl_conncache_add_conn(data); + if(result) + goto out; + + /* + * Setup whatever necessary for a resumed transfer + */ + result = setup_range(data); + if(result) { + DEBUGASSERT(conn->handler->done); + /* we ignore the return code for the protocol-specific DONE */ + (void)conn->handler->done(data, result, FALSE); + goto out; + } + Curl_xfer_setup_nop(data); + } + + /* since we skip do_init() */ + Curl_init_do(data, conn); + + goto out; + } +#endif + + /* Setup filter for network connections */ + conn->recv[FIRSTSOCKET] = Curl_cf_recv; + conn->send[FIRSTSOCKET] = Curl_cf_send; + conn->recv[SECONDARYSOCKET] = Curl_cf_recv; + conn->send[SECONDARYSOCKET] = Curl_cf_send; + conn->bits.tcp_fastopen = data->set.tcp_fastopen; + + /* Complete the easy's SSL configuration for connection cache matching */ + result = Curl_ssl_easy_config_complete(data); + if(result) + goto out; + + prune_dead_connections(data); + + /************************************************************* + * Check the current list of connections to see if we can + * reuse an already existing one or if we have to create a + * new one. + *************************************************************/ + + DEBUGASSERT(conn->user); + DEBUGASSERT(conn->passwd); + + /* reuse_fresh is TRUE if we are told to use a new connection by force, but + we only acknowledge this option if this is not a reused connection + already (which happens due to follow-location or during an HTTP + authentication phase). CONNECT_ONLY transfers also refuse reuse. */ + if((data->set.reuse_fresh && !data->state.followlocation) || + data->set.connect_only) + reuse = FALSE; + else + reuse = ConnectionExists(data, conn, &existing, &force_reuse, &waitpipe); + + if(reuse) { + /* + * We already have a connection for this, we got the former connection in + * `existing` and thus we need to cleanup the one we just + * allocated before we can move along and use `existing`. + */ + reuse_conn(data, conn, existing); + conn = existing; + *in_connect = conn; + +#ifndef CURL_DISABLE_PROXY + infof(data, "Re-using existing connection with %s %s", + conn->bits.proxy?"proxy":"host", + conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : + conn->http_proxy.host.name ? conn->http_proxy.host.dispname : + conn->host.dispname); +#else + infof(data, "Re-using existing connection with host %s", + conn->host.dispname); +#endif + } + else { + /* We have decided that we want a new connection. However, we may not + be able to do that if we have reached the limit of how many + connections we are allowed to open. */ + + if(conn->handler->flags & PROTOPT_ALPN) { + /* The protocol wants it, so set the bits if enabled in the easy handle + (default) */ + if(data->set.ssl_enable_alpn) + conn->bits.tls_enable_alpn = TRUE; + } + + if(waitpipe) + /* There is a connection that *might* become usable for multiplexing + "soon", and we wait for that */ + connections_available = FALSE; + else { + /* this gets a lock on the conncache */ + struct connectbundle *bundle = + Curl_conncache_find_bundle(data, conn, data->state.conn_cache); + + if(max_host_connections > 0 && bundle && + (bundle->num_connections >= max_host_connections)) { + struct connectdata *conn_candidate; + + /* The bundle is full. Extract the oldest connection. */ + conn_candidate = Curl_conncache_extract_bundle(data, bundle); + CONNCACHE_UNLOCK(data); + + if(conn_candidate) + Curl_disconnect(data, conn_candidate, FALSE); + else { + infof(data, "No more connections allowed to host: %zu", + max_host_connections); + connections_available = FALSE; + } + } + else + CONNCACHE_UNLOCK(data); + + } + + if(connections_available && + (max_total_connections > 0) && + (Curl_conncache_size(data) >= max_total_connections)) { + struct connectdata *conn_candidate; + + /* The cache is full. Let's see if we can kill a connection. */ + conn_candidate = Curl_conncache_extract_oldest(data); + if(conn_candidate) + Curl_disconnect(data, conn_candidate, FALSE); + else +#ifndef CURL_DISABLE_DOH + if(data->set.dohfor) + infof(data, "Allowing DoH to override max connection limit"); + else +#endif + { + infof(data, "No connections available in cache"); + connections_available = FALSE; + } + } + + if(!connections_available) { + infof(data, "No connections available."); + + Curl_conn_free(data, conn); + *in_connect = NULL; + + result = CURLE_NO_CONNECTION_AVAILABLE; + goto out; + } + else { + /* + * This is a brand new connection, so let's store it in the connection + * cache of ours! + */ + result = Curl_ssl_conn_config_init(data, conn); + if(result) { + DEBUGF(fprintf(stderr, "Error: init connection ssl config\n")); + goto out; + } + + Curl_attach_connection(data, conn); + result = Curl_conncache_add_conn(data); + if(result) + goto out; + } + +#if defined(USE_NTLM) + /* If NTLM is requested in a part of this connection, make sure we do not + assume the state is fine as this is a fresh connection and NTLM is + connection based. */ + if((data->state.authhost.picked & CURLAUTH_NTLM) && + data->state.authhost.done) { + infof(data, "NTLM picked AND auth done set, clear picked"); + data->state.authhost.picked = CURLAUTH_NONE; + data->state.authhost.done = FALSE; + } + + if((data->state.authproxy.picked & CURLAUTH_NTLM) && + data->state.authproxy.done) { + infof(data, "NTLM-proxy picked AND auth done set, clear picked"); + data->state.authproxy.picked = CURLAUTH_NONE; + data->state.authproxy.done = FALSE; + } +#endif + } + + /* Setup and init stuff before DO starts, in preparing for the transfer. */ + Curl_init_do(data, conn); + + /* + * Setup whatever necessary for a resumed transfer + */ + result = setup_range(data); + if(result) + goto out; + + /* Continue connectdata initialization here. */ + + /************************************************************* + * Resolve the address of the server or proxy + *************************************************************/ + result = resolve_server(data, conn, async); + if(result) + goto out; + + /* Everything general done, inform filters that they need + * to prepare for a data transfer. + */ + result = Curl_conn_ev_data_setup(data); + +out: + return result; +} + +/* Curl_setup_conn() is called after the name resolve initiated in + * create_conn() is all done. + * + * Curl_setup_conn() also handles reused connections + */ +CURLcode Curl_setup_conn(struct Curl_easy *data, + bool *protocol_done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + + Curl_pgrsTime(data, TIMER_NAMELOOKUP); + + if(conn->handler->flags & PROTOPT_NONETWORK) { + /* nothing to setup when not using a network */ + *protocol_done = TRUE; + return result; + } + +#ifndef CURL_DISABLE_PROXY + /* set proxy_connect_closed to false unconditionally already here since it + is used strictly to provide extra information to a parent function in the + case of proxy CONNECT failures and we must make sure we do not have it + lingering set from a previous invoke */ + conn->bits.proxy_connect_closed = FALSE; +#endif + +#ifdef CURL_DO_LINEEND_CONV + data->state.crlf_conversions = 0; /* reset CRLF conversion counter */ +#endif /* CURL_DO_LINEEND_CONV */ + + /* 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(!conn->bits.reuse) + result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry, + CURL_CF_SSL_DEFAULT); + if(!result) + result = Curl_headers_init(data); + + /* not sure we need this flag to be passed around any more */ + *protocol_done = FALSE; + return result; +} + +CURLcode Curl_connect(struct Curl_easy *data, + bool *asyncp, + bool *protocol_done) +{ + CURLcode result; + struct connectdata *conn; + + *asyncp = FALSE; /* assume synchronous resolves by default */ + + /* Set the request to virgin state based on transfer settings */ + Curl_req_hard_reset(&data->req, data); + + /* call the stuff that needs to be called */ + result = create_conn(data, &conn, asyncp); + + if(!result) { + if(CONN_INUSE(conn) > 1) + /* multiplexed */ + *protocol_done = TRUE; + else if(!*asyncp) { + /* DNS resolution is done: that is either because this is a reused + connection, in which case DNS was unnecessary, or because DNS + really did finish already (synch resolver/fast async resolve) */ + result = Curl_setup_conn(data, protocol_done); + } + } + + if(result == CURLE_NO_CONNECTION_AVAILABLE) { + return result; + } + else if(result && conn) { + /* We are not allowed to return failure with memory left allocated in the + connectdata struct, free those here */ + Curl_detach_connection(data); + Curl_conncache_remove_conn(data, conn, TRUE); + Curl_disconnect(data, conn, TRUE); + } + + return result; +} + +/* + * Curl_init_do() inits the readwrite session. This is inited each time (in + * the DO function before the protocol-specific DO functions are invoked) for + * a transfer, sometimes multiple times on the same Curl_easy. Make sure + * nothing in here depends on stuff that are setup dynamically for the + * transfer. + * + * Allow this function to get called with 'conn' set to NULL. + */ + +CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) +{ + /* if this is a pushed stream, we need this: */ + CURLcode result; + + if(conn) { + conn->bits.do_more = FALSE; /* by default there is no curl_do_more() to + use */ + /* if the protocol used does not support wildcards, switch it off */ + if(data->state.wildcardmatch && + !(conn->handler->flags & PROTOPT_WILDCARD)) + data->state.wildcardmatch = FALSE; + } + + data->state.done = FALSE; /* *_done() is not called yet */ + + if(data->req.no_body) + /* in HTTP lingo, no body means using the HEAD request... */ + data->state.httpreq = HTTPREQ_HEAD; + + result = Curl_req_start(&data->req, data); + if(!result) { + Curl_speedinit(data); + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + } + return result; +} + +#if defined(USE_HTTP2) || defined(USE_HTTP3) + +#ifdef USE_NGHTTP2 + +static void priority_remove_child(struct Curl_easy *parent, + struct Curl_easy *child) +{ + struct Curl_data_prio_node **pnext = &parent->set.priority.children; + struct Curl_data_prio_node *pnode = parent->set.priority.children; + + DEBUGASSERT(child->set.priority.parent == parent); + while(pnode && pnode->data != child) { + pnext = &pnode->next; + pnode = pnode->next; + } + + DEBUGASSERT(pnode); + if(pnode) { + *pnext = pnode->next; + free(pnode); + } + + child->set.priority.parent = 0; + child->set.priority.exclusive = FALSE; +} + +CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, + struct Curl_easy *child, + bool exclusive) +{ + if(child->set.priority.parent) { + priority_remove_child(child->set.priority.parent, child); + } + + if(parent) { + struct Curl_data_prio_node **tail; + struct Curl_data_prio_node *pnode; + + pnode = calloc(1, sizeof(*pnode)); + if(!pnode) + return CURLE_OUT_OF_MEMORY; + pnode->data = child; + + if(parent->set.priority.children && exclusive) { + /* exclusive: move all existing children underneath the new child */ + struct Curl_data_prio_node *node = parent->set.priority.children; + while(node) { + node->data->set.priority.parent = child; + node = node->next; + } + + tail = &child->set.priority.children; + while(*tail) + tail = &(*tail)->next; + + DEBUGASSERT(!*tail); + *tail = parent->set.priority.children; + parent->set.priority.children = 0; + } + + tail = &parent->set.priority.children; + while(*tail) { + (*tail)->data->set.priority.exclusive = FALSE; + tail = &(*tail)->next; + } + + DEBUGASSERT(!*tail); + *tail = pnode; + } + + child->set.priority.parent = parent; + child->set.priority.exclusive = exclusive; + return CURLE_OK; +} + +#endif /* USE_NGHTTP2 */ + +#ifdef USE_NGHTTP2 +static void data_priority_cleanup(struct Curl_easy *data) +{ + while(data->set.priority.children) { + struct Curl_easy *tmp = data->set.priority.children->data; + priority_remove_child(data, tmp); + if(data->set.priority.parent) + Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE); + } + + if(data->set.priority.parent) + priority_remove_child(data->set.priority.parent, data); +} +#endif + +void Curl_data_priority_clear_state(struct Curl_easy *data) +{ + memset(&data->state.priority, 0, sizeof(data->state.priority)); +} + +#endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */ diff --git a/third_party/curl/lib/url.h b/third_party/curl/lib/url.h new file mode 100644 index 0000000000..55f9b15855 --- /dev/null +++ b/third_party/curl/lib/url.h @@ -0,0 +1,82 @@ +#ifndef HEADER_CURL_URL_H +#define HEADER_CURL_URL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +/* + * Prototypes for library-wide functions provided by url.c + */ + +CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn); +CURLcode Curl_open(struct Curl_easy **curl); +CURLcode Curl_init_userdefined(struct Curl_easy *data); + +void Curl_freeset(struct Curl_easy *data); +CURLcode Curl_uc_to_curlcode(CURLUcode uc); +CURLcode Curl_close(struct Curl_easy **datap); /* opposite of curl_open() */ +CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect); +void Curl_disconnect(struct Curl_easy *data, + struct connectdata *, bool aborted); +CURLcode Curl_setup_conn(struct Curl_easy *data, + bool *protocol_done); +void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn); +CURLcode Curl_parse_login_details(const char *login, const size_t len, + char **userptr, char **passwdptr, + char **optionsptr); + +/* Get protocol handler for a URI scheme + * @param scheme URI scheme, case-insensitive + * @return NULL of handler not found + */ +const struct Curl_handler *Curl_get_scheme_handler(const char *scheme); +const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, + size_t len); + +#define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ +#define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless + specified */ + +#ifdef CURL_DISABLE_VERBOSE_STRINGS +#define Curl_verboseconnect(x,y,z) Curl_nop_stmt +#else +void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn, + int sockindex); +#endif + +#if defined(USE_HTTP2) || defined(USE_HTTP3) +void Curl_data_priority_clear_state(struct Curl_easy *data); +#else +#define Curl_data_priority_clear_state(x) +#endif /* !(defined(USE_HTTP2) || defined(USE_HTTP3)) */ + +#ifdef USE_NGHTTP2 +CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, + struct Curl_easy *child, + bool exclusive); +#else +#define Curl_data_priority_add_child(x, y, z) CURLE_NOT_BUILT_IN +#endif + +#endif /* HEADER_CURL_URL_H */ diff --git a/third_party/curl/lib/urlapi-int.h b/third_party/curl/lib/urlapi-int.h new file mode 100644 index 0000000000..fcffab2e95 --- /dev/null +++ b/third_party/curl/lib/urlapi-int.h @@ -0,0 +1,38 @@ +#ifndef HEADER_CURL_URLAPI_INT_H +#define HEADER_CURL_URLAPI_INT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, + bool guess_scheme); + +CURLUcode Curl_url_set_authority(CURLU *u, const char *authority); + +#ifdef UNITTESTS +UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme); +#endif + +#endif /* HEADER_CURL_URLAPI_INT_H */ diff --git a/third_party/curl/lib/urlapi.c b/third_party/curl/lib/urlapi.c new file mode 100644 index 0000000000..b71be38470 --- /dev/null +++ b/third_party/curl/lib/urlapi.c @@ -0,0 +1,2005 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "urldata.h" +#include "urlapi-int.h" +#include "strcase.h" +#include "url.h" +#include "escape.h" +#include "curl_ctype.h" +#include "inet_pton.h" +#include "inet_ntop.h" +#include "strdup.h" +#include "idn.h" +#include "curl_memrchr.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + /* MSDOS/Windows style drive prefix, eg c: in c:foo */ +#define STARTS_WITH_DRIVE_PREFIX(str) \ + ((('a' <= str[0] && str[0] <= 'z') || \ + ('A' <= str[0] && str[0] <= 'Z')) && \ + (str[1] == ':')) + + /* MSDOS/Windows style drive prefix, optionally with + * a '|' instead of ':', followed by a slash or NUL */ +#define STARTS_WITH_URL_DRIVE_PREFIX(str) \ + ((('a' <= (str)[0] && (str)[0] <= 'z') || \ + ('A' <= (str)[0] && (str)[0] <= 'Z')) && \ + ((str)[1] == ':' || (str)[1] == '|') && \ + ((str)[2] == '/' || (str)[2] == '\\' || (str)[2] == 0)) + +/* scheme is not URL encoded, the longest libcurl supported ones are... */ +#define MAX_SCHEME_LEN 40 + +/* + * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make + * sure we have _some_ value for AF_INET6 without polluting our fake value + * everywhere. + */ +#if !defined(USE_IPV6) && !defined(AF_INET6) +#define AF_INET6 (AF_INET + 1) +#endif + +/* Internal representation of CURLU. Point to URL-encoded strings. */ +struct Curl_URL { + char *scheme; + char *user; + char *password; + char *options; /* IMAP only? */ + char *host; + char *zoneid; /* for numerical IPv6 addresses */ + char *port; + char *path; + char *query; + char *fragment; + unsigned short portnum; /* the numerical version (if 'port' is set) */ + BIT(query_present); /* to support blank */ + BIT(fragment_present); /* to support blank */ + BIT(guessed_scheme); /* when a URL without scheme is parsed */ +}; + +#define DEFAULT_SCHEME "https" + +static void free_urlhandle(struct Curl_URL *u) +{ + free(u->scheme); + free(u->user); + free(u->password); + free(u->options); + free(u->host); + free(u->zoneid); + free(u->port); + free(u->path); + free(u->query); + free(u->fragment); +} + +/* + * Find the separator at the end of the hostname, or the '?' in cases like + * http://www.example.com?id=2380 + */ +static const char *find_host_sep(const char *url) +{ + const char *sep; + const char *query; + + /* Find the start of the hostname */ + sep = strstr(url, "//"); + if(!sep) + sep = url; + else + sep += 2; + + query = strchr(sep, '?'); + sep = strchr(sep, '/'); + + if(!sep) + sep = url + strlen(url); + + if(!query) + query = url + strlen(url); + + return sep < query ? sep : query; +} + +/* convert CURLcode to CURLUcode */ +#define cc2cu(x) ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE : \ + CURLUE_OUT_OF_MEMORY) +/* + * Decide whether a character in a URL must be escaped. + */ +#define urlchar_needs_escaping(c) (!(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c))) + +static const char hexdigits[] = "0123456789abcdef"; +/* urlencode_str() writes data into an output dynbuf and URL-encodes the + * spaces in the source URL accordingly. + * + * URL encoding should be skipped for hostnames, otherwise IDN resolution + * will fail. + */ +static CURLUcode urlencode_str(struct dynbuf *o, const char *url, + size_t len, bool relative, + bool query) +{ + /* we must add this with whitespace-replacing */ + bool left = !query; + const unsigned char *iptr; + const unsigned char *host_sep = (const unsigned char *) url; + CURLcode result; + + if(!relative) + host_sep = (const unsigned char *) find_host_sep(url); + + for(iptr = (unsigned char *)url; /* read from here */ + len; iptr++, len--) { + + if(iptr < host_sep) { + result = Curl_dyn_addn(o, iptr, 1); + if(result) + return cc2cu(result); + continue; + } + + if(*iptr == ' ') { + if(left) + result = Curl_dyn_addn(o, "%20", 3); + else + result = Curl_dyn_addn(o, "+", 1); + if(result) + return cc2cu(result); + continue; + } + + if(*iptr == '?') + left = FALSE; + + if(urlchar_needs_escaping(*iptr)) { + char out[3]={'%'}; + out[1] = hexdigits[*iptr>>4]; + out[2] = hexdigits[*iptr & 0xf]; + result = Curl_dyn_addn(o, out, 3); + } + else + result = Curl_dyn_addn(o, iptr, 1); + if(result) + return cc2cu(result); + } + + return CURLUE_OK; +} + +/* + * Returns the length of the scheme if the given URL is absolute (as opposed + * to relative). Stores the scheme in the buffer if TRUE and 'buf' is + * non-NULL. The buflen must be larger than MAX_SCHEME_LEN if buf is set. + * + * If 'guess_scheme' is TRUE, it means the URL might be provided without + * scheme. + */ +size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, + bool guess_scheme) +{ + size_t i = 0; + DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN)); + (void)buflen; /* only used in debug-builds */ + if(buf) + buf[0] = 0; /* always leave a defined value in buf */ +#ifdef _WIN32 + if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url)) + return 0; +#endif + if(ISALPHA(url[0])) + for(i = 1; i < MAX_SCHEME_LEN; ++i) { + char s = url[i]; + if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) { + /* RFC 3986 3.1 explains: + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + */ + } + else { + break; + } + } + if(i && (url[i] == ':') && ((url[i + 1] == '/') || !guess_scheme)) { + /* If this does not guess scheme, the scheme always ends with the colon so + that this also detects data: URLs etc. In guessing mode, data: could + be the hostname "data" with a specified port number. */ + + /* the length of the scheme is the name part only */ + size_t len = i; + if(buf) { + Curl_strntolower(buf, url, i); + buf[i] = 0; + } + return len; + } + return 0; +} + +/* + * Concatenate a relative URL to a base URL making it absolute. + * URL-encodes any spaces. + * The returned pointer must be freed by the caller unless NULL + * (returns NULL on out of memory). + * + * Note that this function destroys the 'base' string. + */ +static CURLcode concat_url(char *base, const char *relurl, char **newurl) +{ + /*** + TRY to append this new path to the old URL + to the right of the host part. Oh crap, this is doomed to cause + problems in the future... + */ + struct dynbuf newest; + char *protsep; + char *pathsep; + bool host_changed = FALSE; + const char *useurl = relurl; + CURLcode result = CURLE_OK; + CURLUcode uc; + bool skip_slash = FALSE; + *newurl = NULL; + + /* protsep points to the start of the hostname */ + protsep = strstr(base, "//"); + if(!protsep) + protsep = base; + else + protsep += 2; /* pass the slashes */ + + if('/' != relurl[0]) { + int level = 0; + + /* First we need to find out if there is a ?-letter in the URL, + and cut it and the right-side of that off */ + pathsep = strchr(protsep, '?'); + if(pathsep) + *pathsep = 0; + + /* we have a relative path to append to the last slash if there is one + available, or the new URL is just a query string (starts with a '?') or + a fragment (starts with '#') we append the new one at the end of the + current URL */ + if((useurl[0] != '?') && (useurl[0] != '#')) { + pathsep = strrchr(protsep, '/'); + if(pathsep) + *pathsep = 0; + + /* Check if there is any slash after the hostname, and if so, remember + that position instead */ + pathsep = strchr(protsep, '/'); + if(pathsep) + protsep = pathsep + 1; + else + protsep = NULL; + + /* now deal with one "./" or any amount of "../" in the newurl + and act accordingly */ + + if((useurl[0] == '.') && (useurl[1] == '/')) + useurl += 2; /* just skip the "./" */ + + while((useurl[0] == '.') && + (useurl[1] == '.') && + (useurl[2] == '/')) { + level++; + useurl += 3; /* pass the "../" */ + } + + if(protsep) { + while(level--) { + /* cut off one more level from the right of the original URL */ + pathsep = strrchr(protsep, '/'); + if(pathsep) + *pathsep = 0; + else { + *protsep = 0; + break; + } + } + } + } + else + skip_slash = TRUE; + } + else { + /* We got a new absolute path for this server */ + + if(relurl[1] == '/') { + /* the new URL starts with //, just keep the protocol part from the + original one */ + *protsep = 0; + useurl = &relurl[2]; /* we keep the slashes from the original, so we + skip the new ones */ + host_changed = TRUE; + } + else { + /* cut off the original URL from the first slash, or deal with URLs + without slash */ + pathsep = strchr(protsep, '/'); + if(pathsep) { + /* When people use badly formatted URLs, such as + "http://www.example.com?dir=/home/daniel" we must not use the first + slash, if there is a ?-letter before it! */ + char *sep = strchr(protsep, '?'); + if(sep && (sep < pathsep)) + pathsep = sep; + *pathsep = 0; + } + else { + /* There was no slash. Now, since we might be operating on a badly + formatted URL, such as "http://www.example.com?id=2380" which does + not use a slash separator as it is supposed to, we need to check + for a ?-letter as well! */ + pathsep = strchr(protsep, '?'); + if(pathsep) + *pathsep = 0; + } + } + } + + Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH); + + /* copy over the root URL part */ + result = Curl_dyn_add(&newest, base); + if(result) + return result; + + /* check if we need to append a slash */ + if(('/' == useurl[0]) || (protsep && !*protsep) || skip_slash) + ; + else { + result = Curl_dyn_addn(&newest, "/", 1); + if(result) + return result; + } + + /* then append the new piece on the right side */ + uc = urlencode_str(&newest, useurl, strlen(useurl), !host_changed, + FALSE); + if(uc) + return (uc == CURLUE_TOO_LARGE) ? CURLE_TOO_LARGE : CURLE_OUT_OF_MEMORY; + + *newurl = Curl_dyn_ptr(&newest); + return CURLE_OK; +} + +/* scan for byte values <= 31, 127 and sometimes space */ +static CURLUcode junkscan(const char *url, size_t *urllen, unsigned int flags) +{ + static const char badbytes[]={ + /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x7f, 0x00 /* null-terminate */ + }; + size_t n = strlen(url); + size_t nfine; + + if(n > CURL_MAX_INPUT_LENGTH) + /* excessive input length */ + return CURLUE_MALFORMED_INPUT; + + nfine = strcspn(url, badbytes); + if((nfine != n) || + (!(flags & CURLU_ALLOW_SPACE) && strchr(url, ' '))) + return CURLUE_MALFORMED_INPUT; + + *urllen = n; + return CURLUE_OK; +} + +/* + * parse_hostname_login() + * + * Parse the login details (username, password and options) from the URL and + * strip them out of the hostname + * + */ +static CURLUcode parse_hostname_login(struct Curl_URL *u, + const char *login, + size_t len, + unsigned int flags, + size_t *offset) /* to the hostname */ +{ + CURLUcode result = CURLUE_OK; + CURLcode ccode; + char *userp = NULL; + char *passwdp = NULL; + char *optionsp = NULL; + const struct Curl_handler *h = NULL; + + /* At this point, we assume all the other special cases have been taken + * care of, so the host is at most + * + * [user[:password][;options]]@]hostname + * + * We need somewhere to put the embedded details, so do that first. + */ + char *ptr; + + DEBUGASSERT(login); + + *offset = 0; + ptr = memchr(login, '@', len); + if(!ptr) + goto out; + + /* We will now try to extract the + * possible login information in a string like: + * ftp://user:password@ftp.my.site:8021/README */ + ptr++; + + /* if this is a known scheme, get some details */ + if(u->scheme) + h = Curl_get_scheme_handler(u->scheme); + + /* We could use the login information in the URL so extract it. Only parse + options if the handler says we should. Note that 'h' might be NULL! */ + ccode = Curl_parse_login_details(login, ptr - login - 1, + &userp, &passwdp, + (h && (h->flags & PROTOPT_URLOPTIONS)) ? + &optionsp:NULL); + if(ccode) { + result = CURLUE_BAD_LOGIN; + goto out; + } + + if(userp) { + if(flags & CURLU_DISALLOW_USER) { + /* Option DISALLOW_USER is set and URL contains username. */ + result = CURLUE_USER_NOT_ALLOWED; + goto out; + } + free(u->user); + u->user = userp; + } + + if(passwdp) { + free(u->password); + u->password = passwdp; + } + + if(optionsp) { + free(u->options); + u->options = optionsp; + } + + /* the hostname starts at this offset */ + *offset = ptr - login; + return CURLUE_OK; + +out: + + free(userp); + free(passwdp); + free(optionsp); + u->user = NULL; + u->password = NULL; + u->options = NULL; + + return result; +} + +UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme) +{ + char *portptr; + char *hostname = Curl_dyn_ptr(host); + /* + * Find the end of an IPv6 address on the ']' ending bracket. + */ + if(hostname[0] == '[') { + portptr = strchr(hostname, ']'); + if(!portptr) + return CURLUE_BAD_IPV6; + portptr++; + /* this is a RFC2732-style specified IP-address */ + if(*portptr) { + if(*portptr != ':') + return CURLUE_BAD_PORT_NUMBER; + } + else + portptr = NULL; + } + else + portptr = strchr(hostname, ':'); + + if(portptr) { + char *rest = NULL; + unsigned long port; + size_t keep = portptr - hostname; + + /* Browser behavior adaptation. If there is a colon with no digits after, + just cut off the name there which makes us ignore the colon and just + use the default port. Firefox, Chrome and Safari all do that. + + Do not do it if the URL has no scheme, to make something that looks like + a scheme not work! + */ + Curl_dyn_setlen(host, keep); + portptr++; + if(!*portptr) + return has_scheme ? CURLUE_OK : CURLUE_BAD_PORT_NUMBER; + + if(!ISDIGIT(*portptr)) + return CURLUE_BAD_PORT_NUMBER; + + errno = 0; + port = strtoul(portptr, &rest, 10); /* Port number must be decimal */ + + if(errno || (port > 0xffff) || *rest) + return CURLUE_BAD_PORT_NUMBER; + + u->portnum = (unsigned short) port; + /* generate a new port number string to get rid of leading zeroes etc */ + free(u->port); + u->port = aprintf("%ld", port); + if(!u->port) + return CURLUE_OUT_OF_MEMORY; + } + + return CURLUE_OK; +} + +/* this assumes 'hostname' now starts with [ */ +static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, + size_t hlen) /* length of hostname */ +{ + size_t len; + DEBUGASSERT(*hostname == '['); + if(hlen < 4) /* '[::]' is the shortest possible valid string */ + return CURLUE_BAD_IPV6; + hostname++; + hlen -= 2; + + /* only valid IPv6 letters are ok */ + len = strspn(hostname, "0123456789abcdefABCDEF:."); + + if(hlen != len) { + hlen = len; + if(hostname[len] == '%') { + /* this could now be '%[zone id]' */ + char zoneid[16]; + int i = 0; + char *h = &hostname[len + 1]; + /* pass '25' if present and is a URL encoded percent sign */ + if(!strncmp(h, "25", 2) && h[2] && (h[2] != ']')) + h += 2; + while(*h && (*h != ']') && (i < 15)) + zoneid[i++] = *h++; + if(!i || (']' != *h)) + return CURLUE_BAD_IPV6; + zoneid[i] = 0; + u->zoneid = strdup(zoneid); + if(!u->zoneid) + return CURLUE_OUT_OF_MEMORY; + hostname[len] = ']'; /* insert end bracket */ + hostname[len + 1] = 0; /* terminate the hostname */ + } + else + return CURLUE_BAD_IPV6; + /* hostname is fine */ + } + + /* Check the IPv6 address. */ + { + char dest[16]; /* fits a binary IPv6 address */ + char norm[MAX_IPADR_LEN]; + hostname[hlen] = 0; /* end the address there */ + if(1 != Curl_inet_pton(AF_INET6, hostname, dest)) + return CURLUE_BAD_IPV6; + + /* check if it can be done shorter */ + if(Curl_inet_ntop(AF_INET6, dest, norm, sizeof(norm)) && + (strlen(norm) < hlen)) { + strcpy(hostname, norm); + hlen = strlen(norm); + hostname[hlen + 1] = 0; + } + hostname[hlen] = ']'; /* restore ending bracket */ + } + return CURLUE_OK; +} + +static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, + size_t hlen) /* length of hostname */ +{ + size_t len; + DEBUGASSERT(hostname); + + if(!hlen) + return CURLUE_NO_HOST; + else if(hostname[0] == '[') + return ipv6_parse(u, hostname, hlen); + else { + /* letters from the second string are not ok */ + len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%"); + if(hlen != len) + /* hostname with bad content */ + return CURLUE_BAD_HOSTNAME; + } + return CURLUE_OK; +} + +/* + * Handle partial IPv4 numerical addresses and different bases, like + * '16843009', '0x7f', '0x7f.1' '0177.1.1.1' etc. + * + * If the given input string is syntactically wrong IPv4 or any part for + * example is too big, this function returns HOST_NAME. + * + * Output the "normalized" version of that input string in plain quad decimal + * integers. + * + * Returns the host type. + */ + +#define HOST_ERROR -1 /* out of memory */ + +#define HOST_NAME 1 +#define HOST_IPV4 2 +#define HOST_IPV6 3 + +static int ipv4_normalize(struct dynbuf *host) +{ + bool done = FALSE; + int n = 0; + const char *c = Curl_dyn_ptr(host); + unsigned long parts[4] = {0, 0, 0, 0}; + CURLcode result = CURLE_OK; + + if(*c == '[') + return HOST_IPV6; + + errno = 0; /* for strtoul */ + while(!done) { + char *endp = NULL; + unsigned long l; + if(!ISDIGIT(*c)) + /* most importantly this does not allow a leading plus or minus */ + return HOST_NAME; + l = strtoul(c, &endp, 0); + if(errno) + return HOST_NAME; +#if SIZEOF_LONG > 4 + /* a value larger than 32 bits */ + if(l > UINT_MAX) + return HOST_NAME; +#endif + + parts[n] = l; + c = endp; + + switch(*c) { + case '.': + if(n == 3) + return HOST_NAME; + n++; + c++; + break; + + case '\0': + done = TRUE; + break; + + default: + return HOST_NAME; + } + } + + switch(n) { + case 0: /* a -- 32 bits */ + Curl_dyn_reset(host); + + result = Curl_dyn_addf(host, "%u.%u.%u.%u", + (unsigned int)(parts[0] >> 24), + (unsigned int)((parts[0] >> 16) & 0xff), + (unsigned int)((parts[0] >> 8) & 0xff), + (unsigned int)(parts[0] & 0xff)); + break; + case 1: /* a.b -- 8.24 bits */ + if((parts[0] > 0xff) || (parts[1] > 0xffffff)) + return HOST_NAME; + Curl_dyn_reset(host); + result = Curl_dyn_addf(host, "%u.%u.%u.%u", + (unsigned int)(parts[0]), + (unsigned int)((parts[1] >> 16) & 0xff), + (unsigned int)((parts[1] >> 8) & 0xff), + (unsigned int)(parts[1] & 0xff)); + break; + case 2: /* a.b.c -- 8.8.16 bits */ + if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xffff)) + return HOST_NAME; + Curl_dyn_reset(host); + result = Curl_dyn_addf(host, "%u.%u.%u.%u", + (unsigned int)(parts[0]), + (unsigned int)(parts[1]), + (unsigned int)((parts[2] >> 8) & 0xff), + (unsigned int)(parts[2] & 0xff)); + break; + case 3: /* a.b.c.d -- 8.8.8.8 bits */ + if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff) || + (parts[3] > 0xff)) + return HOST_NAME; + Curl_dyn_reset(host); + result = Curl_dyn_addf(host, "%u.%u.%u.%u", + (unsigned int)(parts[0]), + (unsigned int)(parts[1]), + (unsigned int)(parts[2]), + (unsigned int)(parts[3])); + break; + } + if(result) + return HOST_ERROR; + return HOST_IPV4; +} + +/* if necessary, replace the host content with a URL decoded version */ +static CURLUcode urldecode_host(struct dynbuf *host) +{ + char *per = NULL; + const char *hostname = Curl_dyn_ptr(host); + per = strchr(hostname, '%'); + if(!per) + /* nothing to decode */ + return CURLUE_OK; + else { + /* encoded */ + size_t dlen; + char *decoded; + CURLcode result = Curl_urldecode(hostname, 0, &decoded, &dlen, + REJECT_CTRL); + if(result) + return CURLUE_BAD_HOSTNAME; + Curl_dyn_reset(host); + result = Curl_dyn_addn(host, decoded, dlen); + free(decoded); + if(result) + return cc2cu(result); + } + + return CURLUE_OK; +} + +static CURLUcode parse_authority(struct Curl_URL *u, + const char *auth, size_t authlen, + unsigned int flags, + struct dynbuf *host, + bool has_scheme) +{ + size_t offset; + CURLUcode uc; + CURLcode result; + + /* + * Parse the login details and strip them out of the hostname. + */ + uc = parse_hostname_login(u, auth, authlen, flags, &offset); + if(uc) + goto out; + + result = Curl_dyn_addn(host, auth + offset, authlen - offset); + if(result) { + uc = cc2cu(result); + goto out; + } + + uc = Curl_parse_port(u, host, has_scheme); + if(uc) + goto out; + + if(!Curl_dyn_len(host)) + return CURLUE_NO_HOST; + + switch(ipv4_normalize(host)) { + case HOST_IPV4: + break; + case HOST_IPV6: + uc = ipv6_parse(u, Curl_dyn_ptr(host), Curl_dyn_len(host)); + break; + case HOST_NAME: + uc = urldecode_host(host); + if(!uc) + uc = hostname_check(u, Curl_dyn_ptr(host), Curl_dyn_len(host)); + break; + case HOST_ERROR: + uc = CURLUE_OUT_OF_MEMORY; + break; + default: + uc = CURLUE_BAD_HOSTNAME; /* Bad IPv4 address even */ + break; + } + +out: + return uc; +} + +/* used for HTTP/2 server push */ +CURLUcode Curl_url_set_authority(CURLU *u, const char *authority) +{ + CURLUcode result; + struct dynbuf host; + + DEBUGASSERT(authority); + Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH); + + result = parse_authority(u, authority, strlen(authority), + CURLU_DISALLOW_USER, &host, !!u->scheme); + if(result) + Curl_dyn_free(&host); + else { + free(u->host); + u->host = Curl_dyn_ptr(&host); + } + return result; +} + +/* + * "Remove Dot Segments" + * https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 + */ + +/* + * dedotdotify() + * @unittest: 1395 + * + * This function gets a null-terminated path with dot and dotdot sequences + * passed in and strips them off according to the rules in RFC 3986 section + * 5.2.4. + * + * The function handles a query part ('?' + stuff) appended but it expects + * that fragments ('#' + stuff) have already been cut off. + * + * RETURNS + * + * Zero for success and 'out' set to an allocated dedotdotified string. + */ +UNITTEST int dedotdotify(const char *input, size_t clen, char **outp); +UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) +{ + char *outptr; + const char *endp = &input[clen]; + char *out; + + *outp = NULL; + /* the path always starts with a slash, and a slash has not dot */ + if((clen < 2) || !memchr(input, '.', clen)) + return 0; + + out = malloc(clen + 1); + if(!out) + return 1; /* out of memory */ + + *out = 0; /* null-terminates, for inputs like "./" */ + outptr = out; + + do { + bool dotdot = TRUE; + if(*input == '.') { + /* A. If the input buffer begins with a prefix of "../" or "./", then + remove that prefix from the input buffer; otherwise, */ + + if(!strncmp("./", input, 2)) { + input += 2; + clen -= 2; + } + else if(!strncmp("../", input, 3)) { + input += 3; + clen -= 3; + } + /* D. if the input buffer consists only of "." or "..", then remove + that from the input buffer; otherwise, */ + + else if(!strcmp(".", input) || !strcmp("..", input) || + !strncmp(".?", input, 2) || !strncmp("..?", input, 3)) { + *out = 0; + break; + } + else + dotdot = FALSE; + } + else if(*input == '/') { + /* B. if the input buffer begins with a prefix of "/./" or "/.", where + "." is a complete path segment, then replace that prefix with "/" in + the input buffer; otherwise, */ + if(!strncmp("/./", input, 3)) { + input += 2; + clen -= 2; + } + else if(!strcmp("/.", input) || !strncmp("/.?", input, 3)) { + *outptr++ = '/'; + *outptr = 0; + break; + } + + /* C. if the input buffer begins with a prefix of "/../" or "/..", + where ".." is a complete path segment, then replace that prefix with + "/" in the input buffer and remove the last segment and its + preceding "/" (if any) from the output buffer; otherwise, */ + + else if(!strncmp("/../", input, 4)) { + input += 3; + clen -= 3; + /* remove the last segment from the output buffer */ + while(outptr > out) { + outptr--; + if(*outptr == '/') + break; + } + *outptr = 0; /* null-terminate where it stops */ + } + else if(!strcmp("/..", input) || !strncmp("/..?", input, 4)) { + /* remove the last segment from the output buffer */ + while(outptr > out) { + outptr--; + if(*outptr == '/') + break; + } + *outptr++ = '/'; + *outptr = 0; /* null-terminate where it stops */ + break; + } + else + dotdot = FALSE; + } + else + dotdot = FALSE; + + if(!dotdot) { + /* E. move the first path segment in the input buffer to the end of + the output buffer, including the initial "/" character (if any) and + any subsequent characters up to, but not including, the next "/" + character or the end of the input buffer. */ + + do { + *outptr++ = *input++; + clen--; + } while(*input && (*input != '/') && (*input != '?')); + *outptr = 0; + } + + /* continue until end of path */ + } while(input < endp); + + *outp = out; + return 0; /* success */ +} + +static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) +{ + const char *path; + size_t pathlen; + char *query = NULL; + char *fragment = NULL; + char schemebuf[MAX_SCHEME_LEN + 1]; + size_t schemelen = 0; + size_t urllen; + CURLUcode result = CURLUE_OK; + size_t fraglen = 0; + struct dynbuf host; + + DEBUGASSERT(url); + + Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH); + + result = junkscan(url, &urllen, flags); + if(result) + goto fail; + + schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf), + flags & (CURLU_GUESS_SCHEME| + CURLU_DEFAULT_SCHEME)); + + /* handle the file: scheme */ + if(schemelen && !strcmp(schemebuf, "file")) { + bool uncpath = FALSE; + if(urllen <= 6) { + /* file:/ is not enough to actually be a complete file: URL */ + result = CURLUE_BAD_FILE_URL; + goto fail; + } + + /* path has been allocated large enough to hold this */ + path = (char *)&url[5]; + pathlen = urllen - 5; + + u->scheme = strdup("file"); + if(!u->scheme) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } + + /* Extra handling URLs with an authority component (i.e. that start with + * "file://") + * + * We allow omitted hostname (e.g. file:/) -- valid according to + * RFC 8089, but not the (current) WHAT-WG URL spec. + */ + if(path[0] == '/' && path[1] == '/') { + /* swallow the two slashes */ + const char *ptr = &path[2]; + + /* + * According to RFC 8089, a file: URL can be reliably dereferenced if: + * + * o it has no/blank hostname, or + * + * o the hostname matches "localhost" (case-insensitively), or + * + * o the hostname is a FQDN that resolves to this machine, or + * + * o it is an UNC String transformed to an URI (Windows only, RFC 8089 + * Appendix E.3). + * + * For brevity, we only consider URLs with empty, "localhost", or + * "127.0.0.1" hostnames as local, otherwise as an UNC String. + * + * Additionally, there is an exception for URLs with a Windows drive + * letter in the authority (which was accidentally omitted from RFC 8089 + * Appendix E, but believe me, it was meant to be there. --MK) + */ + if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { + /* the URL includes a hostname, it must match "localhost" or + "127.0.0.1" to be valid */ + if(checkprefix("localhost/", ptr) || + checkprefix("127.0.0.1/", ptr)) { + ptr += 9; /* now points to the slash after the host */ + } + else { +#if defined(_WIN32) + size_t len; + + /* the hostname, NetBIOS computer name, can not contain disallowed + chars, and the delimiting slash character must be appended to the + hostname */ + path = strpbrk(ptr, "/\\:*?\"<>|"); + if(!path || *path != '/') { + result = CURLUE_BAD_FILE_URL; + goto fail; + } + + len = path - ptr; + if(len) { + CURLcode code = Curl_dyn_addn(&host, ptr, len); + if(code) { + result = cc2cu(code); + goto fail; + } + uncpath = TRUE; + } + + ptr -= 2; /* now points to the // before the host in UNC */ +#else + /* Invalid file://hostname/, expected localhost or 127.0.0.1 or + none */ + result = CURLUE_BAD_FILE_URL; + goto fail; +#endif + } + } + + path = ptr; + pathlen = urllen - (ptr - url); + } + + if(!uncpath) + /* no host for file: URLs by default */ + Curl_dyn_reset(&host); + +#if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__) + /* Do not allow Windows drive letters when not in Windows. + * This catches both "file:/c:" and "file:c:" */ + if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || + STARTS_WITH_URL_DRIVE_PREFIX(path)) { + /* File drive letters are only accepted in MSDOS/Windows */ + result = CURLUE_BAD_FILE_URL; + goto fail; + } +#else + /* If the path starts with a slash and a drive letter, ditch the slash */ + if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { + /* This cannot be done with strcpy, as the memory chunks overlap! */ + path++; + pathlen--; + } +#endif + + } + else { + /* clear path */ + const char *schemep = NULL; + const char *hostp; + size_t hostlen; + + if(schemelen) { + int i = 0; + const char *p = &url[schemelen + 1]; + while((*p == '/') && (i < 4)) { + p++; + i++; + } + + schemep = schemebuf; + if(!Curl_get_scheme_handler(schemep) && + !(flags & CURLU_NON_SUPPORT_SCHEME)) { + result = CURLUE_UNSUPPORTED_SCHEME; + goto fail; + } + + if((i < 1) || (i > 3)) { + /* less than one or more than three slashes */ + result = CURLUE_BAD_SLASHES; + goto fail; + } + hostp = p; /* hostname starts here */ + } + else { + /* no scheme! */ + + if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME))) { + result = CURLUE_BAD_SCHEME; + goto fail; + } + if(flags & CURLU_DEFAULT_SCHEME) + schemep = DEFAULT_SCHEME; + + /* + * The URL was badly formatted, let's try without scheme specified. + */ + hostp = url; + } + + if(schemep) { + u->scheme = strdup(schemep); + if(!u->scheme) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } + } + + /* find the end of the hostname + port number */ + hostlen = strcspn(hostp, "/?#"); + path = &hostp[hostlen]; + + /* this pathlen also contains the query and the fragment */ + pathlen = urllen - (path - url); + if(hostlen) { + + result = parse_authority(u, hostp, hostlen, flags, &host, schemelen); + if(result) + goto fail; + + if((flags & CURLU_GUESS_SCHEME) && !schemep) { + const char *hostname = Curl_dyn_ptr(&host); + /* legacy curl-style guess based on hostname */ + if(checkprefix("ftp.", hostname)) + schemep = "ftp"; + else if(checkprefix("dict.", hostname)) + schemep = "dict"; + else if(checkprefix("ldap.", hostname)) + schemep = "ldap"; + else if(checkprefix("imap.", hostname)) + schemep = "imap"; + else if(checkprefix("smtp.", hostname)) + schemep = "smtp"; + else if(checkprefix("pop3.", hostname)) + schemep = "pop3"; + else + schemep = "http"; + + u->scheme = strdup(schemep); + if(!u->scheme) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } + u->guessed_scheme = TRUE; + } + } + else if(flags & CURLU_NO_AUTHORITY) { + /* allowed to be empty. */ + if(Curl_dyn_add(&host, "")) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } + } + else { + result = CURLUE_NO_HOST; + goto fail; + } + } + + fragment = strchr(path, '#'); + if(fragment) { + fraglen = pathlen - (fragment - path); + u->fragment_present = TRUE; + if(fraglen > 1) { + /* skip the leading '#' in the copy but include the terminating null */ + if(flags & CURLU_URLENCODE) { + struct dynbuf enc; + Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE); + if(result) + goto fail; + u->fragment = Curl_dyn_ptr(&enc); + } + else { + u->fragment = Curl_memdup0(fragment + 1, fraglen - 1); + if(!u->fragment) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } + } + } + /* after this, pathlen still contains the query */ + pathlen -= fraglen; + } + + query = memchr(path, '?', pathlen); + if(query) { + size_t qlen = fragment ? (size_t)(fragment - query) : + pathlen - (query - path); + pathlen -= qlen; + u->query_present = TRUE; + if(qlen > 1) { + if(flags & CURLU_URLENCODE) { + struct dynbuf enc; + Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + /* skip the leading question mark */ + result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE); + if(result) + goto fail; + u->query = Curl_dyn_ptr(&enc); + } + else { + u->query = Curl_memdup0(query + 1, qlen - 1); + if(!u->query) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } + } + } + else { + /* single byte query */ + u->query = strdup(""); + if(!u->query) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } + } + } + + if(pathlen && (flags & CURLU_URLENCODE)) { + struct dynbuf enc; + Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + result = urlencode_str(&enc, path, pathlen, TRUE, FALSE); + if(result) + goto fail; + pathlen = Curl_dyn_len(&enc); + path = u->path = Curl_dyn_ptr(&enc); + } + + if(pathlen <= 1) { + /* there is no path left or just the slash, unset */ + path = NULL; + } + else { + if(!u->path) { + u->path = Curl_memdup0(path, pathlen); + if(!u->path) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } + path = u->path; + } + else if(flags & CURLU_URLENCODE) + /* it might have encoded more than just the path so cut it */ + u->path[pathlen] = 0; + + if(!(flags & CURLU_PATH_AS_IS)) { + /* remove ../ and ./ sequences according to RFC3986 */ + char *dedot; + int err = dedotdotify((char *)path, pathlen, &dedot); + if(err) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } + if(dedot) { + free(u->path); + u->path = dedot; + } + } + } + + u->host = Curl_dyn_ptr(&host); + + return result; +fail: + Curl_dyn_free(&host); + free_urlhandle(u); + return result; +} + +/* + * Parse the URL and, if successful, replace everything in the Curl_URL struct. + */ +static CURLUcode parseurl_and_replace(const char *url, CURLU *u, + unsigned int flags) +{ + CURLUcode result; + CURLU tmpurl; + memset(&tmpurl, 0, sizeof(tmpurl)); + result = parseurl(url, &tmpurl, flags); + if(!result) { + free_urlhandle(u); + *u = tmpurl; + } + return result; +} + +/* + */ +CURLU *curl_url(void) +{ + return calloc(1, sizeof(struct Curl_URL)); +} + +void curl_url_cleanup(CURLU *u) +{ + if(u) { + free_urlhandle(u); + free(u); + } +} + +#define DUP(dest, src, name) \ + do { \ + if(src->name) { \ + dest->name = strdup(src->name); \ + if(!dest->name) \ + goto fail; \ + } \ + } while(0) + +CURLU *curl_url_dup(const CURLU *in) +{ + struct Curl_URL *u = calloc(1, sizeof(struct Curl_URL)); + if(u) { + DUP(u, in, scheme); + DUP(u, in, user); + DUP(u, in, password); + DUP(u, in, options); + DUP(u, in, host); + DUP(u, in, port); + DUP(u, in, path); + DUP(u, in, query); + DUP(u, in, fragment); + DUP(u, in, zoneid); + u->portnum = in->portnum; + u->fragment_present = in->fragment_present; + u->query_present = in->query_present; + } + return u; +fail: + curl_url_cleanup(u); + return NULL; +} + +CURLUcode curl_url_get(const CURLU *u, CURLUPart what, + char **part, unsigned int flags) +{ + const char *ptr; + CURLUcode ifmissing = CURLUE_UNKNOWN_PART; + char portbuf[7]; + bool urldecode = (flags & CURLU_URLDECODE)?1:0; + bool urlencode = (flags & CURLU_URLENCODE)?1:0; + bool punycode = FALSE; + bool depunyfy = FALSE; + bool plusdecode = FALSE; + (void)flags; + if(!u) + return CURLUE_BAD_HANDLE; + if(!part) + return CURLUE_BAD_PARTPOINTER; + *part = NULL; + + switch(what) { + case CURLUPART_SCHEME: + ptr = u->scheme; + ifmissing = CURLUE_NO_SCHEME; + urldecode = FALSE; /* never for schemes */ + if((flags & CURLU_NO_GUESS_SCHEME) && u->guessed_scheme) + return CURLUE_NO_SCHEME; + break; + case CURLUPART_USER: + ptr = u->user; + ifmissing = CURLUE_NO_USER; + break; + case CURLUPART_PASSWORD: + ptr = u->password; + ifmissing = CURLUE_NO_PASSWORD; + break; + case CURLUPART_OPTIONS: + ptr = u->options; + ifmissing = CURLUE_NO_OPTIONS; + break; + case CURLUPART_HOST: + ptr = u->host; + ifmissing = CURLUE_NO_HOST; + punycode = (flags & CURLU_PUNYCODE)?1:0; + depunyfy = (flags & CURLU_PUNY2IDN)?1:0; + break; + case CURLUPART_ZONEID: + ptr = u->zoneid; + ifmissing = CURLUE_NO_ZONEID; + break; + case CURLUPART_PORT: + ptr = u->port; + ifmissing = CURLUE_NO_PORT; + urldecode = FALSE; /* never for port */ + if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { + /* there is no stored port number, but asked to deliver + a default one for the scheme */ + const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); + if(h) { + msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); + ptr = portbuf; + } + } + else if(ptr && u->scheme) { + /* there is a stored port number, but ask to inhibit if + it matches the default one for the scheme */ + const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); + if(h && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) + ptr = NULL; + } + break; + case CURLUPART_PATH: + ptr = u->path; + if(!ptr) + ptr = "/"; + break; + case CURLUPART_QUERY: + ptr = u->query; + ifmissing = CURLUE_NO_QUERY; + plusdecode = urldecode; + if(ptr && !ptr[0] && !(flags & CURLU_GET_EMPTY)) + /* there was a blank query and the user do not ask for it */ + ptr = NULL; + break; + case CURLUPART_FRAGMENT: + ptr = u->fragment; + ifmissing = CURLUE_NO_FRAGMENT; + if(!ptr && u->fragment_present && flags & CURLU_GET_EMPTY) + /* there was a blank fragment and the user asks for it */ + ptr = ""; + break; + case CURLUPART_URL: { + char *url; + char *scheme; + char *options = u->options; + char *port = u->port; + char *allochost = NULL; + bool show_fragment = + u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY); + bool show_query = + (u->query && u->query[0]) || + (u->query_present && flags & CURLU_GET_EMPTY); + punycode = (flags & CURLU_PUNYCODE)?1:0; + depunyfy = (flags & CURLU_PUNY2IDN)?1:0; + if(u->scheme && strcasecompare("file", u->scheme)) { + url = aprintf("file://%s%s%s", + u->path, + show_fragment ? "#": "", + u->fragment ? u->fragment : ""); + } + else if(!u->host) + return CURLUE_NO_HOST; + else { + const struct Curl_handler *h = NULL; + char schemebuf[MAX_SCHEME_LEN + 5]; + if(u->scheme) + scheme = u->scheme; + else if(flags & CURLU_DEFAULT_SCHEME) + scheme = (char *) DEFAULT_SCHEME; + else + return CURLUE_NO_SCHEME; + + h = Curl_get_scheme_handler(scheme); + if(!port && (flags & CURLU_DEFAULT_PORT)) { + /* there is no stored port number, but asked to deliver + a default one for the scheme */ + if(h) { + msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); + port = portbuf; + } + } + else if(port) { + /* there is a stored port number, but asked to inhibit if it matches + the default one for the scheme */ + if(h && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) + port = NULL; + } + + if(h && !(h->flags & PROTOPT_URLOPTIONS)) + options = NULL; + + if(u->host[0] == '[') { + if(u->zoneid) { + /* make it '[ host %25 zoneid ]' */ + struct dynbuf enc; + size_t hostlen = strlen(u->host); + Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + if(Curl_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host, + u->zoneid)) + return CURLUE_OUT_OF_MEMORY; + allochost = Curl_dyn_ptr(&enc); + } + } + else if(urlencode) { + allochost = curl_easy_escape(NULL, u->host, 0); + if(!allochost) + return CURLUE_OUT_OF_MEMORY; + } + else if(punycode) { + if(!Curl_is_ASCII_name(u->host)) { +#ifndef USE_IDN + return CURLUE_LACKS_IDN; +#else + CURLcode result = Curl_idn_decode(u->host, &allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; +#endif + } + } + else if(depunyfy) { + if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) { +#ifndef USE_IDN + return CURLUE_LACKS_IDN; +#else + CURLcode result = Curl_idn_encode(u->host, &allochost); + if(result) + /* this is the most likely error */ + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; +#endif + } + } + + if(!(flags & CURLU_NO_GUESS_SCHEME) || !u->guessed_scheme) + msnprintf(schemebuf, sizeof(schemebuf), "%s://", scheme); + else + schemebuf[0] = 0; + + url = aprintf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + schemebuf, + u->user ? u->user : "", + u->password ? ":": "", + u->password ? u->password : "", + options ? ";" : "", + options ? options : "", + (u->user || u->password || options) ? "@": "", + allochost ? allochost : u->host, + port ? ":": "", + port ? port : "", + u->path ? u->path : "/", + show_query ? "?": "", + u->query ? u->query : "", + show_fragment ? "#": "", + u->fragment? u->fragment : ""); + free(allochost); + } + if(!url) + return CURLUE_OUT_OF_MEMORY; + *part = url; + return CURLUE_OK; + } + default: + ptr = NULL; + break; + } + if(ptr) { + size_t partlen = strlen(ptr); + size_t i = 0; + *part = Curl_memdup0(ptr, partlen); + if(!*part) + return CURLUE_OUT_OF_MEMORY; + if(plusdecode) { + /* convert + to space */ + char *plus = *part; + for(i = 0; i < partlen; ++plus, i++) { + if(*plus == '+') + *plus = ' '; + } + } + if(urldecode) { + char *decoded; + size_t dlen; + /* this unconditional rejection of control bytes is documented + API behavior */ + CURLcode res = Curl_urldecode(*part, 0, &decoded, &dlen, REJECT_CTRL); + free(*part); + if(res) { + *part = NULL; + return CURLUE_URLDECODE; + } + *part = decoded; + partlen = dlen; + } + if(urlencode) { + struct dynbuf enc; + CURLUcode uc; + Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + uc = urlencode_str(&enc, *part, partlen, TRUE, what == CURLUPART_QUERY); + if(uc) + return uc; + free(*part); + *part = Curl_dyn_ptr(&enc); + } + else if(punycode) { + if(!Curl_is_ASCII_name(u->host)) { +#ifndef USE_IDN + return CURLUE_LACKS_IDN; +#else + char *allochost; + CURLcode result = Curl_idn_decode(*part, &allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; + free(*part); + *part = allochost; +#endif + } + } + else if(depunyfy) { + if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) { +#ifndef USE_IDN + return CURLUE_LACKS_IDN; +#else + char *allochost; + CURLcode result = Curl_idn_encode(*part, &allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; + free(*part); + *part = allochost; +#endif + } + } + + return CURLUE_OK; + } + else + return ifmissing; +} + +CURLUcode curl_url_set(CURLU *u, CURLUPart what, + const char *part, unsigned int flags) +{ + char **storep = NULL; + bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0; + bool plusencode = FALSE; + bool urlskipslash = FALSE; + bool leadingslash = FALSE; + bool appendquery = FALSE; + bool equalsencode = FALSE; + size_t nalloc; + + if(!u) + return CURLUE_BAD_HANDLE; + if(!part) { + /* setting a part to NULL clears it */ + switch(what) { + case CURLUPART_URL: + break; + case CURLUPART_SCHEME: + storep = &u->scheme; + u->guessed_scheme = FALSE; + break; + case CURLUPART_USER: + storep = &u->user; + break; + case CURLUPART_PASSWORD: + storep = &u->password; + break; + case CURLUPART_OPTIONS: + storep = &u->options; + break; + case CURLUPART_HOST: + storep = &u->host; + break; + case CURLUPART_ZONEID: + storep = &u->zoneid; + break; + case CURLUPART_PORT: + u->portnum = 0; + storep = &u->port; + break; + case CURLUPART_PATH: + storep = &u->path; + break; + case CURLUPART_QUERY: + storep = &u->query; + u->query_present = FALSE; + break; + case CURLUPART_FRAGMENT: + storep = &u->fragment; + u->fragment_present = FALSE; + break; + default: + return CURLUE_UNKNOWN_PART; + } + if(storep && *storep) { + Curl_safefree(*storep); + } + else if(!storep) { + free_urlhandle(u); + memset(u, 0, sizeof(struct Curl_URL)); + } + return CURLUE_OK; + } + + nalloc = strlen(part); + if(nalloc > CURL_MAX_INPUT_LENGTH) + /* excessive input length */ + return CURLUE_MALFORMED_INPUT; + + switch(what) { + case CURLUPART_SCHEME: { + size_t plen = strlen(part); + const char *s = part; + if((plen > MAX_SCHEME_LEN) || (plen < 1)) + /* too long or too short */ + return CURLUE_BAD_SCHEME; + /* verify that it is a fine scheme */ + if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(part)) + return CURLUE_UNSUPPORTED_SCHEME; + storep = &u->scheme; + urlencode = FALSE; /* never */ + if(ISALPHA(*s)) { + /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ + while(--plen) { + if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.')) + s++; /* fine */ + else + return CURLUE_BAD_SCHEME; + } + } + else + return CURLUE_BAD_SCHEME; + u->guessed_scheme = FALSE; + break; + } + case CURLUPART_USER: + storep = &u->user; + break; + case CURLUPART_PASSWORD: + storep = &u->password; + break; + case CURLUPART_OPTIONS: + storep = &u->options; + break; + case CURLUPART_HOST: + storep = &u->host; + Curl_safefree(u->zoneid); + break; + case CURLUPART_ZONEID: + storep = &u->zoneid; + break; + case CURLUPART_PORT: + if(!ISDIGIT(part[0])) + /* not a number */ + return CURLUE_BAD_PORT_NUMBER; + else { + char *tmp; + char *endp; + unsigned long port; + errno = 0; + port = strtoul(part, &endp, 10); /* must be decimal */ + if(errno || (port > 0xffff) || *endp) + /* weirdly provided number, not good! */ + return CURLUE_BAD_PORT_NUMBER; + tmp = strdup(part); + if(!tmp) + return CURLUE_OUT_OF_MEMORY; + free(u->port); + u->port = tmp; + u->portnum = (unsigned short)port; + return CURLUE_OK; + } + case CURLUPART_PATH: + urlskipslash = TRUE; + leadingslash = TRUE; /* enforce */ + storep = &u->path; + break; + case CURLUPART_QUERY: + plusencode = urlencode; + appendquery = (flags & CURLU_APPENDQUERY)?1:0; + equalsencode = appendquery; + storep = &u->query; + u->query_present = TRUE; + break; + case CURLUPART_FRAGMENT: + storep = &u->fragment; + u->fragment_present = TRUE; + break; + case CURLUPART_URL: { + /* + * Allow a new URL to replace the existing (if any) contents. + * + * If the existing contents is enough for a URL, allow a relative URL to + * replace it. + */ + CURLcode result; + CURLUcode uc; + char *oldurl; + char *redired_url; + + if(!nalloc) + /* a blank URL is not a valid URL */ + return CURLUE_MALFORMED_INPUT; + + /* if the new thing is absolute or the old one is not + * (we could not get an absolute URL in 'oldurl'), + * then replace the existing with the new. */ + if(Curl_is_absolute_url(part, NULL, 0, + flags & (CURLU_GUESS_SCHEME| + CURLU_DEFAULT_SCHEME)) + || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) { + return parseurl_and_replace(part, u, flags); + } + + /* apply the relative part to create a new URL + * and replace the existing one with it. */ + result = concat_url(oldurl, part, &redired_url); + free(oldurl); + if(result) + return cc2cu(result); + + uc = parseurl_and_replace(redired_url, u, flags); + free(redired_url); + return uc; + } + default: + return CURLUE_UNKNOWN_PART; + } + DEBUGASSERT(storep); + { + const char *newp; + struct dynbuf enc; + Curl_dyn_init(&enc, nalloc * 3 + 1 + leadingslash); + + if(leadingslash && (part[0] != '/')) { + CURLcode result = Curl_dyn_addn(&enc, "/", 1); + if(result) + return cc2cu(result); + } + if(urlencode) { + const unsigned char *i; + + for(i = (const unsigned char *)part; *i; i++) { + CURLcode result; + if((*i == ' ') && plusencode) { + result = Curl_dyn_addn(&enc, "+", 1); + if(result) + return CURLUE_OUT_OF_MEMORY; + } + else if(ISUNRESERVED(*i) || + ((*i == '/') && urlskipslash) || + ((*i == '=') && equalsencode)) { + if((*i == '=') && equalsencode) + /* only skip the first equals sign */ + equalsencode = FALSE; + result = Curl_dyn_addn(&enc, i, 1); + if(result) + return cc2cu(result); + } + else { + char out[3]={'%'}; + out[1] = hexdigits[*i>>4]; + out[2] = hexdigits[*i & 0xf]; + result = Curl_dyn_addn(&enc, out, 3); + if(result) + return cc2cu(result); + } + } + } + else { + char *p; + CURLcode result = Curl_dyn_add(&enc, part); + if(result) + return cc2cu(result); + p = Curl_dyn_ptr(&enc); + while(*p) { + /* make sure percent encoded are lower case */ + if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) && + (ISUPPER(p[1]) || ISUPPER(p[2]))) { + p[1] = Curl_raw_tolower(p[1]); + p[2] = Curl_raw_tolower(p[2]); + p += 3; + } + else + p++; + } + } + newp = Curl_dyn_ptr(&enc); + + if(appendquery && newp) { + /* Append the 'newp' string onto the old query. Add a '&' separator if + none is present at the end of the existing query already */ + + size_t querylen = u->query ? strlen(u->query) : 0; + bool addamperand = querylen && (u->query[querylen -1] != '&'); + if(querylen) { + struct dynbuf qbuf; + Curl_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH); + + if(Curl_dyn_addn(&qbuf, u->query, querylen)) /* add original query */ + goto nomem; + + if(addamperand) { + if(Curl_dyn_addn(&qbuf, "&", 1)) + goto nomem; + } + if(Curl_dyn_add(&qbuf, newp)) + goto nomem; + Curl_dyn_free(&enc); + free(*storep); + *storep = Curl_dyn_ptr(&qbuf); + return CURLUE_OK; +nomem: + Curl_dyn_free(&enc); + return CURLUE_OUT_OF_MEMORY; + } + } + + else if(what == CURLUPART_HOST) { + size_t n = Curl_dyn_len(&enc); + if(!n && (flags & CURLU_NO_AUTHORITY)) { + /* Skip hostname check, it is allowed to be empty. */ + } + else { + if(!n || hostname_check(u, (char *)newp, n)) { + Curl_dyn_free(&enc); + return CURLUE_BAD_HOSTNAME; + } + } + } + + free(*storep); + *storep = (char *)newp; + } + return CURLUE_OK; +} diff --git a/third_party/curl/lib/urldata.h b/third_party/curl/lib/urldata.h new file mode 100644 index 0000000000..f34aa591ca --- /dev/null +++ b/third_party/curl/lib/urldata.h @@ -0,0 +1,1974 @@ +#ifndef HEADER_CURL_URLDATA_H +#define HEADER_CURL_URLDATA_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* This file is for lib internal stuff */ + +#include "curl_setup.h" + +#define PORT_FTP 21 +#define PORT_FTPS 990 +#define PORT_TELNET 23 +#define PORT_HTTP 80 +#define PORT_HTTPS 443 +#define PORT_DICT 2628 +#define PORT_LDAP 389 +#define PORT_LDAPS 636 +#define PORT_TFTP 69 +#define PORT_SSH 22 +#define PORT_IMAP 143 +#define PORT_IMAPS 993 +#define PORT_POP3 110 +#define PORT_POP3S 995 +#define PORT_SMB 445 +#define PORT_SMBS 445 +#define PORT_SMTP 25 +#define PORT_SMTPS 465 /* sometimes called SSMTP */ +#define PORT_RTSP 554 +#define PORT_RTMP 1935 +#define PORT_RTMPT PORT_HTTP +#define PORT_RTMPS PORT_HTTPS +#define PORT_GOPHER 70 +#define PORT_MQTT 1883 + +struct curl_trc_featt; + +#ifdef USE_ECH +/* CURLECH_ bits for the tls_ech option */ +# define CURLECH_DISABLE (1<<0) +# define CURLECH_GREASE (1<<1) +# define CURLECH_ENABLE (1<<2) +# define CURLECH_HARD (1<<3) +# define CURLECH_CLA_CFG (1<<4) +#endif + +#ifdef USE_WEBSOCKETS +/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number, + * the rest are internal information. If we use higher bits we only do this on + * platforms that have a >= 64-bit type and then we use such a type for the + * protocol fields in the protocol handler. + */ +#define CURLPROTO_WS (1<<30) +#define CURLPROTO_WSS ((curl_prot_t)1<<31) +#else +#define CURLPROTO_WS 0 +#define CURLPROTO_WSS 0 +#endif + +/* This should be undefined once we need bit 32 or higher */ +#define PROTO_TYPE_SMALL + +#ifndef PROTO_TYPE_SMALL +typedef curl_off_t curl_prot_t; +#else +typedef unsigned int curl_prot_t; +#endif + +/* This mask is for all the old protocols that are provided and defined in the + public header and shall exclude protocols added since which are not exposed + in the API */ +#define CURLPROTO_MASK (0x3ffffff) + +#define DICT_MATCH "/MATCH:" +#define DICT_MATCH2 "/M:" +#define DICT_MATCH3 "/FIND:" +#define DICT_DEFINE "/DEFINE:" +#define DICT_DEFINE2 "/D:" +#define DICT_DEFINE3 "/LOOKUP:" + +#define CURL_DEFAULT_USER "anonymous" +#define CURL_DEFAULT_PASSWORD "ftp@example.com" + +/* Convenience defines for checking protocols or their SSL based version. Each + protocol handler should only ever have a single CURLPROTO_ in its protocol + field. */ +#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_WS| \ + CURLPROTO_WSS) +#define PROTO_FAMILY_FTP (CURLPROTO_FTP|CURLPROTO_FTPS) +#define PROTO_FAMILY_POP3 (CURLPROTO_POP3|CURLPROTO_POP3S) +#define PROTO_FAMILY_SMB (CURLPROTO_SMB|CURLPROTO_SMBS) +#define PROTO_FAMILY_SMTP (CURLPROTO_SMTP|CURLPROTO_SMTPS) +#define PROTO_FAMILY_SSH (CURLPROTO_SCP|CURLPROTO_SFTP) + +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \ + !defined(CURL_DISABLE_POP3) || !defined(CURL_DISABLE_FILE) +/* these protocols support CURLOPT_DIRLISTONLY */ +#define CURL_LIST_ONLY_PROTOCOL 1 +#endif + +#define DEFAULT_CONNCACHE_SIZE 5 + +/* length of longest IPv6 address string including the trailing null */ +#define MAX_IPADR_LEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + +/* Default FTP/IMAP etc response timeout in milliseconds */ +#define RESP_TIMEOUT (120*1000) + +/* Max string input length is a precaution against abuse and to detect junk + input easier and better. */ +#define CURL_MAX_INPUT_LENGTH 8000000 + + +#include "cookie.h" +#include "psl.h" +#include "formdata.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETINET_IN6_H +#include +#endif + +#include "timeval.h" + +#include + +#include "http_chunks.h" /* for the structs and enum stuff */ +#include "hostip.h" +#include "hash.h" +#include "splay.h" +#include "dynbuf.h" +#include "dynhds.h" +#include "request.h" + +/* return the count of bytes sent, or -1 on error */ +typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */ + int sockindex, /* socketindex */ + const void *buf, /* data to write */ + size_t len, /* max amount to write */ + CURLcode *err); /* error to return */ + +/* return the count of bytes read, or -1 on error */ +typedef ssize_t (Curl_recv)(struct Curl_easy *data, /* transfer */ + int sockindex, /* socketindex */ + char *buf, /* store data here */ + size_t len, /* max amount to read */ + CURLcode *err); /* error to return */ + +#ifdef USE_HYPER +typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, + struct connectdata *conn, + int *didwhat, + int select_res); +#endif + +#include "mime.h" +#include "imap.h" +#include "pop3.h" +#include "smtp.h" +#include "ftp.h" +#include "file.h" +#include "vssh/ssh.h" +#include "http.h" +#include "rtsp.h" +#include "smb.h" +#include "mqtt.h" +#include "ftplistparser.h" +#include "multihandle.h" +#include "c-hyper.h" +#include "cf-socket.h" + +#ifdef HAVE_GSSAPI +# ifdef HAVE_GSSGNU +# include +# elif defined HAVE_GSSAPI_GSSAPI_H +# include +# else +# include +# endif +# ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H +# include +# endif +#endif + +#ifdef USE_LIBSSH2 +#include +#include +#endif /* USE_LIBSSH2 */ + +#define READBUFFER_SIZE CURL_MAX_WRITE_SIZE +#define READBUFFER_MAX CURL_MAX_READ_SIZE +#define READBUFFER_MIN 1024 + +/* The default upload buffer size, should not be smaller than + CURL_MAX_WRITE_SIZE, as it needs to hold a full buffer as could be sent in + a write callback. + + The size was 16KB for many years but was bumped to 64KB because it makes + libcurl able to do significantly faster uploads in some circumstances. Even + larger buffers can help further, but this is deemed a fair memory/speed + compromise. */ +#define UPLOADBUFFER_DEFAULT 65536 +#define UPLOADBUFFER_MAX (2*1024*1024) +#define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE + +#define CURLEASY_MAGIC_NUMBER 0xc0dedbadU +#ifdef DEBUGBUILD +/* On a debug build, we want to fail hard on easy handles that + * are not NULL, but no longer have the MAGIC touch. This gives + * us early warning on things only discovered by valgrind otherwise. */ +#define GOOD_EASY_HANDLE(x) \ + (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER))? TRUE: \ + (DEBUGASSERT(!(x)), FALSE)) +#else +#define GOOD_EASY_HANDLE(x) \ + ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) +#endif + +#ifdef HAVE_GSSAPI +/* Types needed for krb5-ftp connections */ +struct krb5buffer { + struct dynbuf buf; + size_t index; + BIT(eof_flag); +}; + +enum protection_level { + PROT_NONE, /* first in list */ + PROT_CLEAR, + PROT_SAFE, + PROT_CONFIDENTIAL, + PROT_PRIVATE, + PROT_CMD, + PROT_LAST /* last in list */ +}; +#endif + +/* SSL backend-specific data; declared differently by each SSL backend */ +struct ssl_backend_data; + +typedef enum { + CURL_SSL_PEER_DNS, + CURL_SSL_PEER_IPV4, + CURL_SSL_PEER_IPV6 +} ssl_peer_type; + +struct ssl_peer { + char *hostname; /* hostname for verification */ + char *dispname; /* display version of hostname */ + char *sni; /* SNI version of hostname or NULL if not usable */ + ssl_peer_type type; /* type of the peer information */ + int port; /* port we are talking to */ + int transport; /* TCP or QUIC */ +}; + +struct ssl_primary_config { + char *CApath; /* certificate dir (does not work on windows) */ + char *CAfile; /* certificate to verify peer against */ + char *issuercert; /* optional issuer certificate filename */ + char *clientcert; + char *cipher_list; /* list of ciphers to use */ + char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ + char *pinned_key; + char *CRLfile; /* CRL to check certificate revocation */ + struct curl_blob *cert_blob; + struct curl_blob *ca_info_blob; + struct curl_blob *issuercert_blob; +#ifdef USE_TLS_SRP + char *username; /* TLS username (for, e.g., SRP) */ + char *password; /* TLS password (for, e.g., SRP) */ +#endif + char *curves; /* list of curves to use */ + unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ + unsigned int version_max; /* max supported version the client wants to use */ + unsigned char version; /* what version the client wants to use */ + BIT(verifypeer); /* set TRUE if this is desired */ + BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ + BIT(verifystatus); /* set TRUE if certificate status must be checked */ + BIT(cache_session); /* cache session or not */ +}; + +struct ssl_config_data { + struct ssl_primary_config primary; + long certverifyresult; /* result from the certificate verification */ + curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ + void *fsslctxp; /* parameter for call back */ + char *cert_type; /* format for certificate (default: PEM)*/ + char *key; /* private key filename */ + struct curl_blob *key_blob; + char *key_type; /* format for private key (default: PEM) */ + char *key_passwd; /* plain text private key password */ + BIT(certinfo); /* gather lots of certificate info */ + BIT(falsestart); + BIT(enable_beast); /* allow this flaw for interoperability's sake */ + BIT(no_revoke); /* disable SSL certificate revocation checks */ + BIT(no_partialchain); /* do not accept partial certificate chains */ + BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation + list errors */ + BIT(native_ca_store); /* use the native ca store of operating system */ + BIT(auto_client_cert); /* automatically locate and use a client + certificate for authentication (Schannel) */ +}; + +struct ssl_general_config { + size_t max_ssl_sessions; /* SSL session id cache size */ + int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ +}; + +typedef void Curl_ssl_sessionid_dtor(void *sessionid, size_t idsize); + +/* information stored about one single SSL session */ +struct Curl_ssl_session { + char *name; /* hostname for which this ID was used */ + char *conn_to_host; /* hostname for the connection (may be NULL) */ + const char *scheme; /* protocol scheme used */ + void *sessionid; /* as returned from the SSL layer */ + size_t idsize; /* if known, otherwise 0 */ + Curl_ssl_sessionid_dtor *sessionid_free; /* free `sessionid` callback */ + long age; /* just a number, the higher the more recent */ + int remote_port; /* remote port */ + int conn_to_port; /* remote port for the connection (may be -1) */ + int transport; /* TCP or QUIC */ + struct ssl_primary_config ssl_config; /* setup for this session */ +}; + +#ifdef USE_WINDOWS_SSPI +#include "curl_sspi.h" +#endif + +#ifndef CURL_DISABLE_DIGEST_AUTH +/* Struct used for Digest challenge-response authentication */ +struct digestdata { +#if defined(USE_WINDOWS_SSPI) + BYTE *input_token; + size_t input_token_len; + CtxtHandle *http_context; + /* copy of user/passwd used to make the identity for http_context. + either may be NULL. */ + char *user; + char *passwd; +#else + char *nonce; + char *cnonce; + char *realm; + char *opaque; + char *qop; + char *algorithm; + int nc; /* nonce count */ + unsigned char algo; + BIT(stale); /* set true for re-negotiation */ + BIT(userhash); +#endif +}; +#endif + +typedef enum { + NTLMSTATE_NONE, + NTLMSTATE_TYPE1, + NTLMSTATE_TYPE2, + NTLMSTATE_TYPE3, + NTLMSTATE_LAST +} curlntlm; + +typedef enum { + GSS_AUTHNONE, + GSS_AUTHRECV, + GSS_AUTHSENT, + GSS_AUTHDONE, + GSS_AUTHSUCC +} curlnegotiate; + +/* Struct used for GSSAPI (Kerberos V5) authentication */ +#if defined(USE_KERBEROS5) +struct kerberos5data { +#if defined(USE_WINDOWS_SSPI) + CredHandle *credentials; + CtxtHandle *context; + TCHAR *spn; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; + size_t token_max; + BYTE *output_token; +#else + gss_ctx_id_t context; + gss_name_t spn; +#endif +}; +#endif + +/* Struct used for SCRAM-SHA-1 authentication */ +#ifdef USE_GSASL +#include +struct gsasldata { + Gsasl *ctx; + Gsasl_session *client; +}; +#endif + +/* Struct used for NTLM challenge-response authentication */ +#if defined(USE_NTLM) +struct ntlmdata { +#ifdef USE_WINDOWS_SSPI +/* The sslContext is used for the Schannel bindings. The + * api is available on the Windows 7 SDK and later. + */ +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + CtxtHandle *sslContext; +#endif + CredHandle *credentials; + CtxtHandle *context; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; + size_t token_max; + BYTE *output_token; + BYTE *input_token; + size_t input_token_len; + TCHAR *spn; +#else + unsigned int flags; + unsigned char nonce[8]; + unsigned int target_info_len; + void *target_info; /* TargetInfo received in the ntlm type-2 message */ +#endif +}; +#endif + +/* Struct used for Negotiate (SPNEGO) authentication */ +#ifdef USE_SPNEGO +struct negotiatedata { +#ifdef HAVE_GSSAPI + OM_uint32 status; + gss_ctx_id_t context; + gss_name_t spn; + gss_buffer_desc output_token; +#else +#ifdef USE_WINDOWS_SSPI +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + CtxtHandle *sslContext; +#endif + DWORD status; + CredHandle *credentials; + CtxtHandle *context; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; + TCHAR *spn; + size_t token_max; + BYTE *output_token; + size_t output_token_length; +#endif +#endif + BIT(noauthpersist); + BIT(havenoauthpersist); + BIT(havenegdata); + BIT(havemultiplerequests); +}; +#endif + +#ifdef CURL_DISABLE_PROXY +#define CONN_IS_PROXIED(x) 0 +#else +#define CONN_IS_PROXIED(x) x->bits.proxy +#endif + +/* + * Boolean values that concerns this connection. + */ +struct ConnectBits { +#ifndef CURL_DISABLE_PROXY + BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */ + BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ + BIT(proxy_user_passwd); /* user+password for the proxy? */ + BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy. + This is implicit when SSL-protocols are used through + proxies, but can also be enabled explicitly by + apps */ + BIT(proxy_connect_closed); /* TRUE if a proxy disconnected the connection + in a CONNECT request with auth, so that + libcurl should reconnect and continue. */ + BIT(proxy); /* if set, this transfer is done through a proxy - any type */ +#endif + /* always modify bits.close with the connclose() and connkeep() macros! */ + BIT(close); /* if set, we close the connection after this request */ + BIT(reuse); /* if set, this is a reused connection */ + BIT(altused); /* this is an alt-svc "redirect" */ + BIT(conn_to_host); /* if set, this connection has a "connect to host" + that overrides the host in the URL */ + BIT(conn_to_port); /* if set, this connection has a "connect to port" + that overrides the port in the URL (remote port) */ + BIT(ipv6_ip); /* we communicate with a remote site specified with pure IPv6 + IP address */ + BIT(ipv6); /* we communicate with a site using an IPv6 address */ + BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is + supposed to be called, after ->curl_do() */ + BIT(protoconnstart);/* the protocol layer has STARTED its operation after + the TCP layer connect */ + BIT(retry); /* this connection is about to get closed and then + re-attempted at another connection. */ +#ifndef CURL_DISABLE_FTP + BIT(ftp_use_epsv); /* As set with CURLOPT_FTP_USE_EPSV, but if we find out + EPSV does not work we disable it for the forthcoming + requests */ + BIT(ftp_use_eprt); /* As set with CURLOPT_FTP_USE_EPRT, but if we find out + EPRT does not work we disable it for the forthcoming + requests */ + BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */ + BIT(ftp_use_control_ssl); /* Enabled SSL for the control connection */ +#endif +#ifndef CURL_DISABLE_NETRC + BIT(netrc); /* name+password provided by netrc */ +#endif + BIT(bound); /* set true if bind() has already been done on this socket/ + connection */ + BIT(multiplex); /* connection is multiplexed */ + BIT(tcp_fastopen); /* use TCP Fast Open */ + BIT(tls_enable_alpn); /* TLS ALPN extension? */ +#ifndef CURL_DISABLE_DOH + BIT(doh); +#endif +#ifdef USE_UNIX_SOCKETS + BIT(abstract_unix_socket); +#endif + BIT(tls_upgraded); + BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with + accept() */ + BIT(parallel_connect); /* set TRUE when a parallel connect attempt has + started (happy eyeballs) */ + BIT(aborted); /* connection was aborted, e.g. in unclean state */ + BIT(shutdown_handler); /* connection shutdown: handler shut down */ + BIT(shutdown_filters); /* connection shutdown: filters shut down */ +}; + +struct hostname { + char *rawalloc; /* allocated "raw" version of the name */ + char *encalloc; /* allocated IDN-encoded version of the name */ + char *name; /* name to use internally, might be encoded, might be raw */ + const char *dispname; /* name to display, as 'name' might be encoded */ +}; + +/* + * Flags on the keepon member of the Curl_transfer_keeper + */ + +#define KEEP_NONE 0 +#define KEEP_RECV (1<<0) /* there is or may be data to read */ +#define KEEP_SEND (1<<1) /* there is or may be data to write */ +#define KEEP_RECV_HOLD (1<<2) /* when set, no reading should be done but there + might still be data to read */ +#define KEEP_SEND_HOLD (1<<3) /* when set, no writing should be done but there + might still be data to write */ +#define KEEP_RECV_PAUSE (1<<4) /* reading is paused */ +#define KEEP_SEND_PAUSE (1<<5) /* writing is paused */ + +/* KEEP_SEND_TIMED is set when the transfer should attempt sending + * at timer (or other) events. A transfer waiting on a timer will + * remove KEEP_SEND to suppress POLLOUTs of the connection. + * Adding KEEP_SEND_TIMED will then attempt to send whenever the transfer + * enters the "readwrite" loop, e.g. when a timer fires. + * This is used in HTTP for 'Expect: 100-continue' waiting. */ +#define KEEP_SEND_TIMED (1<<6) + +#define KEEP_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE) +#define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE) + +/* transfer wants to send is not PAUSE or HOLD */ +#define CURL_WANT_SEND(data) \ + (((data)->req.keepon & KEEP_SENDBITS) == KEEP_SEND) +/* transfer receive is not on PAUSE or HOLD */ +#define CURL_WANT_RECV(data) \ + (((data)->req.keepon & KEEP_RECVBITS) == KEEP_RECV) + +#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH) +#define USE_CURL_ASYNC +struct Curl_async { + char *hostname; + struct Curl_dns_entry *dns; + struct thread_data *tdata; + void *resolver; /* resolver state, if it is used in the URL state - + ares_channel e.g. */ + int port; + int status; /* if done is TRUE, this is the status from the callback */ + BIT(done); /* set TRUE when the lookup is complete */ +}; + +#endif + +#define FIRSTSOCKET 0 +#define SECONDARYSOCKET 1 + +/* Polling requested by an easy handle. + * `action` is CURL_POLL_IN, CURL_POLL_OUT or CURL_POLL_INOUT. + */ +struct easy_pollset { + curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; + unsigned int num; + unsigned char actions[MAX_SOCKSPEREASYHANDLE]; +}; + +enum doh_slots { + /* Explicit values for first two symbols so as to match hard-coded + * constants in existing code + */ + DOH_PROBE_SLOT_IPADDR_V4 = 0, /* make 'V4' stand out for readability */ + DOH_PROBE_SLOT_IPADDR_V6 = 1, /* 'V6' likewise */ + + /* Space here for (possibly build-specific) additional slot definitions */ +#ifdef USE_HTTPSRR + DOH_PROBE_SLOT_HTTPS = 2, /* for HTTPS RR */ +#endif + + /* for example */ + /* #ifdef WANT_DOH_FOOBAR_TXT */ + /* DOH_PROBE_SLOT_FOOBAR_TXT, */ + /* #endif */ + + /* AFTER all slot definitions, establish how many we have */ + DOH_PROBE_SLOTS +}; + +/* + * Specific protocol handler. + */ + +struct Curl_handler { + const char *scheme; /* URL scheme name in lowercase */ + + /* Complement to setup_connection_internals(). This is done before the + transfer "owns" the connection. */ + CURLcode (*setup_connection)(struct Curl_easy *data, + struct connectdata *conn); + + /* These two functions MUST be set to be protocol dependent */ + CURLcode (*do_it)(struct Curl_easy *data, bool *done); + CURLcode (*done)(struct Curl_easy *, CURLcode, bool); + + /* If the curl_do() function is better made in two halves, this + * curl_do_more() function will be called afterwards, if set. For example + * for doing the FTP stuff after the PASV/PORT command. + */ + CURLcode (*do_more)(struct Curl_easy *, int *); + + /* This function *MAY* be set to a protocol-dependent function that is run + * after the connect() and everything is done, as a step in the connection. + * The 'done' pointer points to a bool that should be set to TRUE if the + * function completes before return. If it does not complete, the caller + * should call the ->connecting() function until it is. + */ + CURLcode (*connect_it)(struct Curl_easy *data, bool *done); + + /* See above. */ + CURLcode (*connecting)(struct Curl_easy *data, bool *done); + CURLcode (*doing)(struct Curl_easy *data, bool *done); + + /* Called from the multi interface during the PROTOCONNECT phase, and it + should then return a proper fd set */ + int (*proto_getsock)(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); + + /* Called from the multi interface during the DOING phase, and it should + then return a proper fd set */ + int (*doing_getsock)(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); + + /* Called from the multi interface during the DO_MORE phase, and it should + then return a proper fd set */ + int (*domore_getsock)(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); + + /* Called from the multi interface during the DO_DONE, PERFORM and + WAITPERFORM phases, and it should then return a proper fd set. Not setting + this will make libcurl use the generic default one. */ + int (*perform_getsock)(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); + + /* This function *MAY* be set to a protocol-dependent function that is run + * by the curl_disconnect(), as a step in the disconnection. If the handler + * is called because the connection has been considered dead, + * dead_connection is set to TRUE. The connection is (again) associated with + * the transfer here. + */ + CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, + bool dead_connection); + + /* If used, this function gets called from transfer.c to + allow the protocol to do extra handling in writing response to + the client. */ + CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen, + bool is_eos); + + /* If used, this function gets called from transfer.c to + allow the protocol to do extra handling in writing a single response + header line to the client. */ + CURLcode (*write_resp_hd)(struct Curl_easy *data, + const char *hd, size_t hdlen, bool is_eos); + + /* This function can perform various checks on the connection. See + CONNCHECK_* for more information about the checks that can be performed, + and CONNRESULT_* for the results that can be returned. */ + unsigned int (*connection_check)(struct Curl_easy *data, + struct connectdata *conn, + unsigned int checks_to_perform); + + /* attach() attaches this transfer to this connection */ + void (*attach)(struct Curl_easy *data, struct connectdata *conn); + + int defport; /* Default port. */ + curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single + specific protocol bit */ + curl_prot_t family; /* single bit for protocol family; basically the + non-TLS name of the protocol this is */ + unsigned int flags; /* Extra particular characteristics, see PROTOPT_* */ + +}; + +#define PROTOPT_NONE 0 /* nothing extra */ +#define PROTOPT_SSL (1<<0) /* uses SSL */ +#define PROTOPT_DUAL (1<<1) /* this protocol uses two connections */ +#define PROTOPT_CLOSEACTION (1<<2) /* need action before socket close */ +/* some protocols will have to call the underlying functions without regard to + what exact state the socket signals. IE even if the socket says "readable", + the send function might need to be called while uploading, or vice versa. +*/ +#define PROTOPT_DIRLOCK (1<<3) +#define PROTOPT_NONETWORK (1<<4) /* protocol does not use the network! */ +#define PROTOPT_NEEDSPWD (1<<5) /* needs a password, and if none is set it + gets a default */ +#define PROTOPT_NOURLQUERY (1<<6) /* protocol cannot handle + URL query strings (?foo=bar) ! */ +#define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per + request instead of per connection */ +#define PROTOPT_ALPN (1<<8) /* set ALPN for this */ +/* (1<<9) was PROTOPT_STREAM, now free */ +#define PROTOPT_URLOPTIONS (1<<10) /* allow options part in the userinfo field + of the URL */ +#define PROTOPT_PROXY_AS_HTTP (1<<11) /* allow this non-HTTP scheme over a + HTTP proxy as HTTP proxies may know + this protocol and act as a gateway */ +#define PROTOPT_WILDCARD (1<<12) /* protocol supports wildcard matching */ +#define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ascii) in + username and password */ +#define PROTOPT_NOTCPPROXY (1<<14) /* this protocol cannot proxy over TCP */ + +#define CONNCHECK_NONE 0 /* No checks */ +#define CONNCHECK_ISDEAD (1<<0) /* Check if the connection is dead. */ +#define CONNCHECK_KEEPALIVE (1<<1) /* Perform any keepalive function. */ + +#define CONNRESULT_NONE 0 /* No extra information. */ +#define CONNRESULT_DEAD (1<<0) /* The connection is dead. */ + +struct ip_quadruple { + char remote_ip[MAX_IPADR_LEN]; + char local_ip[MAX_IPADR_LEN]; + int remote_port; + int local_port; +}; + +struct proxy_info { + struct hostname host; + int port; + unsigned char proxytype; /* curl_proxytype: what kind of proxy that is in + use */ + char *user; /* proxy username string, allocated */ + char *passwd; /* proxy password string, allocated */ +}; + +struct ldapconninfo; + +#define TRNSPRT_TCP 3 +#define TRNSPRT_UDP 4 +#define TRNSPRT_QUIC 5 +#define TRNSPRT_UNIX 6 + +/* + * The connectdata struct contains all fields and variables that should be + * unique for an entire connection. + */ +struct connectdata { + struct Curl_llist_element bundle_node; /* conncache */ + + curl_closesocket_callback fclosesocket; /* function closing the socket(s) */ + void *closesocket_client; + + /* This is used by the connection cache logic. If this returns TRUE, this + handle is still used by one or more easy handles and can only used by any + other easy handle without careful consideration (== only for + multiplexing) and it cannot be used by another multi handle! */ +#define CONN_INUSE(c) ((c)->easyq.size) + + /**** Fields set when inited and not modified again */ + curl_off_t connection_id; /* Contains a unique number to make it easier to + track the connections in the log output */ + + /* 'dns_entry' is the particular host we use. This points to an entry in the + DNS cache and it will not get pruned while locked. It gets unlocked in + multi_done(). This entry will be NULL if the connection is reused as then + there is no name resolve done. */ + struct Curl_dns_entry *dns_entry; + + /* 'remote_addr' is the particular IP we connected to. it is owned, set + * and NULLed by the connected socket filter (if there is one). */ + const struct Curl_sockaddr_ex *remote_addr; + + struct hostname host; + char *hostname_resolve; /* hostname to resolve to address, allocated */ + char *secondaryhostname; /* secondary socket hostname (ftp) */ + struct hostname conn_to_host; /* the host to connect to. valid only if + bits.conn_to_host is set */ +#ifndef CURL_DISABLE_PROXY + struct proxy_info socks_proxy; + struct proxy_info http_proxy; +#endif + /* 'primary' and 'secondary' get filled with IP quadruple + (local/remote numerical ip address and port) whenever a is *attempted*. + When more than one address is tried for a connection these will hold data + for the last attempt. When the connection is actually established + these are updated with data which comes directly from the socket. */ + struct ip_quadruple primary; + struct ip_quadruple secondary; + char *user; /* username string, allocated */ + char *passwd; /* password string, allocated */ + char *options; /* options string, allocated */ + char *sasl_authzid; /* authorization identity string, allocated */ + char *oauth_bearer; /* OAUTH2 bearer, allocated */ + struct curltime now; /* "current" time */ + struct curltime created; /* creation time */ + struct curltime lastused; /* when returned to the connection cache */ + curl_socket_t sock[2]; /* two sockets, the second is used for the data + transfer when doing FTP */ + Curl_recv *recv[2]; + Curl_send *send[2]; + struct Curl_cfilter *cfilter[2]; /* connection filters */ + struct { + struct curltime start[2]; /* when filter shutdown started */ + unsigned int timeout_ms; /* 0 means no timeout */ + } shutdown; + /* Last pollset used in connection shutdown. Used to detect changes + * for multi_socket API. */ + struct easy_pollset shutdown_poll; + + struct ssl_primary_config ssl_config; +#ifndef CURL_DISABLE_PROXY + struct ssl_primary_config proxy_ssl_config; +#endif + struct ConnectBits bits; /* various state-flags for this connection */ + + const struct Curl_handler *handler; /* Connection's protocol handler */ + const struct Curl_handler *given; /* The protocol first given */ + + /* Protocols can use a custom keepalive mechanism to keep connections alive. + This allows those protocols to track the last time the keepalive mechanism + was used on this connection. */ + struct curltime keepalive; + + /**** curl_get() phase fields */ + + curl_socket_t sockfd; /* socket to read from or CURL_SOCKET_BAD */ + curl_socket_t writesockfd; /* socket to write to, it may very + well be the same we read from. + CURL_SOCKET_BAD disables */ + +#ifdef HAVE_GSSAPI + BIT(sec_complete); /* if Kerberos is enabled for this connection */ + unsigned char command_prot; /* enum protection_level */ + unsigned char data_prot; /* enum protection_level */ + unsigned char request_data_prot; /* enum protection_level */ + size_t buffer_size; + struct krb5buffer in_buffer; + void *app_data; + const struct Curl_sec_client_mech *mech; + struct sockaddr_in local_addr; +#endif + +#if defined(USE_KERBEROS5) /* Consider moving some of the above GSS-API */ + struct kerberos5data krb5; /* variables into the structure definition, */ +#endif /* however, some of them are ftp specific. */ + + struct Curl_llist easyq; /* List of easy handles using this connection */ + + /*************** Request - specific items ************/ +#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) + CtxtHandle *sslContext; +#endif + +#ifdef USE_GSASL + struct gsasldata gsasl; +#endif + +#if defined(USE_NTLM) + curlntlm http_ntlm_state; + curlntlm proxy_ntlm_state; + + struct ntlmdata ntlm; /* NTLM differs from other authentication schemes + because it authenticates connections, not + single requests! */ + struct ntlmdata proxyntlm; /* NTLM data for proxy */ +#endif + +#ifdef USE_SPNEGO + curlnegotiate http_negotiate_state; + curlnegotiate proxy_negotiate_state; + + struct negotiatedata negotiate; /* state data for host Negotiate auth */ + struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */ +#endif + + union { +#ifndef CURL_DISABLE_FTP + struct ftp_conn ftpc; +#endif +#ifdef USE_SSH + struct ssh_conn sshc; +#endif +#ifndef CURL_DISABLE_TFTP + struct tftp_state_data *tftpc; +#endif +#ifndef CURL_DISABLE_IMAP + struct imap_conn imapc; +#endif +#ifndef CURL_DISABLE_POP3 + struct pop3_conn pop3c; +#endif +#ifndef CURL_DISABLE_SMTP + struct smtp_conn smtpc; +#endif +#ifndef CURL_DISABLE_RTSP + struct rtsp_conn rtspc; +#endif +#ifndef CURL_DISABLE_SMB + struct smb_conn smbc; +#endif +#ifdef USE_LIBRTMP + void *rtmp; +#endif +#ifdef USE_OPENLDAP + struct ldapconninfo *ldapc; +#endif +#ifndef CURL_DISABLE_MQTT + struct mqtt_conn mqtt; +#endif +#ifdef USE_WEBSOCKETS + struct websocket *ws; +#endif + unsigned int unused:1; /* avoids empty union */ + } proto; + + struct connectbundle *bundle; /* The bundle we are member of */ +#ifdef USE_UNIX_SOCKETS + char *unix_domain_socket; +#endif +#ifdef USE_HYPER + /* if set, an alternative data transfer function */ + Curl_datastream datastream; +#endif + /* When this connection is created, store the conditions for the local end + bind. This is stored before the actual bind and before any connection is + made and will serve the purpose of being used for comparison reasons so + that subsequent bound-requested connections are not accidentally reusing + wrong connections. */ + char *localdev; + unsigned short localportrange; + int waitfor; /* current READ/WRITE bits to wait for */ +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + int socks5_gssapi_enctype; +#endif + /* The field below gets set in connect.c:connecthost() */ + int remote_port; /* the remote port, not the proxy port! */ + int conn_to_port; /* the remote port to connect to. valid only if + bits.conn_to_port is set */ +#ifdef USE_IPV6 + unsigned int scope_id; /* Scope id for IPv6 */ +#endif + unsigned short localport; + unsigned short secondary_port; /* secondary socket remote port to connect to + (ftp) */ + unsigned char alpn; /* APLN TLS negotiated protocol, a CURL_HTTP_VERSION* + value */ +#ifndef CURL_DISABLE_PROXY + unsigned char proxy_alpn; /* APLN of proxy tunnel, CURL_HTTP_VERSION* */ +#endif + unsigned char transport; /* one of the TRNSPRT_* defines */ + unsigned char ip_version; /* copied from the Curl_easy at creation time */ + unsigned char httpversion; /* the HTTP version*10 reported by the server */ + unsigned char connect_only; + unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */ +}; + +#ifndef CURL_DISABLE_PROXY +#define CURL_CONN_HOST_DISPNAME(c) \ + ((c)->bits.socksproxy ? (c)->socks_proxy.host.dispname : \ + (c)->bits.httpproxy ? (c)->http_proxy.host.dispname : \ + (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ + (c)->host.dispname) +#else +#define CURL_CONN_HOST_DISPNAME(c) \ + (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ + (c)->host.dispname +#endif + +/* The end of connectdata. */ + +/* + * Struct to keep statistical and informational data. + * All variables in this struct must be initialized/reset in Curl_initinfo(). + */ +struct PureInfo { + int httpcode; /* Recent HTTP, FTP, RTSP or SMTP response code */ + int httpproxycode; /* response code from proxy when received separate */ + int httpversion; /* the http version number X.Y = X*10+Y */ + time_t filetime; /* If requested, this is might get set. Set to -1 if the + time was unretrievable. */ + curl_off_t request_size; /* the amount of bytes sent in the request(s) */ + unsigned long proxyauthavail; /* what proxy auth types were announced */ + unsigned long httpauthavail; /* what host auth types were announced */ + long numconnects; /* how many new connection did libcurl created */ + char *contenttype; /* the content type of the object */ + char *wouldredirect; /* URL this would have been redirected to if asked to */ + curl_off_t retry_after; /* info from Retry-After: header */ + unsigned int header_size; /* size of read header(s) in bytes */ + + /* PureInfo primary ip_quadruple is copied over from the connectdata + struct in order to allow curl_easy_getinfo() to return this information + even when the session handle is no longer associated with a connection, + and also allow curl_easy_reset() to clear this information from the + session handle without disturbing information which is still alive, and + that might be reused, in the connection cache. */ + struct ip_quadruple primary; + int conn_remote_port; /* this is the "remote port", which is the port + number of the used URL, independent of proxy or + not */ + const char *conn_scheme; + unsigned int conn_protocol; + struct curl_certinfo certs; /* info about the certs. Asked for with + CURLOPT_CERTINFO / CURLINFO_CERTINFO */ + CURLproxycode pxcode; + BIT(timecond); /* set to TRUE if the time condition did not match, which + thus made the document NOT get fetched */ + BIT(used_proxy); /* the transfer used a proxy */ +}; + + +struct Progress { + time_t lastshow; /* time() of the last displayed progress meter or NULL to + force redraw at next call */ + curl_off_t size_dl; /* total expected size */ + curl_off_t size_ul; /* total expected size */ + curl_off_t downloaded; /* transferred so far */ + curl_off_t uploaded; /* transferred so far */ + + curl_off_t current_speed; /* uses the currently fastest transfer */ + + int width; /* screen width at download start */ + int flags; /* see progress.h */ + + timediff_t timespent; + + curl_off_t dlspeed; + curl_off_t ulspeed; + + timediff_t t_postqueue; + timediff_t t_nslookup; + timediff_t t_connect; + timediff_t t_appconnect; + timediff_t t_pretransfer; + timediff_t t_starttransfer; + timediff_t t_redirect; + + struct curltime start; + struct curltime t_startsingle; + struct curltime t_startop; + struct curltime t_acceptdata; + + + /* upload speed limit */ + struct curltime ul_limit_start; + curl_off_t ul_limit_size; + /* download speed limit */ + struct curltime dl_limit_start; + curl_off_t dl_limit_size; + +#define CURR_TIME (5 + 1) /* 6 entries for 5 seconds */ + + curl_off_t speeder[ CURR_TIME ]; + struct curltime speeder_time[ CURR_TIME ]; + int speeder_c; + BIT(callback); /* set when progress callback is used */ + BIT(is_t_startransfer_set); +}; + +typedef enum { + RTSPREQ_NONE, /* first in list */ + RTSPREQ_OPTIONS, + RTSPREQ_DESCRIBE, + RTSPREQ_ANNOUNCE, + RTSPREQ_SETUP, + RTSPREQ_PLAY, + RTSPREQ_PAUSE, + RTSPREQ_TEARDOWN, + RTSPREQ_GET_PARAMETER, + RTSPREQ_SET_PARAMETER, + RTSPREQ_RECORD, + RTSPREQ_RECEIVE, + RTSPREQ_LAST /* last in list */ +} Curl_RtspReq; + +struct auth { + unsigned long want; /* Bitmask set to the authentication methods wanted by + app (with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */ + unsigned long picked; + unsigned long avail; /* Bitmask for what the server reports to support for + this resource */ + BIT(done); /* TRUE when the auth phase is done and ready to do the + actual request */ + BIT(multipass); /* TRUE if this is not yet authenticated but within the + auth multipass negotiation */ + BIT(iestyle); /* TRUE if digest should be done IE-style or FALSE if it + should be RFC compliant */ +}; + +#ifdef USE_NGHTTP2 +struct Curl_data_prio_node { + struct Curl_data_prio_node *next; + struct Curl_easy *data; +}; +#endif + +/** + * Priority information for an easy handle in relation to others + * on the same connection. + * TODO: we need to adapt it to the new priority scheme as defined in RFC 9218 + */ +struct Curl_data_priority { +#ifdef USE_NGHTTP2 + /* tree like dependencies only implemented in nghttp2 */ + struct Curl_easy *parent; + struct Curl_data_prio_node *children; +#endif + int weight; +#ifdef USE_NGHTTP2 + BIT(exclusive); +#endif +}; + +/* Timers */ +typedef enum { + EXPIRE_100_TIMEOUT, + EXPIRE_ASYNC_NAME, + EXPIRE_CONNECTTIMEOUT, + EXPIRE_DNS_PER_NAME, /* family1 */ + EXPIRE_DNS_PER_NAME2, /* family2 */ + EXPIRE_HAPPY_EYEBALLS_DNS, /* See asyn-ares.c */ + EXPIRE_HAPPY_EYEBALLS, + EXPIRE_MULTI_PENDING, + EXPIRE_RUN_NOW, + EXPIRE_SPEEDCHECK, + EXPIRE_TIMEOUT, + EXPIRE_TOOFAST, + EXPIRE_QUIC, + EXPIRE_FTP_ACCEPT, + EXPIRE_ALPN_EYEBALLS, + EXPIRE_LAST /* not an actual timer, used as a marker only */ +} expire_id; + + +typedef enum { + TRAILERS_NONE, + TRAILERS_INITIALIZED, + TRAILERS_SENDING, + TRAILERS_DONE +} trailers_state; + + +/* + * One instance for each timeout an easy handle can set. + */ +struct time_node { + struct Curl_llist_element list; + struct curltime time; + expire_id eid; +}; + +/* individual pieces of the URL */ +struct urlpieces { + char *scheme; + char *hostname; + char *port; + char *user; + char *password; + char *options; + char *path; + char *query; +}; + +struct UrlState { + /* Points to the connection cache */ + struct conncache *conn_cache; + /* buffers to store authentication data in, as parsed from input options */ + struct curltime keeps_speed; /* for the progress meter really */ + + curl_off_t lastconnect_id; /* The last connection, -1 if undefined */ + curl_off_t recent_conn_id; /* The most recent connection used, might no + * longer exist */ + struct dynbuf headerb; /* buffer to store headers in */ + struct curl_slist *hstslist; /* list of HSTS files set by + curl_easy_setopt(HSTS) calls */ + curl_off_t current_speed; /* the ProgressShow() function sets this, + bytes / second */ + + /* hostname, port number and protocol of the first (not followed) request. + if set, this should be the hostname that we will sent authorization to, + no else. Used to make Location: following not keep sending user+password. + This is strdup()ed data. */ + char *first_host; + int first_remote_port; + curl_prot_t first_remote_protocol; + + int retrycount; /* number of retries on a new connection */ + struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */ + long sessionage; /* number of the most recent session */ + int os_errno; /* filled in with errno whenever an error occurs */ + char *scratch; /* huge buffer[set.buffer_size*2] for upload CRLF replacing */ + long followlocation; /* redirect counter */ + int requests; /* request counter: redirects + authentication retakes */ +#ifdef HAVE_SIGNAL + /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */ + void (*prev_signal)(int sig); +#endif +#ifndef CURL_DISABLE_DIGEST_AUTH + struct digestdata digest; /* state data for host Digest auth */ + struct digestdata proxydigest; /* state data for proxy Digest auth */ +#endif + struct auth authhost; /* auth details for host */ + struct auth authproxy; /* auth details for proxy */ +#ifdef USE_CURL_ASYNC + struct Curl_async async; /* asynchronous name resolver data */ +#endif + +#if defined(USE_OPENSSL) + /* void instead of ENGINE to avoid bleeding OpenSSL into this header */ + void *engine; +#endif /* USE_OPENSSL */ + struct curltime expiretime; /* set this with Curl_expire() only */ + struct Curl_tree timenode; /* for the splay stuff */ + struct Curl_llist timeoutlist; /* list of pending timeouts */ + struct time_node expires[EXPIRE_LAST]; /* nodes for each expire type */ + + /* a place to store the most recently set (S)FTP entrypath */ + char *most_recent_ftp_entrypath; +#if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__) +/* do FTP line-end conversions on most platforms */ +#define CURL_DO_LINEEND_CONV + /* for FTP downloads: how many CRLFs did we converted to LFs? */ + curl_off_t crlf_conversions; +#endif + char *range; /* range, if used. See README for detailed specification on + this syntax. */ + curl_off_t resume_from; /* continue [ftp] transfer from here */ + +#ifndef CURL_DISABLE_RTSP + /* This RTSP state information survives requests and connections */ + long rtsp_next_client_CSeq; /* the session's next client CSeq */ + long rtsp_next_server_CSeq; /* the session's next server CSeq */ + long rtsp_CSeq_recv; /* most recent CSeq received */ + + unsigned char rtp_channel_mask[32]; /* for the correctness checking of the + interleaved data */ +#endif + + curl_off_t infilesize; /* size of file to upload, -1 means unknown. + Copied from set.filesize at start of operation */ +#if defined(USE_HTTP2) || defined(USE_HTTP3) + struct Curl_data_priority priority; /* shallow copy of data->set */ +#endif + + curl_read_callback fread_func; /* read callback/function */ + void *in; /* CURLOPT_READDATA */ + CURLU *uh; /* URL handle for the current parsed URL */ + struct urlpieces up; + char *url; /* work URL, copied from UserDefined */ + char *referer; /* referer string */ + struct curl_slist *resolve; /* set to point to the set.resolve list when + this should be dealt with in pretransfer */ +#ifndef CURL_DISABLE_HTTP + curl_mimepart *mimepost; +#ifndef CURL_DISABLE_FORM_API + curl_mimepart *formp; /* storage for old API form-posting, allocated on + demand */ +#endif + size_t trailers_bytes_sent; + struct dynbuf trailers_buf; /* a buffer containing the compiled trailing + headers */ + struct Curl_llist httphdrs; /* received headers */ + struct curl_header headerout[2]; /* for external purposes */ + struct Curl_header_store *prevhead; /* the latest added header */ + trailers_state trailers_state; /* whether we are sending trailers + and what stage are we at */ +#endif +#ifndef CURL_DISABLE_COOKIES + struct curl_slist *cookielist; /* list of cookie files set by + curl_easy_setopt(COOKIEFILE) calls */ +#endif +#ifdef USE_HYPER + bool hconnect; /* set if a CONNECT request */ + CURLcode hresult; /* used to pass return codes back from hyper callbacks */ +#endif + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + struct curl_trc_feat *feat; /* opt. trace feature transfer is part of */ +#endif + + /* Dynamically allocated strings, MUST be freed before this struct is + killed. */ + struct dynamically_allocated_data { + char *uagent; + char *accept_encoding; + char *userpwd; + char *rangeline; + char *ref; + char *host; +#ifndef CURL_DISABLE_COOKIES + char *cookiehost; +#endif +#ifndef CURL_DISABLE_RTSP + char *rtsp_transport; +#endif + char *te; /* TE: request header */ + + /* transfer credentials */ + char *user; + char *passwd; +#ifndef CURL_DISABLE_PROXY + char *proxyuserpwd; + char *proxyuser; + char *proxypasswd; +#endif + } aptr; + + unsigned char httpwant; /* when non-zero, a specific HTTP version requested + to be used in the library's request(s) */ + unsigned char httpversion; /* the lowest HTTP version*10 reported by any + server involved in this request */ + unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) + is this */ + unsigned char select_bits; /* != 0 -> bitmask of socket events for this + transfer overriding anything the socket may + report */ +#ifdef DEBUGBUILD + BIT(conncache_lock); +#endif + /* when curl_easy_perform() is called, the multi handle is "owned" by + the easy handle so curl_easy_cleanup() on such an easy handle will + also close the multi handle! */ + BIT(multi_owned_by_easy); + + BIT(this_is_a_follow); /* this is a followed Location: request */ + BIT(refused_stream); /* this was refused, try again */ + BIT(errorbuf); /* Set to TRUE if the error buffer is already filled in. + This must be set to FALSE every time _easy_perform() is + called. */ + BIT(allow_port); /* Is set.use_port allowed to take effect or not. This + is always set TRUE when curl_easy_perform() is called. */ + BIT(authproblem); /* TRUE if there is some problem authenticating */ + /* set after initial USER failure, to prevent an authentication loop */ + BIT(wildcardmatch); /* enable wildcard matching */ + BIT(disableexpect); /* TRUE if Expect: is disabled due to a previous + 417 response */ + BIT(use_range); + BIT(rangestringalloc); /* the range string is malloc()'ed */ + BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE + when multi_done() is called, to prevent multi_done() to get + invoked twice when the multi interface is used. */ +#ifndef CURL_DISABLE_COOKIES + BIT(cookie_engine); +#endif + BIT(prefer_ascii); /* ASCII rather than binary */ +#ifdef CURL_LIST_ONLY_PROTOCOL + BIT(list_only); /* list directory contents */ +#endif + BIT(url_alloc); /* URL string is malloc()'ed */ + BIT(referer_alloc); /* referer string is malloc()ed */ + BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */ + BIT(upload); /* upload request */ + BIT(internal); /* internal: true if this easy handle was created for + internal use and the user does not have ownership of the + handle. */ +}; + +/* + * This 'UserDefined' struct must only contain data that is set once to go + * for many (perhaps) independent connections. Values that are generated or + * calculated internally for the "session handle" MUST be defined within the + * 'struct UrlState' instead. The only exceptions MUST note the changes in + * the 'DynamicStatic' struct. + * Character pointer fields point to dynamic storage, unless otherwise stated. + */ + +struct Curl_multi; /* declared in multihandle.c */ + +enum dupstring { + STRING_CERT, /* client certificate filename */ + STRING_CERT_TYPE, /* format for certificate (default: PEM)*/ + STRING_KEY, /* private key filename */ + STRING_KEY_PASSWD, /* plain text private key password */ + STRING_KEY_TYPE, /* format for private key (default: PEM) */ + STRING_SSL_CAPATH, /* CA directory name (does not work on windows) */ + STRING_SSL_CAFILE, /* certificate file to verify peer against */ + STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */ + STRING_SSL_CIPHER_LIST, /* list of ciphers to use */ + STRING_SSL_CIPHER13_LIST, /* list of TLS 1.3 ciphers to use */ + STRING_SSL_CRLFILE, /* crl file to check certificate */ + STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */ + STRING_SERVICE_NAME, /* Service name */ +#ifndef CURL_DISABLE_PROXY + STRING_CERT_PROXY, /* client certificate filename */ + STRING_CERT_TYPE_PROXY, /* format for certificate (default: PEM)*/ + STRING_KEY_PROXY, /* private key filename */ + STRING_KEY_PASSWD_PROXY, /* plain text private key password */ + STRING_KEY_TYPE_PROXY, /* format for private key (default: PEM) */ + STRING_SSL_CAPATH_PROXY, /* CA directory name (does not work on windows) */ + STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */ + STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */ + STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */ + STRING_SSL_CIPHER13_LIST_PROXY, /* list of TLS 1.3 ciphers to use */ + STRING_SSL_CRLFILE_PROXY, /* crl file to check certificate */ + STRING_SSL_ISSUERCERT_PROXY, /* issuer cert file to check certificate */ + STRING_PROXY_SERVICE_NAME, /* Proxy service name */ +#endif +#ifndef CURL_DISABLE_COOKIES + STRING_COOKIE, /* HTTP cookie string to send */ + STRING_COOKIEJAR, /* dump all cookies to this file */ +#endif + STRING_CUSTOMREQUEST, /* HTTP/FTP/RTSP request/method to use */ + STRING_DEFAULT_PROTOCOL, /* Protocol to use when the URL does not specify */ + STRING_DEVICE, /* local network interface/address to use */ + STRING_INTERFACE, /* local network interface to use */ + STRING_BINDHOST, /* local address to use */ + STRING_ENCODING, /* Accept-Encoding string */ +#ifndef CURL_DISABLE_FTP + STRING_FTP_ACCOUNT, /* ftp account data */ + STRING_FTP_ALTERNATIVE_TO_USER, /* command to send if USER/PASS fails */ + STRING_FTPPORT, /* port to send with the FTP PORT command */ +#endif +#if defined(HAVE_GSSAPI) + STRING_KRB_LEVEL, /* krb security level */ +#endif +#ifndef CURL_DISABLE_NETRC + STRING_NETRC_FILE, /* if not NULL, use this instead of trying to find + $HOME/.netrc */ +#endif +#ifndef CURL_DISABLE_PROXY + STRING_PROXY, /* proxy to use */ + STRING_PRE_PROXY, /* pre socks proxy to use */ +#endif + STRING_SET_RANGE, /* range, if used */ + STRING_SET_REFERER, /* custom string for the HTTP referer field */ + STRING_SET_URL, /* what original URL to work on */ + STRING_USERAGENT, /* User-Agent string */ + STRING_SSL_ENGINE, /* name of ssl engine */ + STRING_USERNAME, /* , if used */ + STRING_PASSWORD, /* , if used */ + STRING_OPTIONS, /* , if used */ +#ifndef CURL_DISABLE_PROXY + STRING_PROXYUSERNAME, /* Proxy , if used */ + STRING_PROXYPASSWORD, /* Proxy , if used */ + STRING_NOPROXY, /* List of hosts which should not use the proxy, if + used */ +#endif +#ifndef CURL_DISABLE_RTSP + STRING_RTSP_SESSION_ID, /* Session ID to use */ + STRING_RTSP_STREAM_URI, /* Stream URI for this request */ + STRING_RTSP_TRANSPORT, /* Transport for this session */ +#endif +#ifdef USE_SSH + STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */ + STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */ + STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */ + STRING_SSH_HOST_PUBLIC_KEY_SHA256, /* sha256 of host public key in base64 */ + STRING_SSH_KNOWNHOSTS, /* filename of knownhosts file */ +#endif +#ifndef CURL_DISABLE_SMTP + STRING_MAIL_FROM, + STRING_MAIL_AUTH, +#endif +#ifdef USE_TLS_SRP + STRING_TLSAUTH_USERNAME, /* TLS auth */ + STRING_TLSAUTH_PASSWORD, /* TLS auth */ +#ifndef CURL_DISABLE_PROXY + STRING_TLSAUTH_USERNAME_PROXY, /* TLS auth */ + STRING_TLSAUTH_PASSWORD_PROXY, /* TLS auth */ +#endif +#endif + STRING_BEARER, /* , if used */ +#ifdef USE_UNIX_SOCKETS + STRING_UNIX_SOCKET_PATH, /* path to Unix socket, if used */ +#endif + STRING_TARGET, /* CURLOPT_REQUEST_TARGET */ +#ifndef CURL_DISABLE_DOH + STRING_DOH, /* CURLOPT_DOH_URL */ +#endif +#ifndef CURL_DISABLE_ALTSVC + STRING_ALTSVC, /* CURLOPT_ALTSVC */ +#endif +#ifndef CURL_DISABLE_HSTS + STRING_HSTS, /* CURLOPT_HSTS */ +#endif + STRING_SASL_AUTHZID, /* CURLOPT_SASL_AUTHZID */ +#ifdef USE_ARES + STRING_DNS_SERVERS, + STRING_DNS_INTERFACE, + STRING_DNS_LOCAL_IP4, + STRING_DNS_LOCAL_IP6, +#endif + STRING_SSL_EC_CURVES, +#ifndef CURL_DISABLE_AWS + STRING_AWS_SIGV4, /* Parameters for V4 signature */ +#endif +#ifndef CURL_DISABLE_PROXY + STRING_HAPROXY_CLIENT_IP, /* CURLOPT_HAPROXY_CLIENT_IP */ +#endif + STRING_ECH_CONFIG, /* CURLOPT_ECH_CONFIG */ + STRING_ECH_PUBLIC, /* CURLOPT_ECH_PUBLIC */ + + /* -- end of null-terminated strings -- */ + + STRING_LASTZEROTERMINATED, + + /* -- below this are pointers to binary data that cannot be strdup'ed. --- */ + + STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ + + STRING_LAST /* not used, just an end-of-list marker */ +}; + +enum dupblob { + BLOB_CERT, + BLOB_KEY, + BLOB_SSL_ISSUERCERT, + BLOB_CAINFO, +#ifndef CURL_DISABLE_PROXY + BLOB_CERT_PROXY, + BLOB_KEY_PROXY, + BLOB_SSL_ISSUERCERT_PROXY, + BLOB_CAINFO_PROXY, +#endif + BLOB_LAST +}; + +/* callback that gets called when this easy handle is completed within a multi + handle. Only used for internally created transfers, like for example + DoH. */ +typedef int (*multidone_func)(struct Curl_easy *easy, CURLcode result); + +struct UserDefined { + FILE *err; /* the stderr user data goes here */ + void *debugdata; /* the data that will be passed to fdebug */ + char *errorbuffer; /* (Static) store failure messages in here */ + void *out; /* CURLOPT_WRITEDATA */ + void *in_set; /* CURLOPT_READDATA */ + void *writeheader; /* write the header to this if non-NULL */ + unsigned short use_port; /* which port to use (when not using default) */ + unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */ + unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */ + long maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 + for infinity */ + + void *postfields; /* if POST, set the fields' values here */ + curl_seek_callback seek_func; /* function that seeks the input */ + curl_off_t postfieldsize; /* if POST, this might have a size to use instead + of strlen(), and then the data *may* be binary + (contain zero bytes) */ +#ifndef CURL_DISABLE_BINDLOCAL + unsigned short localport; /* local port number to bind to */ + unsigned short localportrange; /* number of additional port numbers to test + in case the 'localport' one cannot be + bind()ed */ +#endif + curl_write_callback fwrite_func; /* function that stores the output */ + curl_write_callback fwrite_header; /* function that stores headers */ + curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */ + curl_read_callback fread_func_set; /* function that reads the input */ + curl_progress_callback fprogress; /* OLD and deprecated progress callback */ + curl_xferinfo_callback fxferinfo; /* progress callback */ + curl_debug_callback fdebug; /* function that write informational data */ + curl_ioctl_callback ioctl_func; /* function for I/O control */ + curl_sockopt_callback fsockopt; /* function for setting socket options */ + void *sockopt_client; /* pointer to pass to the socket options callback */ + curl_opensocket_callback fopensocket; /* function for checking/translating + the address and opening the + socket */ + void *opensocket_client; + curl_closesocket_callback fclosesocket; /* function for closing the + socket */ + void *closesocket_client; + curl_prereq_callback fprereq; /* pre-initial request callback */ + void *prereq_userp; /* pre-initial request user data */ + + void *seek_client; /* pointer to pass to the seek callback */ +#ifndef CURL_DISABLE_HSTS + curl_hstsread_callback hsts_read; + void *hsts_read_userp; + curl_hstswrite_callback hsts_write; + void *hsts_write_userp; +#endif + void *progress_client; /* pointer to pass to the progress callback */ + void *ioctl_client; /* pointer to pass to the ioctl callback */ + unsigned int timeout; /* ms, 0 means no timeout */ + unsigned int connecttimeout; /* ms, 0 means default timeout */ + unsigned int happy_eyeballs_timeout; /* ms, 0 is a valid value */ + unsigned int server_response_timeout; /* ms, 0 means no timeout */ + unsigned int shutdowntimeout; /* ms, 0 means default timeout */ + long maxage_conn; /* in seconds, max idle time to allow a connection that + is to be reused */ + long maxlifetime_conn; /* in seconds, max time since creation to allow a + connection that is to be reused */ +#ifndef CURL_DISABLE_TFTP + long tftp_blksize; /* in bytes, 0 means use default */ +#endif + curl_off_t filesize; /* size of file to upload, -1 means unknown */ + long low_speed_limit; /* bytes/second */ + long low_speed_time; /* number of seconds */ + curl_off_t max_send_speed; /* high speed limit in bytes/second for upload */ + curl_off_t max_recv_speed; /* high speed limit in bytes/second for + download */ + curl_off_t set_resume_from; /* continue [ftp] transfer from here */ + struct curl_slist *headers; /* linked list of extra headers */ + struct curl_httppost *httppost; /* linked list of old POST data */ +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) + curl_mimepart mimepost; /* MIME/POST data. */ +#endif +#ifndef CURL_DISABLE_TELNET + struct curl_slist *telnet_options; /* linked list of telnet options */ +#endif + struct curl_slist *resolve; /* list of names to add/remove from + DNS cache */ + struct curl_slist *connect_to; /* list of host:port mappings to override + the hostname and port to connect to */ + time_t timevalue; /* what time to compare with */ + unsigned char timecondition; /* kind of time comparison: curl_TimeCond */ + unsigned char method; /* what kind of HTTP request: Curl_HttpReq */ + unsigned char httpwant; /* when non-zero, a specific HTTP version requested + to be used in the library's request(s) */ + struct ssl_config_data ssl; /* user defined SSL stuff */ +#ifndef CURL_DISABLE_PROXY + struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */ + struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */ + unsigned short proxyport; /* If non-zero, use this port number by + default. If the proxy string features a + ":[port]" that one will override this. */ + unsigned char proxytype; /* what kind of proxy: curl_proxytype */ + unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */ +#endif + struct ssl_general_config general_ssl; /* general user defined SSL stuff */ + int dns_cache_timeout; /* DNS cache timeout (seconds) */ + unsigned int buffer_size; /* size of receive buffer to use */ + unsigned int upload_buffer_size; /* size of upload buffer to use, + keep it >= CURL_MAX_WRITE_SIZE */ + void *private_data; /* application-private data */ +#ifndef CURL_DISABLE_HTTP + struct curl_slist *http200aliases; /* linked list of aliases for http200 */ +#endif + unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header + file 0 - whatever, 1 - v2, 2 - v6 */ + curl_off_t max_filesize; /* Maximum file size to download */ +#ifndef CURL_DISABLE_FTP + unsigned char ftp_filemethod; /* how to get to a file: curl_ftpfile */ + unsigned char ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */ + unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */ + unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */ +#endif +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) + struct curl_slist *quote; /* after connection is established */ + struct curl_slist *postquote; /* after the transfer */ + struct curl_slist *prequote; /* before the transfer, after type */ + /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP + 1 - create directories that do not exist + 2 - the same but also allow MKD to fail once + */ + unsigned char ftp_create_missing_dirs; +#endif +#ifdef USE_LIBSSH2 + curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */ + void *ssh_hostkeyfunc_userp; /* custom pointer to callback */ +#endif +#ifdef USE_SSH + curl_sshkeycallback ssh_keyfunc; /* key matching callback */ + void *ssh_keyfunc_userp; /* custom pointer to callback */ + int ssh_auth_types; /* allowed SSH auth types */ + unsigned int new_directory_perms; /* when creating remote dirs */ +#endif +#ifndef CURL_DISABLE_NETRC + unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */ +#endif + unsigned int new_file_perms; /* when creating remote files */ + char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ + struct curl_blob *blobs[BLOB_LAST]; +#ifdef USE_IPV6 + unsigned int scope_id; /* Scope id for IPv6 */ +#endif + curl_prot_t allowed_protocols; + curl_prot_t redir_protocols; +#ifndef CURL_DISABLE_RTSP + void *rtp_out; /* write RTP to this if non-NULL */ + /* Common RTSP header options */ + Curl_RtspReq rtspreq; /* RTSP request type */ +#endif +#ifndef CURL_DISABLE_FTP + curl_chunk_bgn_callback chunk_bgn; /* called before part of transfer + starts */ + curl_chunk_end_callback chunk_end; /* called after part transferring + stopped */ + curl_fnmatch_callback fnmatch; /* callback to decide which file corresponds + to pattern (e.g. if WILDCARDMATCH is on) */ + void *fnmatch_data; + void *wildcardptr; +#endif + /* GSS-API credential delegation, see the documentation of + CURLOPT_GSSAPI_DELEGATION */ + unsigned char gssapi_delegation; + + int tcp_keepidle; /* seconds in idle before sending keepalive probe */ + int tcp_keepintvl; /* seconds between TCP keepalive probes */ + int tcp_keepcnt; /* maximum number of keepalive probes */ + + long expect_100_timeout; /* in milliseconds */ +#if defined(USE_HTTP2) || defined(USE_HTTP3) + struct Curl_data_priority priority; +#endif + curl_resolver_start_callback resolver_start; /* optional callback called + before resolver start */ + void *resolver_start_client; /* pointer to pass to resolver start callback */ + long upkeep_interval_ms; /* Time between calls for connection upkeep. */ + multidone_func fmultidone; +#ifndef CURL_DISABLE_DOH + struct Curl_easy *dohfor; /* this is a DoH request for that transfer */ +#endif + CURLU *uh; /* URL handle for the current parsed URL */ +#ifndef CURL_DISABLE_HTTP + void *trailer_data; /* pointer to pass to trailer data callback */ + curl_trailer_callback trailer_callback; /* trailing data callback */ +#endif + char keep_post; /* keep POSTs as POSTs after a 30x request; each + bit represents a request, from 301 to 303 */ +#ifndef CURL_DISABLE_SMTP + struct curl_slist *mail_rcpt; /* linked list of mail recipients */ + BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some + recipients */ +#endif + unsigned int maxconnects; /* Max idle connections in the connection cache */ + unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or + IMAP or POP3 or others! (type: curl_usessl)*/ + unsigned char connect_only; /* make connection/request, then let + application use the socket */ +#ifndef CURL_DISABLE_MIME + BIT(mime_formescape); +#endif + BIT(is_fread_set); /* has read callback been set to non-NULL? */ +#ifndef CURL_DISABLE_TFTP + BIT(tftp_no_options); /* do not send TFTP options requests */ +#endif + BIT(sep_headers); /* handle host and proxy headers separately */ +#ifndef CURL_DISABLE_COOKIES + BIT(cookiesession); /* new cookie session? */ +#endif + BIT(crlf); /* convert crlf on ftp upload(?) */ +#ifdef USE_SSH + BIT(ssh_compression); /* enable SSH compression */ +#endif + +/* Here follows boolean settings that define how to behave during + this session. They are STATIC, set by libcurl users or at least initially + and they do not change during operations. */ + BIT(quick_exit); /* set 1L when it is okay to leak things (like + threads), as we are about to exit() anyway and + do not want lengthy cleanups to delay termination, + e.g. after a DNS timeout */ + BIT(get_filetime); /* get the time and get of the remote file */ +#ifndef CURL_DISABLE_PROXY + BIT(tunnel_thru_httpproxy); /* use CONNECT through an HTTP proxy */ +#endif + BIT(prefer_ascii); /* ASCII rather than binary */ + BIT(remote_append); /* append, not overwrite, on upload */ +#ifdef CURL_LIST_ONLY_PROTOCOL + BIT(list_only); /* list directory */ +#endif +#ifndef CURL_DISABLE_FTP + BIT(ftp_use_port); /* use the FTP PORT command */ + BIT(ftp_use_epsv); /* if EPSV is to be attempted or not */ + BIT(ftp_use_eprt); /* if EPRT is to be attempted or not */ + BIT(ftp_use_pret); /* if PRET is to be used before PASV or not */ + BIT(ftp_skip_ip); /* skip the IP address the FTP server passes on to + us */ + BIT(wildcard_enabled); /* enable wildcard matching */ +#endif + BIT(hide_progress); /* do not use the progress meter */ + BIT(http_fail_on_error); /* fail on HTTP error codes >= 400 */ + BIT(http_keep_sending_on_error); /* for HTTP status codes >= 300 */ + BIT(http_follow_location); /* follow HTTP redirects */ + BIT(http_transfer_encoding); /* request compressed HTTP transfer-encoding */ + BIT(allow_auth_to_other_hosts); + BIT(include_header); /* include received protocol headers in data output */ + BIT(http_set_referer); /* is a custom referer used */ + BIT(http_auto_referer); /* set "correct" referer when following + location: */ + BIT(opt_no_body); /* as set with CURLOPT_NOBODY */ + BIT(verbose); /* output verbosity */ +#if defined(HAVE_GSSAPI) + BIT(krb); /* Kerberos connection requested */ +#endif + BIT(reuse_forbid); /* forbidden to be reused, close after use */ + BIT(reuse_fresh); /* do not reuse an existing connection */ + BIT(no_signal); /* do not use any signal/alarm handler */ + BIT(tcp_nodelay); /* whether to enable TCP_NODELAY or not */ + BIT(ignorecl); /* ignore content length */ + BIT(http_te_skip); /* pass the raw body data to the user, even when + transfer-encoded (chunked, compressed) */ + BIT(http_ce_skip); /* pass the raw body data to the user, even when + content-encoded (chunked, compressed) */ + BIT(proxy_transfer_mode); /* set transfer mode (;type=) when doing + FTP via an HTTP proxy */ +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + BIT(socks5_gssapi_nec); /* Flag to support NEC SOCKS5 server */ +#endif + BIT(sasl_ir); /* Enable/disable SASL initial response */ + BIT(tcp_keepalive); /* use TCP keepalives */ + BIT(tcp_fastopen); /* use TCP Fast Open */ + BIT(ssl_enable_alpn);/* TLS ALPN extension? */ + BIT(path_as_is); /* allow dotdots? */ + BIT(pipewait); /* wait for multiplex status before starting a new + connection */ + BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers + from user callbacks */ + BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */ +#ifndef CURL_DISABLE_PROXY + BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1 + header */ +#endif +#ifdef USE_UNIX_SOCKETS + BIT(abstract_unix_socket); +#endif + BIT(disallow_username_in_url); /* disallow username in URL */ +#ifndef CURL_DISABLE_DOH + BIT(doh); /* DNS-over-HTTPS enabled */ + BIT(doh_verifypeer); /* DoH certificate peer verification */ + BIT(doh_verifyhost); /* DoH certificate hostname verification */ + BIT(doh_verifystatus); /* DoH certificate status verification */ +#endif + BIT(http09_allowed); /* allow HTTP/0.9 responses */ +#ifdef USE_WEBSOCKETS + BIT(ws_raw_mode); +#endif +#ifdef USE_ECH + int tls_ech; /* TLS ECH configuration */ +#endif +}; + +#ifndef CURL_DISABLE_MIME +#define IS_MIME_POST(a) ((a)->set.mimepost.kind != MIMEKIND_NONE) +#else +#define IS_MIME_POST(a) FALSE +#endif + +struct Names { + struct Curl_hash *hostcache; + enum { + HCACHE_NONE, /* not pointing to anything */ + HCACHE_MULTI, /* points to a shared one in the multi handle */ + HCACHE_SHARED /* points to a shared one in a shared object */ + } hostcachetype; +}; + +/* + * The 'connectdata' struct MUST have all the connection oriented stuff as we + * may have several simultaneous connections and connection structs in memory. + * + * The 'struct UserDefined' must only contain data that is set once to go for + * many (perhaps) independent connections. Values that are generated or + * calculated internally for the "session handle" must be defined within the + * 'struct UrlState' instead. + */ + +struct Curl_easy { + /* First a simple identifier to easier detect if a user mix up this easy + handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ + unsigned int magic; + /* once an easy handle is tied to a connection cache + a non-negative number to distinguish this transfer from + other using the same cache. For easier tracking + in log output. + This may wrap around after LONG_MAX to 0 again, so it + has no uniqueness guarantee for very large processings. */ + curl_off_t id; + + /* first, two fields for the linked list of these */ + struct Curl_easy *next; + struct Curl_easy *prev; + + struct connectdata *conn; + struct Curl_llist_element connect_queue; /* for the pending and msgsent + lists */ + struct Curl_llist_element conn_queue; /* list per connectdata */ + + CURLMstate mstate; /* the handle's state */ + CURLcode result; /* previous result */ + + struct Curl_message msg; /* A single posted message. */ + + /* Array with the plain socket numbers this handle takes care of, in no + particular order. Note that all sockets are added to the sockhash, where + the state etc are also kept. This array is mostly used to detect when a + socket is to be removed from the hash. See singlesocket(). */ + struct easy_pollset last_poll; + + struct Names dns; + struct Curl_multi *multi; /* if non-NULL, points to the multi handle + struct to which this "belongs" when used by + the multi interface */ + struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle + struct to which this "belongs" when used + by the easy interface */ + struct Curl_share *share; /* Share, handles global variable mutexing */ +#ifdef USE_LIBPSL + struct PslCache *psl; /* The associated PSL cache. */ +#endif + struct SingleRequest req; /* Request-specific data */ + struct UserDefined set; /* values set by the libcurl user */ +#ifndef CURL_DISABLE_COOKIES + struct CookieInfo *cookies; /* the cookies, read from files and servers. + NOTE that the 'cookie' field in the + UserDefined struct defines if the "engine" + is to be used or not. */ +#endif +#ifndef CURL_DISABLE_HSTS + struct hsts *hsts; +#endif +#ifndef CURL_DISABLE_ALTSVC + struct altsvcinfo *asi; /* the alt-svc cache */ +#endif + struct Progress progress; /* for all the progress meter data */ + struct UrlState state; /* struct for fields used for state info and + other dynamic purposes */ +#ifndef CURL_DISABLE_FTP + struct WildcardData *wildcard; /* wildcard download state info */ +#endif + struct PureInfo info; /* stats, reports and info data */ + struct curl_tlssessioninfo tsi; /* Information about the TLS session, only + valid after a client has asked for it */ +#ifdef USE_HYPER + struct hyptransfer hyp; +#endif +}; + +#define LIBCURL_NAME "libcurl" + +#endif /* HEADER_CURL_URLDATA_H */ diff --git a/third_party/curl/lib/vauth/cleartext.c b/third_party/curl/lib/vauth/cleartext.c new file mode 100644 index 0000000000..cf8108ac5b --- /dev/null +++ b/third_party/curl/lib/vauth/cleartext.c @@ -0,0 +1,137 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC4616 PLAIN authentication + * Draft LOGIN SASL Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_POP3) || \ + (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) + +#include +#include "urldata.h" + +#include "vauth/vauth.h" +#include "warnless.h" +#include "strtok.h" +#include "sendf.h" +#include "curl_printf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_auth_create_plain_message() + * + * This is used to generate an already encoded PLAIN message ready + * for sending to the recipient. + * + * Parameters: + * + * authzid [in] - The authorization identity. + * authcid [in] - The authentication identity. + * passwd [in] - The password. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_plain_message(const char *authzid, + const char *authcid, + const char *passwd, + struct bufref *out) +{ + char *plainauth; + size_t plainlen; + size_t zlen; + size_t clen; + size_t plen; + + zlen = (authzid == NULL ? 0 : strlen(authzid)); + clen = strlen(authcid); + plen = strlen(passwd); + + /* Compute binary message length. Check for overflows. */ + if((zlen > SIZE_T_MAX/4) || (clen > SIZE_T_MAX/4) || + (plen > (SIZE_T_MAX/2 - 2))) + return CURLE_OUT_OF_MEMORY; + plainlen = zlen + clen + plen + 2; + + plainauth = malloc(plainlen + 1); + if(!plainauth) + return CURLE_OUT_OF_MEMORY; + + /* Calculate the reply */ + if(zlen) + memcpy(plainauth, authzid, zlen); + plainauth[zlen] = '\0'; + memcpy(plainauth + zlen + 1, authcid, clen); + plainauth[zlen + clen + 1] = '\0'; + memcpy(plainauth + zlen + clen + 2, passwd, plen); + plainauth[plainlen] = '\0'; + Curl_bufref_set(out, plainauth, plainlen, curl_free); + return CURLE_OK; +} + +/* + * Curl_auth_create_login_message() + * + * This is used to generate an already encoded LOGIN message containing the + * username or password ready for sending to the recipient. + * + * Parameters: + * + * valuep [in] - The username or user's password. + * out [out] - The result storage. + * + * Returns void. + */ +void Curl_auth_create_login_message(const char *valuep, struct bufref *out) +{ + Curl_bufref_set(out, valuep, strlen(valuep), NULL); +} + +/* + * Curl_auth_create_external_message() + * + * This is used to generate an already encoded EXTERNAL message containing + * the username ready for sending to the recipient. + * + * Parameters: + * + * user [in] - The username. + * out [out] - The result storage. + * + * Returns void. + */ +void Curl_auth_create_external_message(const char *user, + struct bufref *out) +{ + /* This is the same formatting as the login message */ + Curl_auth_create_login_message(user, out); +} + +#endif /* if no users */ diff --git a/third_party/curl/lib/vauth/cram.c b/third_party/curl/lib/vauth/cram.c new file mode 100644 index 0000000000..f8bdd5458d --- /dev/null +++ b/third_party/curl/lib/vauth/cram.c @@ -0,0 +1,97 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC2195 CRAM-MD5 authentication + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_DIGEST_AUTH + +#include +#include "urldata.h" + +#include "vauth/vauth.h" +#include "curl_hmac.h" +#include "curl_md5.h" +#include "warnless.h" +#include "curl_printf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + + +/* + * Curl_auth_create_cram_md5_message() + * + * This is used to generate a CRAM-MD5 response message ready for sending to + * the recipient. + * + * Parameters: + * + * chlg [in] - The challenge. + * userp [in] - The username. + * passwdp [in] - The user's password. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, + const char *userp, + const char *passwdp, + struct bufref *out) +{ + struct HMAC_context *ctxt; + unsigned char digest[MD5_DIGEST_LEN]; + char *response; + + /* Compute the digest using the password as the key */ + ctxt = Curl_HMAC_init(Curl_HMAC_MD5, + (const unsigned char *) passwdp, + curlx_uztoui(strlen(passwdp))); + if(!ctxt) + return CURLE_OUT_OF_MEMORY; + + /* Update the digest with the given challenge */ + if(Curl_bufref_len(chlg)) + Curl_HMAC_update(ctxt, Curl_bufref_ptr(chlg), + curlx_uztoui(Curl_bufref_len(chlg))); + + /* Finalise the digest */ + Curl_HMAC_final(ctxt, digest); + + /* Generate the response */ + response = aprintf( + "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + userp, digest[0], digest[1], digest[2], digest[3], digest[4], + digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], + digest[11], digest[12], digest[13], digest[14], digest[15]); + if(!response) + return CURLE_OUT_OF_MEMORY; + + Curl_bufref_set(out, response, strlen(response), curl_free); + return CURLE_OK; +} + +#endif /* !CURL_DISABLE_DIGEST_AUTH */ diff --git a/third_party/curl/lib/vauth/digest.c b/third_party/curl/lib/vauth/digest.c new file mode 100644 index 0000000000..f00e6651f8 --- /dev/null +++ b/third_party/curl/lib/vauth/digest.c @@ -0,0 +1,1031 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC2831 DIGEST-MD5 authentication + * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_DIGEST_AUTH + +#include + +#include "vauth/vauth.h" +#include "vauth/digest.h" +#include "urldata.h" +#include "curl_base64.h" +#include "curl_hmac.h" +#include "curl_md5.h" +#include "curl_sha256.h" +#include "curl_sha512_256.h" +#include "vtls/vtls.h" +#include "warnless.h" +#include "strtok.h" +#include "strcase.h" +#include "curl_printf.h" +#include "rand.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +#define SESSION_ALGO 1 /* for algos with this bit set */ + +#define ALGO_MD5 0 +#define ALGO_MD5SESS (ALGO_MD5 | SESSION_ALGO) +#define ALGO_SHA256 2 +#define ALGO_SHA256SESS (ALGO_SHA256 | SESSION_ALGO) +#define ALGO_SHA512_256 4 +#define ALGO_SHA512_256SESS (ALGO_SHA512_256 | SESSION_ALGO) + +#if !defined(USE_WINDOWS_SSPI) +#define DIGEST_QOP_VALUE_AUTH (1 << 0) +#define DIGEST_QOP_VALUE_AUTH_INT (1 << 1) +#define DIGEST_QOP_VALUE_AUTH_CONF (1 << 2) + +#define DIGEST_QOP_VALUE_STRING_AUTH "auth" +#define DIGEST_QOP_VALUE_STRING_AUTH_INT "auth-int" +#define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf" +#endif + +bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, + const char **endptr) +{ + int c; + bool starts_with_quote = FALSE; + bool escape = FALSE; + + for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);) + *value++ = *str++; + *value = 0; + + if('=' != *str++) + /* eek, no match */ + return FALSE; + + if('\"' == *str) { + /* This starts with a quote so it must end with one as well! */ + str++; + starts_with_quote = TRUE; + } + + for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) { + if(!escape) { + switch(*str) { + case '\\': + if(starts_with_quote) { + /* the start of an escaped quote */ + escape = TRUE; + continue; + } + break; + + case ',': + if(!starts_with_quote) { + /* This signals the end of the content if we did not get a starting + quote and then we do "sloppy" parsing */ + c = 0; /* the end */ + continue; + } + break; + + case '\r': + case '\n': + /* end of string */ + if(starts_with_quote) + return FALSE; /* No closing quote */ + c = 0; + continue; + + case '\"': + if(starts_with_quote) { + /* end of string */ + c = 0; + continue; + } + else + return FALSE; + } + } + + escape = FALSE; + *content++ = *str; + } + if(escape) + return FALSE; /* No character after backslash */ + + *content = 0; + *endptr = str; + + return TRUE; +} + +#if !defined(USE_WINDOWS_SSPI) +/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string */ +static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ + unsigned char *dest) /* 33 bytes */ +{ + int i; + for(i = 0; i < 16; i++) + msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); +} + +/* Convert sha256 or SHA-512/256 chunk to RFC7616 -suitable ascii string */ +static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ + unsigned char *dest) /* 65 bytes */ +{ + int i; + for(i = 0; i < 32; i++) + msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); +} + +/* Perform quoted-string escaping as described in RFC2616 and its errata */ +static char *auth_digest_string_quoted(const char *source) +{ + char *dest; + const char *s = source; + size_t n = 1; /* null terminator */ + + /* Calculate size needed */ + while(*s) { + ++n; + if(*s == '"' || *s == '\\') { + ++n; + } + ++s; + } + + dest = malloc(n); + if(dest) { + char *d = dest; + s = source; + while(*s) { + if(*s == '"' || *s == '\\') { + *d++ = '\\'; + } + *d++ = *s++; + } + *d = '\0'; + } + + return dest; +} + +/* Retrieves the value for a corresponding key from the challenge string + * returns TRUE if the key could be found, FALSE if it does not exists + */ +static bool auth_digest_get_key_value(const char *chlg, + const char *key, + char *value, + size_t max_val_len, + char end_char) +{ + char *find_pos; + size_t i; + + find_pos = strstr(chlg, key); + if(!find_pos) + return FALSE; + + find_pos += strlen(key); + + for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i) + value[i] = *find_pos++; + value[i] = '\0'; + + return TRUE; +} + +static CURLcode auth_digest_get_qop_values(const char *options, int *value) +{ + char *tmp; + char *token; + char *tok_buf = NULL; + + /* Initialise the output */ + *value = 0; + + /* Tokenise the list of qop values. Use a temporary clone of the buffer since + strtok_r() ruins it. */ + tmp = strdup(options); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + token = strtok_r(tmp, ",", &tok_buf); + while(token) { + if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) + *value |= DIGEST_QOP_VALUE_AUTH; + else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) + *value |= DIGEST_QOP_VALUE_AUTH_INT; + else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF)) + *value |= DIGEST_QOP_VALUE_AUTH_CONF; + + token = strtok_r(NULL, ",", &tok_buf); + } + + free(tmp); + + return CURLE_OK; +} + +/* + * auth_decode_digest_md5_message() + * + * This is used internally to decode an already encoded DIGEST-MD5 challenge + * message into the separate attributes. + * + * Parameters: + * + * chlgref [in] - The challenge message. + * nonce [in/out] - The buffer where the nonce will be stored. + * nlen [in] - The length of the nonce buffer. + * realm [in/out] - The buffer where the realm will be stored. + * rlen [in] - The length of the realm buffer. + * alg [in/out] - The buffer where the algorithm will be stored. + * alen [in] - The length of the algorithm buffer. + * qop [in/out] - The buffer where the qop-options will be stored. + * qlen [in] - The length of the qop buffer. + * + * Returns CURLE_OK on success. + */ +static CURLcode auth_decode_digest_md5_message(const struct bufref *chlgref, + char *nonce, size_t nlen, + char *realm, size_t rlen, + char *alg, size_t alen, + char *qop, size_t qlen) +{ + const char *chlg = (const char *) Curl_bufref_ptr(chlgref); + + /* Ensure we have a valid challenge message */ + if(!Curl_bufref_len(chlgref)) + return CURLE_BAD_CONTENT_ENCODING; + + /* Retrieve nonce string from the challenge */ + if(!auth_digest_get_key_value(chlg, "nonce=\"", nonce, nlen, '\"')) + return CURLE_BAD_CONTENT_ENCODING; + + /* Retrieve realm string from the challenge */ + if(!auth_digest_get_key_value(chlg, "realm=\"", realm, rlen, '\"')) { + /* Challenge does not have a realm, set empty string [RFC2831] page 6 */ + *realm = '\0'; + } + + /* Retrieve algorithm string from the challenge */ + if(!auth_digest_get_key_value(chlg, "algorithm=", alg, alen, ',')) + return CURLE_BAD_CONTENT_ENCODING; + + /* Retrieve qop-options string from the challenge */ + if(!auth_digest_get_key_value(chlg, "qop=\"", qop, qlen, '\"')) + return CURLE_BAD_CONTENT_ENCODING; + + return CURLE_OK; +} + +/* + * Curl_auth_is_digest_supported() + * + * This is used to evaluate if DIGEST is supported. + * + * Parameters: None + * + * Returns TRUE as DIGEST as handled by libcurl. + */ +bool Curl_auth_is_digest_supported(void) +{ + return TRUE; +} + +/* + * Curl_auth_create_digest_md5_message() + * + * This is used to generate an already encoded DIGEST-MD5 response message + * ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * chlg [in] - The challenge message. + * userp [in] - The username. + * passwdp [in] - The user's password. + * service [in] - The service type such as http, smtp, pop or imap. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, + const struct bufref *chlg, + const char *userp, + const char *passwdp, + const char *service, + struct bufref *out) +{ + size_t i; + struct MD5_context *ctxt; + char *response = NULL; + unsigned char digest[MD5_DIGEST_LEN]; + char HA1_hex[2 * MD5_DIGEST_LEN + 1]; + char HA2_hex[2 * MD5_DIGEST_LEN + 1]; + char resp_hash_hex[2 * MD5_DIGEST_LEN + 1]; + char nonce[64]; + char realm[128]; + char algorithm[64]; + char qop_options[64]; + int qop_values; + char cnonce[33]; + char nonceCount[] = "00000001"; + char method[] = "AUTHENTICATE"; + char qop[] = DIGEST_QOP_VALUE_STRING_AUTH; + char *spn = NULL; + + /* Decode the challenge message */ + CURLcode result = auth_decode_digest_md5_message(chlg, + nonce, sizeof(nonce), + realm, sizeof(realm), + algorithm, + sizeof(algorithm), + qop_options, + sizeof(qop_options)); + if(result) + return result; + + /* We only support md5 sessions */ + if(strcmp(algorithm, "md5-sess") != 0) + return CURLE_BAD_CONTENT_ENCODING; + + /* Get the qop-values from the qop-options */ + result = auth_digest_get_qop_values(qop_options, &qop_values); + if(result) + return result; + + /* We only support auth quality-of-protection */ + if(!(qop_values & DIGEST_QOP_VALUE_AUTH)) + return CURLE_BAD_CONTENT_ENCODING; + + /* Generate 32 random hex chars, 32 bytes + 1 null-termination */ + result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce)); + if(result) + return result; + + /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */ + ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + if(!ctxt) + return CURLE_OUT_OF_MEMORY; + + Curl_MD5_update(ctxt, (const unsigned char *) userp, + curlx_uztoui(strlen(userp))); + Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + Curl_MD5_update(ctxt, (const unsigned char *) realm, + curlx_uztoui(strlen(realm))); + Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + Curl_MD5_update(ctxt, (const unsigned char *) passwdp, + curlx_uztoui(strlen(passwdp))); + Curl_MD5_final(ctxt, digest); + + ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + if(!ctxt) + return CURLE_OUT_OF_MEMORY; + + Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN); + Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + Curl_MD5_update(ctxt, (const unsigned char *) nonce, + curlx_uztoui(strlen(nonce))); + Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + Curl_MD5_update(ctxt, (const unsigned char *) cnonce, + curlx_uztoui(strlen(cnonce))); + Curl_MD5_final(ctxt, digest); + + /* Convert calculated 16 octet hex into 32 bytes string */ + for(i = 0; i < MD5_DIGEST_LEN; i++) + msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); + + /* Generate our SPN */ + spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); + if(!spn) + return CURLE_OUT_OF_MEMORY; + + /* Calculate H(A2) */ + ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + if(!ctxt) { + free(spn); + + return CURLE_OUT_OF_MEMORY; + } + + Curl_MD5_update(ctxt, (const unsigned char *) method, + curlx_uztoui(strlen(method))); + Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + Curl_MD5_update(ctxt, (const unsigned char *) spn, + curlx_uztoui(strlen(spn))); + Curl_MD5_final(ctxt, digest); + + for(i = 0; i < MD5_DIGEST_LEN; i++) + msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]); + + /* Now calculate the response hash */ + ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + if(!ctxt) { + free(spn); + + return CURLE_OUT_OF_MEMORY; + } + + Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN); + Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + Curl_MD5_update(ctxt, (const unsigned char *) nonce, + curlx_uztoui(strlen(nonce))); + Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + + Curl_MD5_update(ctxt, (const unsigned char *) nonceCount, + curlx_uztoui(strlen(nonceCount))); + Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + Curl_MD5_update(ctxt, (const unsigned char *) cnonce, + curlx_uztoui(strlen(cnonce))); + Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + Curl_MD5_update(ctxt, (const unsigned char *) qop, + curlx_uztoui(strlen(qop))); + Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + + Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN); + Curl_MD5_final(ctxt, digest); + + for(i = 0; i < MD5_DIGEST_LEN; i++) + msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]); + + /* Generate the response */ + response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\"," + "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s," + "qop=%s", + userp, realm, nonce, + cnonce, nonceCount, spn, resp_hash_hex, qop); + free(spn); + if(!response) + return CURLE_OUT_OF_MEMORY; + + /* Return the response. */ + Curl_bufref_set(out, response, strlen(response), curl_free); + return result; +} + +/* + * Curl_auth_decode_digest_http_message() + * + * This is used to decode an HTTP DIGEST challenge message into the separate + * attributes. + * + * Parameters: + * + * chlg [in] - The challenge message. + * digest [in/out] - The digest data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_decode_digest_http_message(const char *chlg, + struct digestdata *digest) +{ + bool before = FALSE; /* got a nonce before */ + bool foundAuth = FALSE; + bool foundAuthInt = FALSE; + char *token = NULL; + char *tmp = NULL; + + /* If we already have received a nonce, keep that in mind */ + if(digest->nonce) + before = TRUE; + + /* Clean up any former leftovers and initialise to defaults */ + Curl_auth_digest_cleanup(digest); + + for(;;) { + char value[DIGEST_MAX_VALUE_LENGTH]; + char content[DIGEST_MAX_CONTENT_LENGTH]; + + /* Pass all additional spaces here */ + while(*chlg && ISBLANK(*chlg)) + chlg++; + + /* Extract a value=content pair */ + if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) { + if(strcasecompare(value, "nonce")) { + free(digest->nonce); + digest->nonce = strdup(content); + if(!digest->nonce) + return CURLE_OUT_OF_MEMORY; + } + else if(strcasecompare(value, "stale")) { + if(strcasecompare(content, "true")) { + digest->stale = TRUE; + digest->nc = 1; /* we make a new nonce now */ + } + } + else if(strcasecompare(value, "realm")) { + free(digest->realm); + digest->realm = strdup(content); + if(!digest->realm) + return CURLE_OUT_OF_MEMORY; + } + else if(strcasecompare(value, "opaque")) { + free(digest->opaque); + digest->opaque = strdup(content); + if(!digest->opaque) + return CURLE_OUT_OF_MEMORY; + } + else if(strcasecompare(value, "qop")) { + char *tok_buf = NULL; + /* Tokenize the list and choose auth if possible, use a temporary + clone of the buffer since strtok_r() ruins it */ + tmp = strdup(content); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + token = strtok_r(tmp, ",", &tok_buf); + while(token) { + /* Pass additional spaces here */ + while(*token && ISBLANK(*token)) + token++; + if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) { + foundAuth = TRUE; + } + else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) { + foundAuthInt = TRUE; + } + token = strtok_r(NULL, ",", &tok_buf); + } + + free(tmp); + + /* Select only auth or auth-int. Otherwise, ignore */ + if(foundAuth) { + free(digest->qop); + digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH); + if(!digest->qop) + return CURLE_OUT_OF_MEMORY; + } + else if(foundAuthInt) { + free(digest->qop); + digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT); + if(!digest->qop) + return CURLE_OUT_OF_MEMORY; + } + } + else if(strcasecompare(value, "algorithm")) { + free(digest->algorithm); + digest->algorithm = strdup(content); + if(!digest->algorithm) + return CURLE_OUT_OF_MEMORY; + + if(strcasecompare(content, "MD5-sess")) + digest->algo = ALGO_MD5SESS; + else if(strcasecompare(content, "MD5")) + digest->algo = ALGO_MD5; + else if(strcasecompare(content, "SHA-256")) + digest->algo = ALGO_SHA256; + else if(strcasecompare(content, "SHA-256-SESS")) + digest->algo = ALGO_SHA256SESS; + else if(strcasecompare(content, "SHA-512-256")) { +#ifdef CURL_HAVE_SHA512_256 + digest->algo = ALGO_SHA512_256; +#else /* ! CURL_HAVE_SHA512_256 */ + return CURLE_NOT_BUILT_IN; +#endif /* ! CURL_HAVE_SHA512_256 */ + } + else if(strcasecompare(content, "SHA-512-256-SESS")) { +#ifdef CURL_HAVE_SHA512_256 + digest->algo = ALGO_SHA512_256SESS; +#else /* ! CURL_HAVE_SHA512_256 */ + return CURLE_NOT_BUILT_IN; +#endif /* ! CURL_HAVE_SHA512_256 */ + } + else + return CURLE_BAD_CONTENT_ENCODING; + } + else if(strcasecompare(value, "userhash")) { + if(strcasecompare(content, "true")) { + digest->userhash = TRUE; + } + } + else { + /* Unknown specifier, ignore it! */ + } + } + else + break; /* We are done here */ + + /* Pass all additional spaces here */ + while(*chlg && ISBLANK(*chlg)) + chlg++; + + /* Allow the list to be comma-separated */ + if(',' == *chlg) + chlg++; + } + + /* We had a nonce since before, and we got another one now without + 'stale=true'. This means we provided bad credentials in the previous + request */ + if(before && !digest->stale) + return CURLE_BAD_CONTENT_ENCODING; + + /* We got this header without a nonce, that is a bad Digest line! */ + if(!digest->nonce) + return CURLE_BAD_CONTENT_ENCODING; + + /* "-sess" protocol versions require "auth" or "auth-int" qop */ + if(!digest->qop && (digest->algo & SESSION_ALGO)) + return CURLE_BAD_CONTENT_ENCODING; + + return CURLE_OK; +} + +/* + * auth_create_digest_http_message() + * + * This is used to generate an HTTP DIGEST response message ready for sending + * to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The username. + * passwdp [in] - The user's password. + * request [in] - The HTTP request. + * uripath [in] - The path of the HTTP uri. + * digest [in/out] - The digest data struct being used and modified. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +static CURLcode auth_create_digest_http_message( + struct Curl_easy *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uripath, + struct digestdata *digest, + char **outptr, size_t *outlen, + void (*convert_to_ascii)(unsigned char *, unsigned char *), + CURLcode (*hash)(unsigned char *, const unsigned char *, + const size_t)) +{ + CURLcode result; + unsigned char hashbuf[32]; /* 32 bytes/256 bits */ + unsigned char request_digest[65]; + unsigned char ha1[65]; /* 64 digits and 1 zero byte */ + unsigned char ha2[65]; /* 64 digits and 1 zero byte */ + char userh[65]; + char *cnonce = NULL; + size_t cnonce_sz = 0; + char *userp_quoted; + char *realm_quoted; + char *nonce_quoted; + char *response = NULL; + char *hashthis = NULL; + char *tmp = NULL; + + memset(hashbuf, 0, sizeof(hashbuf)); + if(!digest->nc) + digest->nc = 1; + + if(!digest->cnonce) { + char cnoncebuf[33]; + result = Curl_rand_hex(data, (unsigned char *)cnoncebuf, + sizeof(cnoncebuf)); + if(result) + return result; + + result = Curl_base64_encode(cnoncebuf, strlen(cnoncebuf), + &cnonce, &cnonce_sz); + if(result) + return result; + + digest->cnonce = cnonce; + } + + if(digest->userhash) { + hashthis = aprintf("%s:%s", userp, digest->realm ? digest->realm : ""); + if(!hashthis) + return CURLE_OUT_OF_MEMORY; + + result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); + free(hashthis); + if(result) + return result; + convert_to_ascii(hashbuf, (unsigned char *)userh); + } + + /* + If the algorithm is "MD5" or unspecified (which then defaults to MD5): + + A1 = unq(username-value) ":" unq(realm-value) ":" passwd + + If the algorithm is "MD5-sess" then: + + A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":" + unq(nonce-value) ":" unq(cnonce-value) + */ + + hashthis = aprintf("%s:%s:%s", userp, digest->realm ? digest->realm : "", + passwdp); + if(!hashthis) + return CURLE_OUT_OF_MEMORY; + + result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); + free(hashthis); + if(result) + return result; + convert_to_ascii(hashbuf, ha1); + + if(digest->algo & SESSION_ALGO) { + /* nonce and cnonce are OUTSIDE the hash */ + tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + result = hash(hashbuf, (unsigned char *) tmp, strlen(tmp)); + free(tmp); + if(result) + return result; + convert_to_ascii(hashbuf, ha1); + } + + /* + If the "qop" directive's value is "auth" or is unspecified, then A2 is: + + A2 = Method ":" digest-uri-value + + If the "qop" value is "auth-int", then A2 is: + + A2 = Method ":" digest-uri-value ":" H(entity-body) + + (The "Method" value is the HTTP request method as specified in section + 5.1.1 of RFC 2616) + */ + + hashthis = aprintf("%s:%s", request, uripath); + if(!hashthis) + return CURLE_OUT_OF_MEMORY; + + if(digest->qop && strcasecompare(digest->qop, "auth-int")) { + /* We do not support auth-int for PUT or POST */ + char hashed[65]; + char *hashthis2; + + result = hash(hashbuf, (const unsigned char *)"", 0); + if(result) { + free(hashthis); + return result; + } + convert_to_ascii(hashbuf, (unsigned char *)hashed); + + hashthis2 = aprintf("%s:%s", hashthis, hashed); + free(hashthis); + hashthis = hashthis2; + } + + if(!hashthis) + return CURLE_OUT_OF_MEMORY; + + result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); + free(hashthis); + if(result) + return result; + convert_to_ascii(hashbuf, ha2); + + if(digest->qop) { + hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc, + digest->cnonce, digest->qop, ha2); + } + else { + hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2); + } + + if(!hashthis) + return CURLE_OUT_OF_MEMORY; + + result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); + free(hashthis); + if(result) + return result; + convert_to_ascii(hashbuf, request_digest); + + /* For test case 64 (snooped from a Mozilla 1.3a request) + + Authorization: Digest username="testuser", realm="testrealm", \ + nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" + + Digest parameters are all quoted strings. Username which is provided by + the user will need double quotes and backslashes within it escaped. + realm, nonce, and opaque will need backslashes as well as they were + de-escaped when copied from request header. cnonce is generated with + web-safe characters. uri is already percent encoded. nc is 8 hex + characters. algorithm and qop with standard values only contain web-safe + characters. + */ + userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp); + if(!userp_quoted) + return CURLE_OUT_OF_MEMORY; + if(digest->realm) + realm_quoted = auth_digest_string_quoted(digest->realm); + else { + realm_quoted = malloc(1); + if(realm_quoted) + realm_quoted[0] = 0; + } + if(!realm_quoted) { + free(userp_quoted); + return CURLE_OUT_OF_MEMORY; + } + nonce_quoted = auth_digest_string_quoted(digest->nonce); + if(!nonce_quoted) { + free(realm_quoted); + free(userp_quoted); + return CURLE_OUT_OF_MEMORY; + } + + if(digest->qop) { + response = aprintf("username=\"%s\", " + "realm=\"%s\", " + "nonce=\"%s\", " + "uri=\"%s\", " + "cnonce=\"%s\", " + "nc=%08x, " + "qop=%s, " + "response=\"%s\"", + userp_quoted, + realm_quoted, + nonce_quoted, + uripath, + digest->cnonce, + digest->nc, + digest->qop, + request_digest); + + /* Increment nonce-count to use another nc value for the next request */ + digest->nc++; + } + else { + response = aprintf("username=\"%s\", " + "realm=\"%s\", " + "nonce=\"%s\", " + "uri=\"%s\", " + "response=\"%s\"", + userp_quoted, + realm_quoted, + nonce_quoted, + uripath, + request_digest); + } + free(nonce_quoted); + free(realm_quoted); + free(userp_quoted); + if(!response) + return CURLE_OUT_OF_MEMORY; + + /* Add the optional fields */ + if(digest->opaque) { + char *opaque_quoted; + /* Append the opaque */ + opaque_quoted = auth_digest_string_quoted(digest->opaque); + if(!opaque_quoted) { + free(response); + return CURLE_OUT_OF_MEMORY; + } + tmp = aprintf("%s, opaque=\"%s\"", response, opaque_quoted); + free(response); + free(opaque_quoted); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + response = tmp; + } + + if(digest->algorithm) { + /* Append the algorithm */ + tmp = aprintf("%s, algorithm=%s", response, digest->algorithm); + free(response); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + response = tmp; + } + + if(digest->userhash) { + /* Append the userhash */ + tmp = aprintf("%s, userhash=true", response); + free(response); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + response = tmp; + } + + /* Return the output */ + *outptr = response; + *outlen = strlen(response); + + return CURLE_OK; +} + +/* + * Curl_auth_create_digest_http_message() + * + * This is used to generate an HTTP DIGEST response message ready for sending + * to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The username. + * passwdp [in] - The user's password. + * request [in] - The HTTP request. + * uripath [in] - The path of the HTTP uri. + * digest [in/out] - The digest data struct being used and modified. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uripath, + struct digestdata *digest, + char **outptr, size_t *outlen) +{ + if(digest->algo <= ALGO_MD5SESS) + return auth_create_digest_http_message(data, userp, passwdp, + request, uripath, digest, + outptr, outlen, + auth_digest_md5_to_ascii, + Curl_md5it); + + if(digest->algo <= ALGO_SHA256SESS) + return auth_create_digest_http_message(data, userp, passwdp, + request, uripath, digest, + outptr, outlen, + auth_digest_sha256_to_ascii, + Curl_sha256it); +#ifdef CURL_HAVE_SHA512_256 + if(digest->algo <= ALGO_SHA512_256SESS) + return auth_create_digest_http_message(data, userp, passwdp, + request, uripath, digest, + outptr, outlen, + auth_digest_sha256_to_ascii, + Curl_sha512_256it); +#endif /* CURL_HAVE_SHA512_256 */ + + /* Should be unreachable */ + return CURLE_BAD_CONTENT_ENCODING; +} + +/* + * Curl_auth_digest_cleanup() + * + * This is used to clean up the digest specific data. + * + * Parameters: + * + * digest [in/out] - The digest data struct being cleaned up. + * + */ +void Curl_auth_digest_cleanup(struct digestdata *digest) +{ + Curl_safefree(digest->nonce); + Curl_safefree(digest->cnonce); + Curl_safefree(digest->realm); + Curl_safefree(digest->opaque); + Curl_safefree(digest->qop); + Curl_safefree(digest->algorithm); + + digest->nc = 0; + digest->algo = ALGO_MD5; /* default algorithm */ + digest->stale = FALSE; /* default means normal, not stale */ + digest->userhash = FALSE; +} +#endif /* !USE_WINDOWS_SSPI */ + +#endif /* !CURL_DISABLE_DIGEST_AUTH */ diff --git a/third_party/curl/lib/vauth/digest.h b/third_party/curl/lib/vauth/digest.h new file mode 100644 index 0000000000..99ce1f9138 --- /dev/null +++ b/third_party/curl/lib/vauth/digest.h @@ -0,0 +1,40 @@ +#ifndef HEADER_CURL_DIGEST_H +#define HEADER_CURL_DIGEST_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include + +#ifndef CURL_DISABLE_DIGEST_AUTH + +#define DIGEST_MAX_VALUE_LENGTH 256 +#define DIGEST_MAX_CONTENT_LENGTH 1024 + +/* This is used to extract the realm from a challenge message */ +bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, + const char **endptr); + +#endif + +#endif /* HEADER_CURL_DIGEST_H */ diff --git a/third_party/curl/lib/vauth/digest_sspi.c b/third_party/curl/lib/vauth/digest_sspi.c new file mode 100644 index 0000000000..1813566714 --- /dev/null +++ b/third_party/curl/lib/vauth/digest_sspi.c @@ -0,0 +1,672 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC2831 DIGEST-MD5 authentication + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH) + +#include + +#include "vauth/vauth.h" +#include "vauth/digest.h" +#include "urldata.h" +#include "warnless.h" +#include "curl_multibyte.h" +#include "sendf.h" +#include "strdup.h" +#include "strcase.h" +#include "strerror.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* +* Curl_auth_is_digest_supported() +* +* This is used to evaluate if DIGEST is supported. +* +* Parameters: None +* +* Returns TRUE if DIGEST is supported by Windows SSPI. +*/ +bool Curl_auth_is_digest_supported(void) +{ + PSecPkgInfo SecurityPackage; + SECURITY_STATUS status; + + /* Query the security package for Digest */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), + &SecurityPackage); + + /* Release the package buffer as it is not required anymore */ + if(status == SEC_E_OK) { + s_pSecFn->FreeContextBuffer(SecurityPackage); + } + + return (status == SEC_E_OK ? TRUE : FALSE); +} + +/* + * Curl_auth_create_digest_md5_message() + * + * This is used to generate an already encoded DIGEST-MD5 response message + * ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * chlg [in] - The challenge message. + * userp [in] - The username in the format User or Domain\User. + * passwdp [in] - The user's password. + * service [in] - The service type such as http, smtp, pop or imap. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, + const struct bufref *chlg, + const char *userp, + const char *passwdp, + const char *service, + struct bufref *out) +{ + CURLcode result = CURLE_OK; + TCHAR *spn = NULL; + size_t token_max = 0; + unsigned char *output_token = NULL; + CredHandle credentials; + CtxtHandle context; + PSecPkgInfo SecurityPackage; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; + SecBuffer chlg_buf; + SecBuffer resp_buf; + SecBufferDesc chlg_desc; + SecBufferDesc resp_desc; + SECURITY_STATUS status; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ + + /* Ensure we have a valid challenge message */ + if(!Curl_bufref_len(chlg)) { + infof(data, "DIGEST-MD5 handshake failure (empty challenge message)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Query the security package for DigestSSP */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), + &SecurityPackage); + if(status != SEC_E_OK) { + failf(data, "SSPI: could not get auth info"); + return CURLE_AUTH_ERROR; + } + + token_max = SecurityPackage->cbMaxToken; + + /* Release the package buffer as it is not required anymore */ + s_pSecFn->FreeContextBuffer(SecurityPackage); + + /* Allocate our response buffer */ + output_token = malloc(token_max); + if(!output_token) + return CURLE_OUT_OF_MEMORY; + + /* Generate our SPN */ + spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); + if(!spn) { + free(output_token); + return CURLE_OUT_OF_MEMORY; + } + + if(userp && *userp) { + /* Populate our identity structure */ + result = Curl_create_sspi_identity(userp, passwdp, &identity); + if(result) { + free(spn); + free(output_token); + return result; + } + + /* Allow proper cleanup of the identity structure */ + p_identity = &identity; + } + else + /* Use the current Windows user */ + p_identity = NULL; + + /* Acquire our credentials handle */ + status = s_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *) TEXT(SP_NAME_DIGEST), + SECPKG_CRED_OUTBOUND, NULL, + p_identity, NULL, NULL, + &credentials, &expiry); + + if(status != SEC_E_OK) { + Curl_sspi_free_identity(p_identity); + free(spn); + free(output_token); + return CURLE_LOGIN_DENIED; + } + + /* Setup the challenge "input" security buffer */ + chlg_desc.ulVersion = SECBUFFER_VERSION; + chlg_desc.cBuffers = 1; + chlg_desc.pBuffers = &chlg_buf; + chlg_buf.BufferType = SECBUFFER_TOKEN; + chlg_buf.pvBuffer = (void *) Curl_bufref_ptr(chlg); + chlg_buf.cbBuffer = curlx_uztoul(Curl_bufref_len(chlg)); + + /* Setup the response "output" security buffer */ + resp_desc.ulVersion = SECBUFFER_VERSION; + resp_desc.cBuffers = 1; + resp_desc.pBuffers = &resp_buf; + resp_buf.BufferType = SECBUFFER_TOKEN; + resp_buf.pvBuffer = output_token; + resp_buf.cbBuffer = curlx_uztoul(token_max); + + /* Generate our response message */ + status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn, + 0, 0, 0, &chlg_desc, 0, + &context, &resp_desc, &attrs, + &expiry); + + if(status == SEC_I_COMPLETE_NEEDED || + status == SEC_I_COMPLETE_AND_CONTINUE) + s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); + else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + char buffer[STRERROR_LEN]; +#endif + + s_pSecFn->FreeCredentialsHandle(&credentials); + Curl_sspi_free_identity(p_identity); + free(spn); + free(output_token); + + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + infof(data, "schannel: InitializeSecurityContext failed: %s", + Curl_sspi_strerror(status, buffer, sizeof(buffer))); +#endif + + return CURLE_AUTH_ERROR; + } + + /* Return the response. */ + Curl_bufref_set(out, output_token, resp_buf.cbBuffer, curl_free); + + /* Free our handles */ + s_pSecFn->DeleteSecurityContext(&context); + s_pSecFn->FreeCredentialsHandle(&credentials); + + /* Free the identity structure */ + Curl_sspi_free_identity(p_identity); + + /* Free the SPN */ + free(spn); + + return result; +} + +/* + * Curl_override_sspi_http_realm() + * + * This is used to populate the domain in a SSPI identity structure + * The realm is extracted from the challenge message and used as the + * domain if it is not already explicitly set. + * + * Parameters: + * + * chlg [in] - The challenge message. + * identity [in/out] - The identity structure. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_override_sspi_http_realm(const char *chlg, + SEC_WINNT_AUTH_IDENTITY *identity) +{ + xcharp_u domain, dup_domain; + + /* If domain is blank or unset, check challenge message for realm */ + if(!identity->Domain || !identity->DomainLength) { + for(;;) { + char value[DIGEST_MAX_VALUE_LENGTH]; + char content[DIGEST_MAX_CONTENT_LENGTH]; + + /* Pass all additional spaces here */ + while(*chlg && ISBLANK(*chlg)) + chlg++; + + /* Extract a value=content pair */ + if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) { + if(strcasecompare(value, "realm")) { + + /* Setup identity's domain and length */ + domain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *) content); + if(!domain.tchar_ptr) + return CURLE_OUT_OF_MEMORY; + + dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr); + if(!dup_domain.tchar_ptr) { + curlx_unicodefree(domain.tchar_ptr); + return CURLE_OUT_OF_MEMORY; + } + + free(identity->Domain); + identity->Domain = dup_domain.tbyte_ptr; + identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr)); + dup_domain.tchar_ptr = NULL; + + curlx_unicodefree(domain.tchar_ptr); + } + else { + /* Unknown specifier, ignore it! */ + } + } + else + break; /* We are done here */ + + /* Pass all additional spaces here */ + while(*chlg && ISBLANK(*chlg)) + chlg++; + + /* Allow the list to be comma-separated */ + if(',' == *chlg) + chlg++; + } + } + + return CURLE_OK; +} + +/* + * Curl_auth_decode_digest_http_message() + * + * This is used to decode an HTTP DIGEST challenge message into the separate + * attributes. + * + * Parameters: + * + * chlg [in] - The challenge message. + * digest [in/out] - The digest data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_decode_digest_http_message(const char *chlg, + struct digestdata *digest) +{ + size_t chlglen = strlen(chlg); + + /* We had an input token before so if there is another one now that means we + provided bad credentials in the previous request or it is stale. */ + if(digest->input_token) { + bool stale = false; + const char *p = chlg; + + /* Check for the 'stale' directive */ + for(;;) { + char value[DIGEST_MAX_VALUE_LENGTH]; + char content[DIGEST_MAX_CONTENT_LENGTH]; + + while(*p && ISBLANK(*p)) + p++; + + if(!Curl_auth_digest_get_pair(p, value, content, &p)) + break; + + if(strcasecompare(value, "stale") && + strcasecompare(content, "true")) { + stale = true; + break; + } + + while(*p && ISBLANK(*p)) + p++; + + if(',' == *p) + p++; + } + + if(stale) + Curl_auth_digest_cleanup(digest); + else + return CURLE_LOGIN_DENIED; + } + + /* Store the challenge for use later */ + digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen + 1); + if(!digest->input_token) + return CURLE_OUT_OF_MEMORY; + + digest->input_token_len = chlglen; + + return CURLE_OK; +} + +/* + * Curl_auth_create_digest_http_message() + * + * This is used to generate an HTTP DIGEST response message ready for sending + * to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The username in the format User or Domain\User. + * passwdp [in] - The user's password. + * request [in] - The HTTP request. + * uripath [in] - The path of the HTTP uri. + * digest [in/out] - The digest data struct being used and modified. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uripath, + struct digestdata *digest, + char **outptr, size_t *outlen) +{ + size_t token_max; + char *resp; + BYTE *output_token; + size_t output_token_len = 0; + PSecPkgInfo SecurityPackage; + SecBuffer chlg_buf[5]; + SecBufferDesc chlg_desc; + SECURITY_STATUS status; + + (void) data; + + /* Query the security package for DigestSSP */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), + &SecurityPackage); + if(status != SEC_E_OK) { + failf(data, "SSPI: could not get auth info"); + return CURLE_AUTH_ERROR; + } + + token_max = SecurityPackage->cbMaxToken; + + /* Release the package buffer as it is not required anymore */ + s_pSecFn->FreeContextBuffer(SecurityPackage); + + /* Allocate the output buffer according to the max token size as indicated + by the security package */ + output_token = malloc(token_max); + if(!output_token) { + return CURLE_OUT_OF_MEMORY; + } + + /* If the user/passwd that was used to make the identity for http_context + has changed then delete that context. */ + if((userp && !digest->user) || (!userp && digest->user) || + (passwdp && !digest->passwd) || (!passwdp && digest->passwd) || + (userp && digest->user && Curl_timestrcmp(userp, digest->user)) || + (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) { + if(digest->http_context) { + s_pSecFn->DeleteSecurityContext(digest->http_context); + Curl_safefree(digest->http_context); + } + Curl_safefree(digest->user); + Curl_safefree(digest->passwd); + } + + if(digest->http_context) { + chlg_desc.ulVersion = SECBUFFER_VERSION; + chlg_desc.cBuffers = 5; + chlg_desc.pBuffers = chlg_buf; + chlg_buf[0].BufferType = SECBUFFER_TOKEN; + chlg_buf[0].pvBuffer = NULL; + chlg_buf[0].cbBuffer = 0; + chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS; + chlg_buf[1].pvBuffer = (void *) request; + chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request)); + chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS; + chlg_buf[2].pvBuffer = (void *) uripath; + chlg_buf[2].cbBuffer = curlx_uztoul(strlen((const char *) uripath)); + chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS; + chlg_buf[3].pvBuffer = NULL; + chlg_buf[3].cbBuffer = 0; + chlg_buf[4].BufferType = SECBUFFER_PADDING; + chlg_buf[4].pvBuffer = output_token; + chlg_buf[4].cbBuffer = curlx_uztoul(token_max); + + status = s_pSecFn->MakeSignature(digest->http_context, 0, &chlg_desc, 0); + if(status == SEC_E_OK) + output_token_len = chlg_buf[4].cbBuffer; + else { /* delete the context so a new one can be made */ + infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", + (long)status); + s_pSecFn->DeleteSecurityContext(digest->http_context); + Curl_safefree(digest->http_context); + } + } + + if(!digest->http_context) { + CredHandle credentials; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; + SecBuffer resp_buf; + SecBufferDesc resp_desc; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ + TCHAR *spn; + + /* free the copy of user/passwd used to make the previous identity */ + Curl_safefree(digest->user); + Curl_safefree(digest->passwd); + + if(userp && *userp) { + /* Populate our identity structure */ + if(Curl_create_sspi_identity(userp, passwdp, &identity)) { + free(output_token); + return CURLE_OUT_OF_MEMORY; + } + + /* Populate our identity domain */ + if(Curl_override_sspi_http_realm((const char *) digest->input_token, + &identity)) { + free(output_token); + return CURLE_OUT_OF_MEMORY; + } + + /* Allow proper cleanup of the identity structure */ + p_identity = &identity; + } + else + /* Use the current Windows user */ + p_identity = NULL; + + if(userp) { + digest->user = strdup(userp); + + if(!digest->user) { + free(output_token); + return CURLE_OUT_OF_MEMORY; + } + } + + if(passwdp) { + digest->passwd = strdup(passwdp); + + if(!digest->passwd) { + free(output_token); + Curl_safefree(digest->user); + return CURLE_OUT_OF_MEMORY; + } + } + + /* Acquire our credentials handle */ + status = s_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *) TEXT(SP_NAME_DIGEST), + SECPKG_CRED_OUTBOUND, NULL, + p_identity, NULL, NULL, + &credentials, &expiry); + if(status != SEC_E_OK) { + Curl_sspi_free_identity(p_identity); + free(output_token); + + return CURLE_LOGIN_DENIED; + } + + /* Setup the challenge "input" security buffer if present */ + chlg_desc.ulVersion = SECBUFFER_VERSION; + chlg_desc.cBuffers = 3; + chlg_desc.pBuffers = chlg_buf; + chlg_buf[0].BufferType = SECBUFFER_TOKEN; + chlg_buf[0].pvBuffer = digest->input_token; + chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len); + chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS; + chlg_buf[1].pvBuffer = (void *) request; + chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request)); + chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS; + chlg_buf[2].pvBuffer = NULL; + chlg_buf[2].cbBuffer = 0; + + /* Setup the response "output" security buffer */ + resp_desc.ulVersion = SECBUFFER_VERSION; + resp_desc.cBuffers = 1; + resp_desc.pBuffers = &resp_buf; + resp_buf.BufferType = SECBUFFER_TOKEN; + resp_buf.pvBuffer = output_token; + resp_buf.cbBuffer = curlx_uztoul(token_max); + + spn = curlx_convert_UTF8_to_tchar((char *) uripath); + if(!spn) { + s_pSecFn->FreeCredentialsHandle(&credentials); + + Curl_sspi_free_identity(p_identity); + free(output_token); + + return CURLE_OUT_OF_MEMORY; + } + + /* Allocate our new context handle */ + digest->http_context = calloc(1, sizeof(CtxtHandle)); + if(!digest->http_context) + return CURLE_OUT_OF_MEMORY; + + /* Generate our response message */ + status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, + spn, + ISC_REQ_USE_HTTP_STYLE, 0, 0, + &chlg_desc, 0, + digest->http_context, + &resp_desc, &attrs, &expiry); + curlx_unicodefree(spn); + + if(status == SEC_I_COMPLETE_NEEDED || + status == SEC_I_COMPLETE_AND_CONTINUE) + s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); + else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + char buffer[STRERROR_LEN]; +#endif + + s_pSecFn->FreeCredentialsHandle(&credentials); + + Curl_sspi_free_identity(p_identity); + free(output_token); + + Curl_safefree(digest->http_context); + + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + infof(data, "schannel: InitializeSecurityContext failed: %s", + Curl_sspi_strerror(status, buffer, sizeof(buffer))); +#endif + + return CURLE_AUTH_ERROR; + } + + output_token_len = resp_buf.cbBuffer; + + s_pSecFn->FreeCredentialsHandle(&credentials); + Curl_sspi_free_identity(p_identity); + } + + resp = malloc(output_token_len + 1); + if(!resp) { + free(output_token); + + return CURLE_OUT_OF_MEMORY; + } + + /* Copy the generated response */ + memcpy(resp, output_token, output_token_len); + resp[output_token_len] = 0; + + /* Return the response */ + *outptr = resp; + *outlen = output_token_len; + + /* Free the response buffer */ + free(output_token); + + return CURLE_OK; +} + +/* + * Curl_auth_digest_cleanup() + * + * This is used to clean up the digest specific data. + * + * Parameters: + * + * digest [in/out] - The digest data struct being cleaned up. + * + */ +void Curl_auth_digest_cleanup(struct digestdata *digest) +{ + /* Free the input token */ + Curl_safefree(digest->input_token); + + /* Reset any variables */ + digest->input_token_len = 0; + + /* Delete security context */ + if(digest->http_context) { + s_pSecFn->DeleteSecurityContext(digest->http_context); + Curl_safefree(digest->http_context); + } + + /* Free the copy of user/passwd used to make the identity for http_context */ + Curl_safefree(digest->user); + Curl_safefree(digest->passwd); +} + +#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */ diff --git a/third_party/curl/lib/vauth/gsasl.c b/third_party/curl/lib/vauth/gsasl.c new file mode 100644 index 0000000000..c7d0a8d3b2 --- /dev/null +++ b/third_party/curl/lib/vauth/gsasl.c @@ -0,0 +1,127 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Simon Josefsson, , 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 + * + * RFC5802 SCRAM-SHA-1 authentication + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_GSASL + +#include + +#include "vauth/vauth.h" +#include "urldata.h" +#include "sendf.h" + +#include + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, + const char *mech, + struct gsasldata *gsasl) +{ + int res; + + res = gsasl_init(&gsasl->ctx); + if(res != GSASL_OK) { + failf(data, "gsasl init: %s\n", gsasl_strerror(res)); + return FALSE; + } + + res = gsasl_client_start(gsasl->ctx, mech, &gsasl->client); + if(res != GSASL_OK) { + gsasl_done(gsasl->ctx); + return FALSE; + } + + return true; +} + +CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, + const char *userp, + const char *passwdp, + struct gsasldata *gsasl) +{ +#if GSASL_VERSION_NUMBER >= 0x010b00 + int res; + res = +#endif + gsasl_property_set(gsasl->client, GSASL_AUTHID, userp); +#if GSASL_VERSION_NUMBER >= 0x010b00 + if(res != GSASL_OK) { + failf(data, "setting AUTHID failed: %s\n", gsasl_strerror(res)); + return CURLE_OUT_OF_MEMORY; + } +#endif + +#if GSASL_VERSION_NUMBER >= 0x010b00 + res = +#endif + gsasl_property_set(gsasl->client, GSASL_PASSWORD, passwdp); +#if GSASL_VERSION_NUMBER >= 0x010b00 + if(res != GSASL_OK) { + failf(data, "setting PASSWORD failed: %s\n", gsasl_strerror(res)); + return CURLE_OUT_OF_MEMORY; + } +#endif + + (void)data; + + return CURLE_OK; +} + +CURLcode Curl_auth_gsasl_token(struct Curl_easy *data, + const struct bufref *chlg, + struct gsasldata *gsasl, + struct bufref *out) +{ + int res; + char *response; + size_t outlen; + + res = gsasl_step(gsasl->client, + (const char *) Curl_bufref_ptr(chlg), Curl_bufref_len(chlg), + &response, &outlen); + if(res != GSASL_OK && res != GSASL_NEEDS_MORE) { + failf(data, "GSASL step: %s\n", gsasl_strerror(res)); + return CURLE_BAD_CONTENT_ENCODING; + } + + Curl_bufref_set(out, response, outlen, gsasl_free); + return CURLE_OK; +} + +void Curl_auth_gsasl_cleanup(struct gsasldata *gsasl) +{ + gsasl_finish(gsasl->client); + gsasl->client = NULL; + + gsasl_done(gsasl->ctx); + gsasl->ctx = NULL; +} +#endif diff --git a/third_party/curl/lib/vauth/krb5_gssapi.c b/third_party/curl/lib/vauth/krb5_gssapi.c new file mode 100644 index 0000000000..748cdf93ad --- /dev/null +++ b/third_party/curl/lib/vauth/krb5_gssapi.c @@ -0,0 +1,324 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5) + +#include + +#include "vauth/vauth.h" +#include "curl_sasl.h" +#include "urldata.h" +#include "curl_gssapi.h" +#include "sendf.h" +#include "curl_printf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_auth_is_gssapi_supported() + * + * This is used to evaluate if GSSAPI (Kerberos V5) is supported. + * + * Parameters: None + * + * Returns TRUE if Kerberos V5 is supported by the GSS-API library. + */ +bool Curl_auth_is_gssapi_supported(void) +{ + return TRUE; +} + +/* + * Curl_auth_create_gssapi_user_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) user token + * message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The username. + * passwdp [in] - The user's password. + * service [in] - The service type such as http, smtp, pop or imap. + * host [in[ - The hostname. + * mutual_auth [in] - Flag specifying whether or not mutual authentication + * is enabled. + * chlg [in] - Optional challenge message. + * krb5 [in/out] - The Kerberos 5 data struct being used and modified. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + const char *service, + const char *host, + const bool mutual_auth, + const struct bufref *chlg, + struct kerberos5data *krb5, + struct bufref *out) +{ + CURLcode result = CURLE_OK; + OM_uint32 major_status; + OM_uint32 minor_status; + OM_uint32 unused_status; + gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + + (void) userp; + (void) passwdp; + + if(!krb5->spn) { + /* Generate our SPN */ + char *spn = Curl_auth_build_spn(service, NULL, host); + if(!spn) + return CURLE_OUT_OF_MEMORY; + + /* Populate the SPN structure */ + spn_token.value = spn; + spn_token.length = strlen(spn); + + /* Import the SPN */ + major_status = gss_import_name(&minor_status, &spn_token, + GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn); + if(GSS_ERROR(major_status)) { + Curl_gss_log_error(data, "gss_import_name() failed: ", + major_status, minor_status); + + free(spn); + + return CURLE_AUTH_ERROR; + } + + free(spn); + } + + if(chlg) { + if(!Curl_bufref_len(chlg)) { + infof(data, "GSSAPI handshake failure (empty challenge message)"); + return CURLE_BAD_CONTENT_ENCODING; + } + input_token.value = (void *) Curl_bufref_ptr(chlg); + input_token.length = Curl_bufref_len(chlg); + } + + major_status = Curl_gss_init_sec_context(data, + &minor_status, + &krb5->context, + krb5->spn, + &Curl_krb5_mech_oid, + GSS_C_NO_CHANNEL_BINDINGS, + &input_token, + &output_token, + mutual_auth, + NULL); + + if(GSS_ERROR(major_status)) { + if(output_token.value) + gss_release_buffer(&unused_status, &output_token); + + Curl_gss_log_error(data, "gss_init_sec_context() failed: ", + major_status, minor_status); + + return CURLE_AUTH_ERROR; + } + + if(output_token.value && output_token.length) { + result = Curl_bufref_memdup(out, output_token.value, output_token.length); + gss_release_buffer(&unused_status, &output_token); + } + else + Curl_bufref_set(out, mutual_auth? "": NULL, 0, NULL); + + return result; +} + +/* + * Curl_auth_create_gssapi_security_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) security + * token message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * authzid [in] - The authorization identity if some. + * chlg [in] - Optional challenge message. + * krb5 [in/out] - The Kerberos 5 data struct being used and modified. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, + const char *authzid, + const struct bufref *chlg, + struct kerberos5data *krb5, + struct bufref *out) +{ + CURLcode result = CURLE_OK; + size_t messagelen = 0; + unsigned char *message = NULL; + OM_uint32 major_status; + OM_uint32 minor_status; + OM_uint32 unused_status; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + unsigned char *indata; + gss_qop_t qop = GSS_C_QOP_DEFAULT; + unsigned int sec_layer = 0; + unsigned int max_size = 0; + + /* Ensure we have a valid challenge message */ + if(!Curl_bufref_len(chlg)) { + infof(data, "GSSAPI handshake failure (empty security message)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Setup the challenge "input" security buffer */ + input_token.value = (void *) Curl_bufref_ptr(chlg); + input_token.length = Curl_bufref_len(chlg); + + /* Decrypt the inbound challenge and obtain the qop */ + major_status = gss_unwrap(&minor_status, krb5->context, &input_token, + &output_token, NULL, &qop); + if(GSS_ERROR(major_status)) { + Curl_gss_log_error(data, "gss_unwrap() failed: ", + major_status, minor_status); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ + if(output_token.length != 4) { + infof(data, "GSSAPI handshake failure (invalid security data)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Extract the security layer and the maximum message size */ + indata = output_token.value; + sec_layer = indata[0]; + max_size = ((unsigned int)indata[1] << 16) | + ((unsigned int)indata[2] << 8) | indata[3]; + + /* Free the challenge as it is not required anymore */ + gss_release_buffer(&unused_status, &output_token); + + /* Process the security layer */ + if(!(sec_layer & GSSAUTH_P_NONE)) { + infof(data, "GSSAPI handshake failure (invalid security layer)"); + + return CURLE_BAD_CONTENT_ENCODING; + } + sec_layer &= GSSAUTH_P_NONE; /* We do not support a security layer */ + + /* Process the maximum message size the server can receive */ + if(max_size > 0) { + /* The server has told us it supports a maximum receive buffer, however, as + we do not require one unless we are encrypting data, we tell the server + our receive buffer is zero. */ + max_size = 0; + } + + /* Allocate our message */ + messagelen = 4; + if(authzid) + messagelen += strlen(authzid); + message = malloc(messagelen); + if(!message) + return CURLE_OUT_OF_MEMORY; + + /* Populate the message with the security layer and client supported receive + message size. */ + message[0] = sec_layer & 0xFF; + message[1] = (max_size >> 16) & 0xFF; + message[2] = (max_size >> 8) & 0xFF; + message[3] = max_size & 0xFF; + + /* If given, append the authorization identity. */ + + if(authzid && *authzid) + memcpy(message + 4, authzid, messagelen - 4); + + /* Setup the "authentication data" security buffer */ + input_token.value = message; + input_token.length = messagelen; + + /* Encrypt the data */ + major_status = gss_wrap(&minor_status, krb5->context, 0, + GSS_C_QOP_DEFAULT, &input_token, NULL, + &output_token); + if(GSS_ERROR(major_status)) { + Curl_gss_log_error(data, "gss_wrap() failed: ", + major_status, minor_status); + free(message); + return CURLE_AUTH_ERROR; + } + + /* Return the response. */ + result = Curl_bufref_memdup(out, output_token.value, output_token.length); + /* Free the output buffer */ + gss_release_buffer(&unused_status, &output_token); + + /* Free the message buffer */ + free(message); + + return result; +} + +/* + * Curl_auth_cleanup_gssapi() + * + * This is used to clean up the GSSAPI (Kerberos V5) specific data. + * + * Parameters: + * + * krb5 [in/out] - The Kerberos 5 data struct being cleaned up. + * + */ +void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) +{ + OM_uint32 minor_status; + + /* Free our security context */ + if(krb5->context != GSS_C_NO_CONTEXT) { + gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER); + krb5->context = GSS_C_NO_CONTEXT; + } + + /* Free the SPN */ + if(krb5->spn != GSS_C_NO_NAME) { + gss_release_name(&minor_status, &krb5->spn); + krb5->spn = GSS_C_NO_NAME; + } +} + +#endif /* HAVE_GSSAPI && USE_KERBEROS5 */ diff --git a/third_party/curl/lib/vauth/krb5_sspi.c b/third_party/curl/lib/vauth/krb5_sspi.c new file mode 100644 index 0000000000..430eb3ef0e --- /dev/null +++ b/third_party/curl/lib/vauth/krb5_sspi.c @@ -0,0 +1,475 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * + * 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 + * + * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5) + +#include + +#include "vauth/vauth.h" +#include "urldata.h" +#include "warnless.h" +#include "curl_multibyte.h" +#include "sendf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_auth_is_gssapi_supported() + * + * This is used to evaluate if GSSAPI (Kerberos V5) is supported. + * + * Parameters: None + * + * Returns TRUE if Kerberos V5 is supported by Windows SSPI. + */ +bool Curl_auth_is_gssapi_supported(void) +{ + PSecPkgInfo SecurityPackage; + SECURITY_STATUS status; + + /* Query the security package for Kerberos */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) + TEXT(SP_NAME_KERBEROS), + &SecurityPackage); + + /* Release the package buffer as it is not required anymore */ + if(status == SEC_E_OK) { + s_pSecFn->FreeContextBuffer(SecurityPackage); + } + + return (status == SEC_E_OK ? TRUE : FALSE); +} + +/* + * Curl_auth_create_gssapi_user_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) user token + * message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The username in the format User or Domain\User. + * passwdp [in] - The user's password. + * service [in] - The service type such as http, smtp, pop or imap. + * host [in] - The hostname. + * mutual_auth [in] - Flag specifying whether or not mutual authentication + * is enabled. + * chlg [in] - Optional challenge message. + * krb5 [in/out] - The Kerberos 5 data struct being used and modified. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + const char *service, + const char *host, + const bool mutual_auth, + const struct bufref *chlg, + struct kerberos5data *krb5, + struct bufref *out) +{ + CURLcode result = CURLE_OK; + CtxtHandle context; + PSecPkgInfo SecurityPackage; + SecBuffer chlg_buf; + SecBuffer resp_buf; + SecBufferDesc chlg_desc; + SecBufferDesc resp_desc; + SECURITY_STATUS status; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ + + if(!krb5->spn) { + /* Generate our SPN */ + krb5->spn = Curl_auth_build_spn(service, host, NULL); + if(!krb5->spn) + return CURLE_OUT_OF_MEMORY; + } + + if(!krb5->output_token) { + /* Query the security package for Kerberos */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) + TEXT(SP_NAME_KERBEROS), + &SecurityPackage); + if(status != SEC_E_OK) { + failf(data, "SSPI: could not get auth info"); + return CURLE_AUTH_ERROR; + } + + krb5->token_max = SecurityPackage->cbMaxToken; + + /* Release the package buffer as it is not required anymore */ + s_pSecFn->FreeContextBuffer(SecurityPackage); + + /* Allocate our response buffer */ + krb5->output_token = malloc(krb5->token_max); + if(!krb5->output_token) + return CURLE_OUT_OF_MEMORY; + } + + if(!krb5->credentials) { + /* Do we have credentials to use or are we using single sign-on? */ + if(userp && *userp) { + /* Populate our identity structure */ + result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); + if(result) + return result; + + /* Allow proper cleanup of the identity structure */ + krb5->p_identity = &krb5->identity; + } + else + /* Use the current Windows user */ + krb5->p_identity = NULL; + + /* Allocate our credentials handle */ + krb5->credentials = calloc(1, sizeof(CredHandle)); + if(!krb5->credentials) + return CURLE_OUT_OF_MEMORY; + + /* Acquire our credentials handle */ + status = s_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *) + TEXT(SP_NAME_KERBEROS), + SECPKG_CRED_OUTBOUND, NULL, + krb5->p_identity, NULL, NULL, + krb5->credentials, &expiry); + if(status != SEC_E_OK) + return CURLE_LOGIN_DENIED; + + /* Allocate our new context handle */ + krb5->context = calloc(1, sizeof(CtxtHandle)); + if(!krb5->context) + return CURLE_OUT_OF_MEMORY; + } + + if(chlg) { + if(!Curl_bufref_len(chlg)) { + infof(data, "GSSAPI handshake failure (empty challenge message)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Setup the challenge "input" security buffer */ + chlg_desc.ulVersion = SECBUFFER_VERSION; + chlg_desc.cBuffers = 1; + chlg_desc.pBuffers = &chlg_buf; + chlg_buf.BufferType = SECBUFFER_TOKEN; + chlg_buf.pvBuffer = (void *) Curl_bufref_ptr(chlg); + chlg_buf.cbBuffer = curlx_uztoul(Curl_bufref_len(chlg)); + } + + /* Setup the response "output" security buffer */ + resp_desc.ulVersion = SECBUFFER_VERSION; + resp_desc.cBuffers = 1; + resp_desc.pBuffers = &resp_buf; + resp_buf.BufferType = SECBUFFER_TOKEN; + resp_buf.pvBuffer = krb5->output_token; + resp_buf.cbBuffer = curlx_uztoul(krb5->token_max); + + /* Generate our challenge-response message */ + status = s_pSecFn->InitializeSecurityContext(krb5->credentials, + chlg ? krb5->context : NULL, + krb5->spn, + (mutual_auth ? + ISC_REQ_MUTUAL_AUTH : 0), + 0, SECURITY_NATIVE_DREP, + chlg ? &chlg_desc : NULL, 0, + &context, + &resp_desc, &attrs, + &expiry); + + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) + return CURLE_AUTH_ERROR; + + if(memcmp(&context, krb5->context, sizeof(context))) { + s_pSecFn->DeleteSecurityContext(krb5->context); + + memcpy(krb5->context, &context, sizeof(context)); + } + + if(resp_buf.cbBuffer) { + result = Curl_bufref_memdup(out, resp_buf.pvBuffer, resp_buf.cbBuffer); + } + else if(mutual_auth) + Curl_bufref_set(out, "", 0, NULL); + else + Curl_bufref_set(out, NULL, 0, NULL); + + return result; +} + +/* + * Curl_auth_create_gssapi_security_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) security + * token message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * authzid [in] - The authorization identity if some. + * chlg [in] - The optional challenge message. + * krb5 [in/out] - The Kerberos 5 data struct being used and modified. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, + const char *authzid, + const struct bufref *chlg, + struct kerberos5data *krb5, + struct bufref *out) +{ + size_t offset = 0; + size_t messagelen = 0; + size_t appdatalen = 0; + unsigned char *trailer = NULL; + unsigned char *message = NULL; + unsigned char *padding = NULL; + unsigned char *appdata = NULL; + SecBuffer input_buf[2]; + SecBuffer wrap_buf[3]; + SecBufferDesc input_desc; + SecBufferDesc wrap_desc; + unsigned char *indata; + unsigned long qop = 0; + unsigned long sec_layer = 0; + unsigned long max_size = 0; + SecPkgContext_Sizes sizes; + SECURITY_STATUS status; + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) data; +#endif + + /* Ensure we have a valid challenge message */ + if(!Curl_bufref_len(chlg)) { + infof(data, "GSSAPI handshake failure (empty security message)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Get our response size information */ + status = s_pSecFn->QueryContextAttributes(krb5->context, + SECPKG_ATTR_SIZES, + &sizes); + + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + if(status != SEC_E_OK) + return CURLE_AUTH_ERROR; + + /* Setup the "input" security buffer */ + input_desc.ulVersion = SECBUFFER_VERSION; + input_desc.cBuffers = 2; + input_desc.pBuffers = input_buf; + input_buf[0].BufferType = SECBUFFER_STREAM; + input_buf[0].pvBuffer = (void *) Curl_bufref_ptr(chlg); + input_buf[0].cbBuffer = curlx_uztoul(Curl_bufref_len(chlg)); + input_buf[1].BufferType = SECBUFFER_DATA; + input_buf[1].pvBuffer = NULL; + input_buf[1].cbBuffer = 0; + + /* Decrypt the inbound challenge and obtain the qop */ + status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); + if(status != SEC_E_OK) { + infof(data, "GSSAPI handshake failure (empty security message)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ + if(input_buf[1].cbBuffer != 4) { + infof(data, "GSSAPI handshake failure (invalid security data)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Extract the security layer and the maximum message size */ + indata = input_buf[1].pvBuffer; + sec_layer = indata[0]; + max_size = ((unsigned long)indata[1] << 16) | + ((unsigned long)indata[2] << 8) | indata[3]; + + /* Free the challenge as it is not required anymore */ + s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); + + /* Process the security layer */ + if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { + infof(data, "GSSAPI handshake failure (invalid security layer)"); + return CURLE_BAD_CONTENT_ENCODING; + } + sec_layer &= KERB_WRAP_NO_ENCRYPT; /* We do not support a security layer */ + + /* Process the maximum message size the server can receive */ + if(max_size > 0) { + /* The server has told us it supports a maximum receive buffer, however, as + we do not require one unless we are encrypting data, we tell the server + our receive buffer is zero. */ + max_size = 0; + } + + /* Allocate the trailer */ + trailer = malloc(sizes.cbSecurityTrailer); + if(!trailer) + return CURLE_OUT_OF_MEMORY; + + /* Allocate our message */ + messagelen = 4; + if(authzid) + messagelen += strlen(authzid); + message = malloc(messagelen); + if(!message) { + free(trailer); + + return CURLE_OUT_OF_MEMORY; + } + + /* Populate the message with the security layer and client supported receive + message size. */ + message[0] = sec_layer & 0xFF; + message[1] = (max_size >> 16) & 0xFF; + message[2] = (max_size >> 8) & 0xFF; + message[3] = max_size & 0xFF; + + /* If given, append the authorization identity. */ + + if(authzid && *authzid) + memcpy(message + 4, authzid, messagelen - 4); + + /* Allocate the padding */ + padding = malloc(sizes.cbBlockSize); + if(!padding) { + free(message); + free(trailer); + + return CURLE_OUT_OF_MEMORY; + } + + /* Setup the "authentication data" security buffer */ + wrap_desc.ulVersion = SECBUFFER_VERSION; + wrap_desc.cBuffers = 3; + wrap_desc.pBuffers = wrap_buf; + wrap_buf[0].BufferType = SECBUFFER_TOKEN; + wrap_buf[0].pvBuffer = trailer; + wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer; + wrap_buf[1].BufferType = SECBUFFER_DATA; + wrap_buf[1].pvBuffer = message; + wrap_buf[1].cbBuffer = curlx_uztoul(messagelen); + wrap_buf[2].BufferType = SECBUFFER_PADDING; + wrap_buf[2].pvBuffer = padding; + wrap_buf[2].cbBuffer = sizes.cbBlockSize; + + /* Encrypt the data */ + status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, + &wrap_desc, 0); + if(status != SEC_E_OK) { + free(padding); + free(message); + free(trailer); + + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; + } + + /* Allocate the encryption (wrap) buffer */ + appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer + + wrap_buf[2].cbBuffer; + appdata = malloc(appdatalen); + if(!appdata) { + free(padding); + free(message); + free(trailer); + + return CURLE_OUT_OF_MEMORY; + } + + /* Populate the encryption buffer */ + memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer); + offset += wrap_buf[0].cbBuffer; + memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer); + offset += wrap_buf[1].cbBuffer; + memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer); + + /* Free all of our local buffers */ + free(padding); + free(message); + free(trailer); + + /* Return the response. */ + Curl_bufref_set(out, appdata, appdatalen, curl_free); + return CURLE_OK; +} + +/* + * Curl_auth_cleanup_gssapi() + * + * This is used to clean up the GSSAPI (Kerberos V5) specific data. + * + * Parameters: + * + * krb5 [in/out] - The Kerberos 5 data struct being cleaned up. + * + */ +void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) +{ + /* Free our security context */ + if(krb5->context) { + s_pSecFn->DeleteSecurityContext(krb5->context); + free(krb5->context); + krb5->context = NULL; + } + + /* Free our credentials handle */ + if(krb5->credentials) { + s_pSecFn->FreeCredentialsHandle(krb5->credentials); + free(krb5->credentials); + krb5->credentials = NULL; + } + + /* Free our identity */ + Curl_sspi_free_identity(krb5->p_identity); + krb5->p_identity = NULL; + + /* Free the SPN and output token */ + Curl_safefree(krb5->spn); + Curl_safefree(krb5->output_token); + + /* Reset any variables */ + krb5->token_max = 0; +} + +#endif /* USE_WINDOWS_SSPI && USE_KERBEROS5 */ diff --git a/third_party/curl/lib/vauth/ntlm.c b/third_party/curl/lib/vauth/ntlm.c new file mode 100644 index 0000000000..abebdeda74 --- /dev/null +++ b/third_party/curl/lib/vauth/ntlm.c @@ -0,0 +1,780 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI) + +/* + * NTLM details: + * + * https://davenport.sourceforge.net/ntlm.html + * https://www.innovation.ch/java/ntlm.html + */ + +#define DEBUG_ME 0 + +#include "urldata.h" +#include "sendf.h" +#include "curl_ntlm_core.h" +#include "curl_gethostname.h" +#include "curl_multibyte.h" +#include "curl_md5.h" +#include "warnless.h" +#include "rand.h" +#include "vtls/vtls.h" +#include "strdup.h" + +#define BUILDING_CURL_NTLM_MSGS_C +#include "vauth/vauth.h" +#include "vauth/ntlm.h" +#include "curl_endian.h" +#include "curl_printf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* "NTLMSSP" signature is always in ASCII regardless of the platform */ +#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50" + +/* The fixed hostname we provide, in order to not leak our real local host + name. Copy the name used by Firefox. */ +#define NTLM_HOSTNAME "WORKSTATION" + +#if DEBUG_ME +# define DEBUG_OUT(x) x +static void ntlm_print_flags(FILE *handle, unsigned long flags) +{ + if(flags & NTLMFLAG_NEGOTIATE_UNICODE) + fprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE "); + if(flags & NTLMFLAG_NEGOTIATE_OEM) + fprintf(handle, "NTLMFLAG_NEGOTIATE_OEM "); + if(flags & NTLMFLAG_REQUEST_TARGET) + fprintf(handle, "NTLMFLAG_REQUEST_TARGET "); + if(flags & (1<<3)) + fprintf(handle, "NTLMFLAG_UNKNOWN_3 "); + if(flags & NTLMFLAG_NEGOTIATE_SIGN) + fprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN "); + if(flags & NTLMFLAG_NEGOTIATE_SEAL) + fprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL "); + if(flags & NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE) + fprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE "); + if(flags & NTLMFLAG_NEGOTIATE_LM_KEY) + fprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY "); + if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY) + fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY "); + if(flags & (1<<10)) + fprintf(handle, "NTLMFLAG_UNKNOWN_10 "); + if(flags & NTLMFLAG_NEGOTIATE_ANONYMOUS) + fprintf(handle, "NTLMFLAG_NEGOTIATE_ANONYMOUS "); + if(flags & NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED) + fprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED "); + if(flags & NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED) + fprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED "); + if(flags & NTLMFLAG_NEGOTIATE_LOCAL_CALL) + fprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL "); + if(flags & NTLMFLAG_NEGOTIATE_ALWAYS_SIGN) + fprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN "); + if(flags & NTLMFLAG_TARGET_TYPE_DOMAIN) + fprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN "); + if(flags & NTLMFLAG_TARGET_TYPE_SERVER) + fprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER "); + if(flags & NTLMFLAG_TARGET_TYPE_SHARE) + fprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE "); + if(flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) + fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY "); + if(flags & NTLMFLAG_REQUEST_INIT_RESPONSE) + fprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE "); + if(flags & NTLMFLAG_REQUEST_ACCEPT_RESPONSE) + fprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE "); + if(flags & NTLMFLAG_REQUEST_NONNT_SESSION_KEY) + fprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY "); + if(flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) + fprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO "); + if(flags & (1<<24)) + fprintf(handle, "NTLMFLAG_UNKNOWN_24 "); + if(flags & (1<<25)) + fprintf(handle, "NTLMFLAG_UNKNOWN_25 "); + if(flags & (1<<26)) + fprintf(handle, "NTLMFLAG_UNKNOWN_26 "); + if(flags & (1<<27)) + fprintf(handle, "NTLMFLAG_UNKNOWN_27 "); + if(flags & (1<<28)) + fprintf(handle, "NTLMFLAG_UNKNOWN_28 "); + if(flags & NTLMFLAG_NEGOTIATE_128) + fprintf(handle, "NTLMFLAG_NEGOTIATE_128 "); + if(flags & NTLMFLAG_NEGOTIATE_KEY_EXCHANGE) + fprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE "); + if(flags & NTLMFLAG_NEGOTIATE_56) + fprintf(handle, "NTLMFLAG_NEGOTIATE_56 "); +} + +static void ntlm_print_hex(FILE *handle, const char *buf, size_t len) +{ + const char *p = buf; + + (void) handle; + + fprintf(stderr, "0x"); + while(len-- > 0) + fprintf(stderr, "%02.2x", (unsigned int)*p++); +} +#else +# define DEBUG_OUT(x) Curl_nop_stmt +#endif + +/* + * ntlm_decode_type2_target() + * + * This is used to decode the "target info" in the NTLM type-2 message + * received. + * + * Parameters: + * + * data [in] - The session handle. + * type2ref [in] - The type-2 message. + * ntlm [in/out] - The NTLM data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +static CURLcode ntlm_decode_type2_target(struct Curl_easy *data, + const struct bufref *type2ref, + struct ntlmdata *ntlm) +{ + unsigned short target_info_len = 0; + unsigned int target_info_offset = 0; + const unsigned char *type2 = Curl_bufref_ptr(type2ref); + size_t type2len = Curl_bufref_len(type2ref); + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) data; +#endif + + if(type2len >= 48) { + target_info_len = Curl_read16_le(&type2[40]); + target_info_offset = Curl_read32_le(&type2[44]); + if(target_info_len > 0) { + if((target_info_offset > type2len) || + (target_info_offset + target_info_len) > type2len || + target_info_offset < 48) { + infof(data, "NTLM handshake failure (bad type-2 message). " + "Target Info Offset Len is set incorrect by the peer"); + return CURLE_BAD_CONTENT_ENCODING; + } + + free(ntlm->target_info); /* replace any previous data */ + ntlm->target_info = Curl_memdup(&type2[target_info_offset], + target_info_len); + if(!ntlm->target_info) + return CURLE_OUT_OF_MEMORY; + } + } + + ntlm->target_info_len = target_info_len; + + return CURLE_OK; +} + +/* + NTLM message structure notes: + + A 'short' is a 'network short', a little-endian 16-bit unsigned value. + + A 'long' is a 'network long', a little-endian, 32-bit unsigned value. + + A 'security buffer' represents a triplet used to point to a buffer, + consisting of two shorts and one long: + + 1. A 'short' containing the length of the buffer content in bytes. + 2. A 'short' containing the allocated space for the buffer in bytes. + 3. A 'long' containing the offset to the start of the buffer in bytes, + from the beginning of the NTLM message. +*/ + +/* + * Curl_auth_is_ntlm_supported() + * + * This is used to evaluate if NTLM is supported. + * + * Parameters: None + * + * Returns TRUE as NTLM as handled by libcurl. + */ +bool Curl_auth_is_ntlm_supported(void) +{ + return TRUE; +} + +/* + * Curl_auth_decode_ntlm_type2_message() + * + * This is used to decode an NTLM type-2 message. The raw NTLM message is + * checked * for validity before the appropriate data for creating a type-3 + * message is * written to the given NTLM data structure. + * + * Parameters: + * + * data [in] - The session handle. + * type2ref [in] - The type-2 message. + * ntlm [in/out] - The NTLM data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, + const struct bufref *type2ref, + struct ntlmdata *ntlm) +{ + static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 }; + + /* NTLM type-2 message structure: + + Index Description Content + 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" + (0x4e544c4d53535000) + 8 NTLM Message Type long (0x02000000) + 12 Target Name security buffer + 20 Flags long + 24 Challenge 8 bytes + (32) Context 8 bytes (two consecutive longs) (*) + (40) Target Information security buffer (*) + (48) OS Version Structure 8 bytes (*) + 32 (48) (56) Start of data block (*) + (*) -> Optional + */ + + CURLcode result = CURLE_OK; + const unsigned char *type2 = Curl_bufref_ptr(type2ref); + size_t type2len = Curl_bufref_len(type2ref); + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void)data; +#endif + + ntlm->flags = 0; + + if((type2len < 32) || + (memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) || + (memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) { + /* This was not a good enough type-2 message */ + infof(data, "NTLM handshake failure (bad type-2 message)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + ntlm->flags = Curl_read32_le(&type2[20]); + memcpy(ntlm->nonce, &type2[24], 8); + + if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) { + result = ntlm_decode_type2_target(data, type2ref, ntlm); + if(result) { + infof(data, "NTLM handshake failure (bad type-2 message)"); + return result; + } + } + + DEBUG_OUT({ + fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags); + ntlm_print_flags(stderr, ntlm->flags); + fprintf(stderr, "\n nonce="); + ntlm_print_hex(stderr, (char *)ntlm->nonce, 8); + fprintf(stderr, "\n****\n"); + fprintf(stderr, "**** Header %s\n ", header); + }); + + return result; +} + +/* copy the source to the destination and fill in zeroes in every + other destination byte! */ +static void unicodecpy(unsigned char *dest, const char *src, size_t length) +{ + size_t i; + for(i = 0; i < length; i++) { + dest[2 * i] = (unsigned char)src[i]; + dest[2 * i + 1] = '\0'; + } +} + +/* + * Curl_auth_create_ntlm_type1_message() + * + * This is used to generate an NTLM type-1 message ready for sending to the + * recipient using the appropriate compile time crypto API. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The username in the format User or Domain\User. + * passwdp [in] - The user's password. + * service [in] - The service type such as http, smtp, pop or imap. + * host [in] - The hostname. + * ntlm [in/out] - The NTLM data struct being used and modified. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + const char *service, + const char *hostname, + struct ntlmdata *ntlm, + struct bufref *out) +{ + /* NTLM type-1 message structure: + + Index Description Content + 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" + (0x4e544c4d53535000) + 8 NTLM Message Type long (0x01000000) + 12 Flags long + (16) Supplied Domain security buffer (*) + (24) Supplied Workstation security buffer (*) + (32) OS Version Structure 8 bytes (*) + (32) (40) Start of data block (*) + (*) -> Optional + */ + + size_t size; + + char *ntlmbuf; + const char *host = ""; /* empty */ + const char *domain = ""; /* empty */ + size_t hostlen = 0; + size_t domlen = 0; + size_t hostoff = 0; + size_t domoff = hostoff + hostlen; /* This is 0: remember that host and + domain are empty */ + (void)data; + (void)userp; + (void)passwdp; + (void)service; + (void)hostname; + + /* Clean up any former leftovers and initialise to defaults */ + Curl_auth_cleanup_ntlm(ntlm); + + ntlmbuf = aprintf(NTLMSSP_SIGNATURE "%c" + "\x01%c%c%c" /* 32-bit type = 1 */ + "%c%c%c%c" /* 32-bit NTLM flag field */ + "%c%c" /* domain length */ + "%c%c" /* domain allocated space */ + "%c%c" /* domain name offset */ + "%c%c" /* 2 zeroes */ + "%c%c" /* host length */ + "%c%c" /* host allocated space */ + "%c%c" /* hostname offset */ + "%c%c" /* 2 zeroes */ + "%s" /* hostname */ + "%s", /* domain string */ + 0, /* trailing zero */ + 0, 0, 0, /* part of type-1 long */ + + LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM | + NTLMFLAG_REQUEST_TARGET | + NTLMFLAG_NEGOTIATE_NTLM_KEY | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | + NTLMFLAG_NEGOTIATE_ALWAYS_SIGN), + SHORTPAIR(domlen), + SHORTPAIR(domlen), + SHORTPAIR(domoff), + 0, 0, + SHORTPAIR(hostlen), + SHORTPAIR(hostlen), + SHORTPAIR(hostoff), + 0, 0, + host, /* this is empty */ + domain /* this is empty */); + + if(!ntlmbuf) + return CURLE_OUT_OF_MEMORY; + + /* Initial packet length */ + size = 32 + hostlen + domlen; + + DEBUG_OUT({ + fprintf(stderr, "* TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x " + "0x%08.8x ", + LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM | + NTLMFLAG_REQUEST_TARGET | + NTLMFLAG_NEGOTIATE_NTLM_KEY | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | + NTLMFLAG_NEGOTIATE_ALWAYS_SIGN), + NTLMFLAG_NEGOTIATE_OEM | + NTLMFLAG_REQUEST_TARGET | + NTLMFLAG_NEGOTIATE_NTLM_KEY | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | + NTLMFLAG_NEGOTIATE_ALWAYS_SIGN); + ntlm_print_flags(stderr, + NTLMFLAG_NEGOTIATE_OEM | + NTLMFLAG_REQUEST_TARGET | + NTLMFLAG_NEGOTIATE_NTLM_KEY | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | + NTLMFLAG_NEGOTIATE_ALWAYS_SIGN); + fprintf(stderr, "\n****\n"); + }); + + Curl_bufref_set(out, ntlmbuf, size, curl_free); + return CURLE_OK; +} + +/* + * Curl_auth_create_ntlm_type3_message() + * + * This is used to generate an already encoded NTLM type-3 message ready for + * sending to the recipient using the appropriate compile time crypto API. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The username in the format User or Domain\User. + * passwdp [in] - The user's password. + * ntlm [in/out] - The NTLM data struct being used and modified. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + struct ntlmdata *ntlm, + struct bufref *out) +{ + /* NTLM type-3 message structure: + + Index Description Content + 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" + (0x4e544c4d53535000) + 8 NTLM Message Type long (0x03000000) + 12 LM/LMv2 Response security buffer + 20 NTLM/NTLMv2 Response security buffer + 28 Target Name security buffer + 36 username security buffer + 44 Workstation Name security buffer + (52) Session Key security buffer (*) + (60) Flags long (*) + (64) OS Version Structure 8 bytes (*) + 52 (64) (72) Start of data block + (*) -> Optional + */ + + CURLcode result = CURLE_OK; + size_t size; + unsigned char ntlmbuf[NTLM_BUFSIZE]; + unsigned int lmrespoff; + unsigned char lmresp[24]; /* fixed-size */ + unsigned int ntrespoff; + unsigned int ntresplen = 24; + unsigned char ntresp[24]; /* fixed-size */ + unsigned char *ptr_ntresp = &ntresp[0]; + unsigned char *ntlmv2resp = NULL; + bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? TRUE : FALSE; + char host[HOSTNAME_MAX + 1] = ""; + const char *user; + const char *domain = ""; + size_t hostoff = 0; + size_t useroff = 0; + size_t domoff = 0; + size_t hostlen = 0; + size_t userlen = 0; + size_t domlen = 0; + + memset(lmresp, 0, sizeof(lmresp)); + memset(ntresp, 0, sizeof(ntresp)); + user = strchr(userp, '\\'); + if(!user) + user = strchr(userp, '/'); + + if(user) { + domain = userp; + domlen = (user - domain); + user++; + } + else + user = userp; + + userlen = strlen(user); + +#ifndef NTLM_HOSTNAME + /* Get the machine's un-qualified hostname as NTLM does not like the fully + qualified domain name */ + if(Curl_gethostname(host, sizeof(host))) { + infof(data, "gethostname() failed, continuing without"); + hostlen = 0; + } + else { + hostlen = strlen(host); + } +#else + (void)msnprintf(host, sizeof(host), "%s", NTLM_HOSTNAME); + hostlen = sizeof(NTLM_HOSTNAME)-1; +#endif + + if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) { + unsigned char ntbuffer[0x18]; + unsigned char entropy[8]; + unsigned char ntlmv2hash[0x18]; + + /* Full NTLM version 2 + Although this cannot be negotiated, it is used here if available, as + servers featuring extended security are likely supporting also + NTLMv2. */ + result = Curl_rand(data, entropy, 8); + if(result) + return result; + + result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer); + if(result) + return result; + + result = Curl_ntlm_core_mk_ntlmv2_hash(user, userlen, domain, domlen, + ntbuffer, ntlmv2hash); + if(result) + return result; + + /* LMv2 response */ + result = Curl_ntlm_core_mk_lmv2_resp(ntlmv2hash, entropy, + &ntlm->nonce[0], lmresp); + if(result) + return result; + + /* NTLMv2 response */ + result = Curl_ntlm_core_mk_ntlmv2_resp(ntlmv2hash, entropy, + ntlm, &ntlmv2resp, &ntresplen); + if(result) + return result; + + ptr_ntresp = ntlmv2resp; + } + else { + + unsigned char ntbuffer[0x18]; + unsigned char lmbuffer[0x18]; + + /* NTLM version 1 */ + + result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer); + if(result) + return result; + + Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], ntresp); + + result = Curl_ntlm_core_mk_lm_hash(passwdp, lmbuffer); + if(result) + return result; + + Curl_ntlm_core_lm_resp(lmbuffer, &ntlm->nonce[0], lmresp); + ntlm->flags &= ~(unsigned int)NTLMFLAG_NEGOTIATE_NTLM2_KEY; + + /* A safer but less compatible alternative is: + * Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], lmresp); + * See https://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */ + } + + if(unicode) { + domlen = domlen * 2; + userlen = userlen * 2; + hostlen = hostlen * 2; + } + + lmrespoff = 64; /* size of the message header */ + ntrespoff = lmrespoff + 0x18; + domoff = ntrespoff + ntresplen; + useroff = domoff + domlen; + hostoff = useroff + userlen; + + /* Create the big type-3 message binary blob */ + size = msnprintf((char *)ntlmbuf, NTLM_BUFSIZE, + NTLMSSP_SIGNATURE "%c" + "\x03%c%c%c" /* 32-bit type = 3 */ + + "%c%c" /* LanManager length */ + "%c%c" /* LanManager allocated space */ + "%c%c" /* LanManager offset */ + "%c%c" /* 2 zeroes */ + + "%c%c" /* NT-response length */ + "%c%c" /* NT-response allocated space */ + "%c%c" /* NT-response offset */ + "%c%c" /* 2 zeroes */ + + "%c%c" /* domain length */ + "%c%c" /* domain allocated space */ + "%c%c" /* domain name offset */ + "%c%c" /* 2 zeroes */ + + "%c%c" /* user length */ + "%c%c" /* user allocated space */ + "%c%c" /* user offset */ + "%c%c" /* 2 zeroes */ + + "%c%c" /* host length */ + "%c%c" /* host allocated space */ + "%c%c" /* host offset */ + "%c%c" /* 2 zeroes */ + + "%c%c" /* session key length (unknown purpose) */ + "%c%c" /* session key allocated space (unknown purpose) */ + "%c%c" /* session key offset (unknown purpose) */ + "%c%c" /* 2 zeroes */ + + "%c%c%c%c", /* flags */ + + /* domain string */ + /* user string */ + /* host string */ + /* LanManager response */ + /* NT response */ + + 0, /* null-termination */ + 0, 0, 0, /* type-3 long, the 24 upper bits */ + + SHORTPAIR(0x18), /* LanManager response length, twice */ + SHORTPAIR(0x18), + SHORTPAIR(lmrespoff), + 0x0, 0x0, + + SHORTPAIR(ntresplen), /* NT-response length, twice */ + SHORTPAIR(ntresplen), + SHORTPAIR(ntrespoff), + 0x0, 0x0, + + SHORTPAIR(domlen), + SHORTPAIR(domlen), + SHORTPAIR(domoff), + 0x0, 0x0, + + SHORTPAIR(userlen), + SHORTPAIR(userlen), + SHORTPAIR(useroff), + 0x0, 0x0, + + SHORTPAIR(hostlen), + SHORTPAIR(hostlen), + SHORTPAIR(hostoff), + 0x0, 0x0, + + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + + LONGQUARTET(ntlm->flags)); + + DEBUGASSERT(size == 64); + DEBUGASSERT(size == (size_t)lmrespoff); + + /* We append the binary hashes */ + if(size < (NTLM_BUFSIZE - 0x18)) { + memcpy(&ntlmbuf[size], lmresp, 0x18); + size += 0x18; + } + + DEBUG_OUT({ + fprintf(stderr, "**** TYPE3 header lmresp="); + ntlm_print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18); + }); + + /* ntresplen + size should not be risking an integer overflow here */ + if(ntresplen + size > sizeof(ntlmbuf)) { + failf(data, "incoming NTLM message too big"); + return CURLE_OUT_OF_MEMORY; + } + DEBUGASSERT(size == (size_t)ntrespoff); + memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen); + size += ntresplen; + + DEBUG_OUT({ + fprintf(stderr, "\n ntresp="); + ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], ntresplen); + }); + + free(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */ + + DEBUG_OUT({ + fprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ", + LONGQUARTET(ntlm->flags), ntlm->flags); + ntlm_print_flags(stderr, ntlm->flags); + fprintf(stderr, "\n****\n"); + }); + + /* Make sure that the domain, user and host strings fit in the + buffer before we copy them there. */ + if(size + userlen + domlen + hostlen >= NTLM_BUFSIZE) { + failf(data, "user + domain + hostname too big"); + return CURLE_OUT_OF_MEMORY; + } + + DEBUGASSERT(size == domoff); + if(unicode) + unicodecpy(&ntlmbuf[size], domain, domlen / 2); + else + memcpy(&ntlmbuf[size], domain, domlen); + + size += domlen; + + DEBUGASSERT(size == useroff); + if(unicode) + unicodecpy(&ntlmbuf[size], user, userlen / 2); + else + memcpy(&ntlmbuf[size], user, userlen); + + size += userlen; + + DEBUGASSERT(size == hostoff); + if(unicode) + unicodecpy(&ntlmbuf[size], host, hostlen / 2); + else + memcpy(&ntlmbuf[size], host, hostlen); + + size += hostlen; + + /* Return the binary blob. */ + result = Curl_bufref_memdup(out, ntlmbuf, size); + + Curl_auth_cleanup_ntlm(ntlm); + + return result; +} + +/* + * Curl_auth_cleanup_ntlm() + * + * This is used to clean up the NTLM specific data. + * + * Parameters: + * + * ntlm [in/out] - The NTLM data struct being cleaned up. + * + */ +void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) +{ + /* Free the target info */ + Curl_safefree(ntlm->target_info); + + /* Reset any variables */ + ntlm->target_info_len = 0; +} + +#endif /* USE_NTLM && !USE_WINDOWS_SSPI */ diff --git a/third_party/curl/lib/vauth/ntlm.h b/third_party/curl/lib/vauth/ntlm.h new file mode 100644 index 0000000000..31ce921cd1 --- /dev/null +++ b/third_party/curl/lib/vauth/ntlm.h @@ -0,0 +1,143 @@ +#ifndef HEADER_VAUTH_NTLM_H +#define HEADER_VAUTH_NTLM_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_NTLM + +/* NTLM buffer fixed size, large enough for long user + host + domain */ +#define NTLM_BUFSIZE 1024 + +/* Stuff only required for curl_ntlm_msgs.c */ +#ifdef BUILDING_CURL_NTLM_MSGS_C + +/* Flag bits definitions based on + https://davenport.sourceforge.net/ntlm.html */ + +#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0) +/* Indicates that Unicode strings are supported for use in security buffer + data. */ + +#define NTLMFLAG_NEGOTIATE_OEM (1<<1) +/* Indicates that OEM strings are supported for use in security buffer data. */ + +#define NTLMFLAG_REQUEST_TARGET (1<<2) +/* Requests that the server's authentication realm be included in the Type 2 + message. */ + +/* unknown (1<<3) */ +#define NTLMFLAG_NEGOTIATE_SIGN (1<<4) +/* Specifies that authenticated communication between the client and server + should carry a digital signature (message integrity). */ + +#define NTLMFLAG_NEGOTIATE_SEAL (1<<5) +/* Specifies that authenticated communication between the client and server + should be encrypted (message confidentiality). */ + +#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6) +/* Indicates that datagram authentication is being used. */ + +#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7) +/* Indicates that the LAN Manager session key should be used for signing and + sealing authenticated communications. */ + +#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9) +/* Indicates that NTLM authentication is being used. */ + +/* unknown (1<<10) */ + +#define NTLMFLAG_NEGOTIATE_ANONYMOUS (1<<11) +/* Sent by the client in the Type 3 message to indicate that an anonymous + context has been established. This also affects the response fields. */ + +#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12) +/* Sent by the client in the Type 1 message to indicate that a desired + authentication realm is included in the message. */ + +#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13) +/* Sent by the client in the Type 1 message to indicate that the client + workstation's name is included in the message. */ + +#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14) +/* Sent by the server to indicate that the server and client are on the same + machine. Implies that the client may use a pre-established local security + context rather than responding to the challenge. */ + +#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15) +/* Indicates that authenticated communication between the client and server + should be signed with a "dummy" signature. */ + +#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16) +/* Sent by the server in the Type 2 message to indicate that the target + authentication realm is a domain. */ + +#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17) +/* Sent by the server in the Type 2 message to indicate that the target + authentication realm is a server. */ + +#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18) +/* Sent by the server in the Type 2 message to indicate that the target + authentication realm is a share. Presumably, this is for share-level + authentication. Usage is unclear. */ + +#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19) +/* Indicates that the NTLM2 signing and sealing scheme should be used for + protecting authenticated communications. */ + +#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20) +/* unknown purpose */ + +#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21) +/* unknown purpose */ + +#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22) +/* unknown purpose */ + +#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23) +/* Sent by the server in the Type 2 message to indicate that it is including a + Target Information block in the message. */ + +/* unknown (1<24) */ +/* unknown (1<25) */ +/* unknown (1<26) */ +/* unknown (1<27) */ +/* unknown (1<28) */ + +#define NTLMFLAG_NEGOTIATE_128 (1<<29) +/* Indicates that 128-bit encryption is supported. */ + +#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30) +/* Indicates that the client will provide an encrypted master key in + the "Session Key" field of the Type 3 message. */ + +#define NTLMFLAG_NEGOTIATE_56 (1<<31) +/* Indicates that 56-bit encryption is supported. */ + +#endif /* BUILDING_CURL_NTLM_MSGS_C */ + +#endif /* USE_NTLM */ + +#endif /* HEADER_VAUTH_NTLM_H */ diff --git a/third_party/curl/lib/vauth/ntlm_sspi.c b/third_party/curl/lib/vauth/ntlm_sspi.c new file mode 100644 index 0000000000..7d1d04e004 --- /dev/null +++ b/third_party/curl/lib/vauth/ntlm_sspi.c @@ -0,0 +1,372 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_WINDOWS_SSPI) && defined(USE_NTLM) + +#include + +#include "vauth/vauth.h" +#include "urldata.h" +#include "curl_ntlm_core.h" +#include "warnless.h" +#include "curl_multibyte.h" +#include "sendf.h" +#include "strdup.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_auth_is_ntlm_supported() + * + * This is used to evaluate if NTLM is supported. + * + * Parameters: None + * + * Returns TRUE if NTLM is supported by Windows SSPI. + */ +bool Curl_auth_is_ntlm_supported(void) +{ + PSecPkgInfo SecurityPackage; + SECURITY_STATUS status; + + /* Query the security package for NTLM */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), + &SecurityPackage); + + /* Release the package buffer as it is not required anymore */ + if(status == SEC_E_OK) { + s_pSecFn->FreeContextBuffer(SecurityPackage); + } + + return (status == SEC_E_OK ? TRUE : FALSE); +} + +/* + * Curl_auth_create_ntlm_type1_message() + * + * This is used to generate an already encoded NTLM type-1 message ready for + * sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The username in the format User or Domain\User. + * passwdp [in] - The user's password. + * service [in] - The service type such as http, smtp, pop or imap. + * host [in] - The hostname. + * ntlm [in/out] - The NTLM data struct being used and modified. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + const char *service, + const char *host, + struct ntlmdata *ntlm, + struct bufref *out) +{ + PSecPkgInfo SecurityPackage; + SecBuffer type_1_buf; + SecBufferDesc type_1_desc; + SECURITY_STATUS status; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ + + /* Clean up any former leftovers and initialise to defaults */ + Curl_auth_cleanup_ntlm(ntlm); + + /* Query the security package for NTLM */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), + &SecurityPackage); + if(status != SEC_E_OK) { + failf(data, "SSPI: could not get auth info"); + return CURLE_AUTH_ERROR; + } + + ntlm->token_max = SecurityPackage->cbMaxToken; + + /* Release the package buffer as it is not required anymore */ + s_pSecFn->FreeContextBuffer(SecurityPackage); + + /* Allocate our output buffer */ + ntlm->output_token = malloc(ntlm->token_max); + if(!ntlm->output_token) + return CURLE_OUT_OF_MEMORY; + + if(userp && *userp) { + CURLcode result; + + /* Populate our identity structure */ + result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity); + if(result) + return result; + + /* Allow proper cleanup of the identity structure */ + ntlm->p_identity = &ntlm->identity; + } + else + /* Use the current Windows user */ + ntlm->p_identity = NULL; + + /* Allocate our credentials handle */ + ntlm->credentials = calloc(1, sizeof(CredHandle)); + if(!ntlm->credentials) + return CURLE_OUT_OF_MEMORY; + + /* Acquire our credentials handle */ + status = s_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *) TEXT(SP_NAME_NTLM), + SECPKG_CRED_OUTBOUND, NULL, + ntlm->p_identity, NULL, NULL, + ntlm->credentials, &expiry); + if(status != SEC_E_OK) + return CURLE_LOGIN_DENIED; + + /* Allocate our new context handle */ + ntlm->context = calloc(1, sizeof(CtxtHandle)); + if(!ntlm->context) + return CURLE_OUT_OF_MEMORY; + + ntlm->spn = Curl_auth_build_spn(service, host, NULL); + if(!ntlm->spn) + return CURLE_OUT_OF_MEMORY; + + /* Setup the type-1 "output" security buffer */ + type_1_desc.ulVersion = SECBUFFER_VERSION; + type_1_desc.cBuffers = 1; + type_1_desc.pBuffers = &type_1_buf; + type_1_buf.BufferType = SECBUFFER_TOKEN; + type_1_buf.pvBuffer = ntlm->output_token; + type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max); + + /* Generate our type-1 message */ + status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL, + ntlm->spn, + 0, 0, SECURITY_NETWORK_DREP, + NULL, 0, + ntlm->context, &type_1_desc, + &attrs, &expiry); + if(status == SEC_I_COMPLETE_NEEDED || + status == SEC_I_COMPLETE_AND_CONTINUE) + s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc); + else if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) + return CURLE_AUTH_ERROR; + + /* Return the response. */ + Curl_bufref_set(out, ntlm->output_token, type_1_buf.cbBuffer, NULL); + return CURLE_OK; +} + +/* + * Curl_auth_decode_ntlm_type2_message() + * + * This is used to decode an already encoded NTLM type-2 message. + * + * Parameters: + * + * data [in] - The session handle. + * type2 [in] - The type-2 message. + * ntlm [in/out] - The NTLM data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, + const struct bufref *type2, + struct ntlmdata *ntlm) +{ +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) data; +#endif + + /* Ensure we have a valid type-2 message */ + if(!Curl_bufref_len(type2)) { + infof(data, "NTLM handshake failure (empty type-2 message)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Store the challenge for later use */ + ntlm->input_token = Curl_memdup0((const char *)Curl_bufref_ptr(type2), + Curl_bufref_len(type2)); + if(!ntlm->input_token) + return CURLE_OUT_OF_MEMORY; + ntlm->input_token_len = Curl_bufref_len(type2); + + return CURLE_OK; +} + +/* +* Curl_auth_create_ntlm_type3_message() + * Curl_auth_create_ntlm_type3_message() + * + * This is used to generate an already encoded NTLM type-3 message ready for + * sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The username in the format User or Domain\User. + * passwdp [in] - The user's password. + * ntlm [in/out] - The NTLM data struct being used and modified. + * out [out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + struct ntlmdata *ntlm, + struct bufref *out) +{ + CURLcode result = CURLE_OK; + SecBuffer type_2_bufs[2]; + SecBuffer type_3_buf; + SecBufferDesc type_2_desc; + SecBufferDesc type_3_desc; + SECURITY_STATUS status; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) data; +#endif + (void) passwdp; + (void) userp; + + /* Setup the type-2 "input" security buffer */ + type_2_desc.ulVersion = SECBUFFER_VERSION; + type_2_desc.cBuffers = 1; + type_2_desc.pBuffers = &type_2_bufs[0]; + type_2_bufs[0].BufferType = SECBUFFER_TOKEN; + type_2_bufs[0].pvBuffer = ntlm->input_token; + type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len); + +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + /* ssl context comes from schannel. + * When extended protection is used in IIS server, + * we have to pass a second SecBuffer to the SecBufferDesc + * otherwise IIS will not pass the authentication (401 response). + * Minimum supported version is Windows 7. + * https://docs.microsoft.com/en-us/security-updates + * /SecurityAdvisories/2009/973811 + */ + if(ntlm->sslContext) { + SEC_CHANNEL_BINDINGS channelBindings; + SecPkgContext_Bindings pkgBindings; + pkgBindings.Bindings = &channelBindings; + status = s_pSecFn->QueryContextAttributes( + ntlm->sslContext, + SECPKG_ATTR_ENDPOINT_BINDINGS, + &pkgBindings + ); + if(status == SEC_E_OK) { + type_2_desc.cBuffers++; + type_2_bufs[1].BufferType = SECBUFFER_CHANNEL_BINDINGS; + type_2_bufs[1].cbBuffer = pkgBindings.BindingsLength; + type_2_bufs[1].pvBuffer = pkgBindings.Bindings; + } + } +#endif + + /* Setup the type-3 "output" security buffer */ + type_3_desc.ulVersion = SECBUFFER_VERSION; + type_3_desc.cBuffers = 1; + type_3_desc.pBuffers = &type_3_buf; + type_3_buf.BufferType = SECBUFFER_TOKEN; + type_3_buf.pvBuffer = ntlm->output_token; + type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max); + + /* Generate our type-3 message */ + status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, + ntlm->context, + ntlm->spn, + 0, 0, SECURITY_NETWORK_DREP, + &type_2_desc, + 0, ntlm->context, + &type_3_desc, + &attrs, &expiry); + if(status != SEC_E_OK) { + infof(data, "NTLM handshake failure (type-3 message): Status=%lx", + status); + + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; + } + + /* Return the response. */ + result = Curl_bufref_memdup(out, ntlm->output_token, type_3_buf.cbBuffer); + Curl_auth_cleanup_ntlm(ntlm); + return result; +} + +/* + * Curl_auth_cleanup_ntlm() + * + * This is used to clean up the NTLM specific data. + * + * Parameters: + * + * ntlm [in/out] - The NTLM data struct being cleaned up. + * + */ +void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) +{ + /* Free our security context */ + if(ntlm->context) { + s_pSecFn->DeleteSecurityContext(ntlm->context); + free(ntlm->context); + ntlm->context = NULL; + } + + /* Free our credentials handle */ + if(ntlm->credentials) { + s_pSecFn->FreeCredentialsHandle(ntlm->credentials); + free(ntlm->credentials); + ntlm->credentials = NULL; + } + + /* Free our identity */ + Curl_sspi_free_identity(ntlm->p_identity); + ntlm->p_identity = NULL; + + /* Free the input and output tokens */ + Curl_safefree(ntlm->input_token); + Curl_safefree(ntlm->output_token); + + /* Reset any variables */ + ntlm->token_max = 0; + + Curl_safefree(ntlm->spn); +} + +#endif /* USE_WINDOWS_SSPI && USE_NTLM */ diff --git a/third_party/curl/lib/vauth/oauth2.c b/third_party/curl/lib/vauth/oauth2.c new file mode 100644 index 0000000000..dc94afa365 --- /dev/null +++ b/third_party/curl/lib/vauth/oauth2.c @@ -0,0 +1,108 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC6749 OAuth 2.0 Authorization Framework + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_POP3) || \ + (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) + +#include +#include "urldata.h" + +#include "vauth/vauth.h" +#include "warnless.h" +#include "curl_printf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_auth_create_oauth_bearer_message() + * + * This is used to generate an OAuth 2.0 message ready for sending to the + * recipient. + * + * Parameters: + * + * user[in] - The username. + * host[in] - The hostname. + * port[in] - The port(when not Port 80). + * bearer[in] - The bearer token. + * out[out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_oauth_bearer_message(const char *user, + const char *host, + const long port, + const char *bearer, + struct bufref *out) +{ + char *oauth; + + /* Generate the message */ + if(port == 0 || port == 80) + oauth = aprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host, + bearer); + else + oauth = aprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", user, + host, port, bearer); + if(!oauth) + return CURLE_OUT_OF_MEMORY; + + Curl_bufref_set(out, oauth, strlen(oauth), curl_free); + return CURLE_OK; +} + +/* + * Curl_auth_create_xoauth_bearer_message() + * + * This is used to generate a XOAuth 2.0 message ready for * sending to the + * recipient. + * + * Parameters: + * + * user[in] - The username. + * bearer[in] - The bearer token. + * out[out] - The result storage. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, + const char *bearer, + struct bufref *out) +{ + /* Generate the message */ + char *xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer); + if(!xoauth) + return CURLE_OUT_OF_MEMORY; + + Curl_bufref_set(out, xoauth, strlen(xoauth), curl_free); + return CURLE_OK; +} +#endif /* disabled, no users */ diff --git a/third_party/curl/lib/vauth/spnego_gssapi.c b/third_party/curl/lib/vauth/spnego_gssapi.c new file mode 100644 index 0000000000..23822838be --- /dev/null +++ b/third_party/curl/lib/vauth/spnego_gssapi.c @@ -0,0 +1,281 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC4178 Simple and Protected GSS-API Negotiation Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(HAVE_GSSAPI) && defined(USE_SPNEGO) + +#include + +#include "vauth/vauth.h" +#include "urldata.h" +#include "curl_base64.h" +#include "curl_gssapi.h" +#include "warnless.h" +#include "curl_multibyte.h" +#include "sendf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_auth_is_spnego_supported() + * + * This is used to evaluate if SPNEGO (Negotiate) is supported. + * + * Parameters: None + * + * Returns TRUE if Negotiate supported by the GSS-API library. + */ +bool Curl_auth_is_spnego_supported(void) +{ + return TRUE; +} + +/* + * Curl_auth_decode_spnego_message() + * + * This is used to decode an already encoded SPNEGO (Negotiate) challenge + * message. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The username in the format User or Domain\User. + * passwdp [in] - The user's password. + * service [in] - The service type such as http, smtp, pop or imap. + * host [in] - The hostname. + * chlg64 [in] - The optional base64 encoded challenge message. + * nego [in/out] - The Negotiate data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, + const char *user, + const char *password, + const char *service, + const char *host, + const char *chlg64, + struct negotiatedata *nego) +{ + CURLcode result = CURLE_OK; + size_t chlglen = 0; + unsigned char *chlg = NULL; + OM_uint32 major_status; + OM_uint32 minor_status; + OM_uint32 unused_status; + gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + + (void) user; + (void) password; + + if(nego->context && nego->status == GSS_S_COMPLETE) { + /* We finished successfully our part of authentication, but server + * rejected it (since we are again here). Exit with an error since we + * cannot invent anything better */ + Curl_auth_cleanup_spnego(nego); + return CURLE_LOGIN_DENIED; + } + + if(!nego->spn) { + /* Generate our SPN */ + char *spn = Curl_auth_build_spn(service, NULL, host); + if(!spn) + return CURLE_OUT_OF_MEMORY; + + /* Populate the SPN structure */ + spn_token.value = spn; + spn_token.length = strlen(spn); + + /* Import the SPN */ + major_status = gss_import_name(&minor_status, &spn_token, + GSS_C_NT_HOSTBASED_SERVICE, + &nego->spn); + if(GSS_ERROR(major_status)) { + Curl_gss_log_error(data, "gss_import_name() failed: ", + major_status, minor_status); + + free(spn); + + return CURLE_AUTH_ERROR; + } + + free(spn); + } + + if(chlg64 && *chlg64) { + /* Decode the base-64 encoded challenge message */ + if(*chlg64 != '=') { + result = Curl_base64_decode(chlg64, &chlg, &chlglen); + if(result) + return result; + } + + /* Ensure we have a valid challenge message */ + if(!chlg) { + infof(data, "SPNEGO handshake failure (empty challenge message)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Setup the challenge "input" security buffer */ + input_token.value = chlg; + input_token.length = chlglen; + } + + /* Generate our challenge-response message */ + major_status = Curl_gss_init_sec_context(data, + &minor_status, + &nego->context, + nego->spn, + &Curl_spnego_mech_oid, + GSS_C_NO_CHANNEL_BINDINGS, + &input_token, + &output_token, + TRUE, + NULL); + + /* Free the decoded challenge as it is not required anymore */ + Curl_safefree(input_token.value); + + nego->status = major_status; + if(GSS_ERROR(major_status)) { + if(output_token.value) + gss_release_buffer(&unused_status, &output_token); + + Curl_gss_log_error(data, "gss_init_sec_context() failed: ", + major_status, minor_status); + + return CURLE_AUTH_ERROR; + } + + if(!output_token.value || !output_token.length) { + if(output_token.value) + gss_release_buffer(&unused_status, &output_token); + + return CURLE_AUTH_ERROR; + } + + /* Free previous token */ + if(nego->output_token.length && nego->output_token.value) + gss_release_buffer(&unused_status, &nego->output_token); + + nego->output_token = output_token; + + return CURLE_OK; +} + +/* + * Curl_auth_create_spnego_message() + * + * This is used to generate an already encoded SPNEGO (Negotiate) response + * message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * nego [in/out] - The Negotiate data struct being used and modified. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, + char **outptr, size_t *outlen) +{ + CURLcode result; + OM_uint32 minor_status; + + /* Base64 encode the already generated response */ + result = Curl_base64_encode(nego->output_token.value, + nego->output_token.length, + outptr, outlen); + + if(result) { + gss_release_buffer(&minor_status, &nego->output_token); + nego->output_token.value = NULL; + nego->output_token.length = 0; + + return result; + } + + if(!*outptr || !*outlen) { + gss_release_buffer(&minor_status, &nego->output_token); + nego->output_token.value = NULL; + nego->output_token.length = 0; + + return CURLE_REMOTE_ACCESS_DENIED; + } + + return CURLE_OK; +} + +/* + * Curl_auth_cleanup_spnego() + * + * This is used to clean up the SPNEGO (Negotiate) specific data. + * + * Parameters: + * + * nego [in/out] - The Negotiate data struct being cleaned up. + * + */ +void Curl_auth_cleanup_spnego(struct negotiatedata *nego) +{ + OM_uint32 minor_status; + + /* Free our security context */ + if(nego->context != GSS_C_NO_CONTEXT) { + gss_delete_sec_context(&minor_status, &nego->context, GSS_C_NO_BUFFER); + nego->context = GSS_C_NO_CONTEXT; + } + + /* Free the output token */ + if(nego->output_token.value) { + gss_release_buffer(&minor_status, &nego->output_token); + nego->output_token.value = NULL; + nego->output_token.length = 0; + + } + + /* Free the SPN */ + if(nego->spn != GSS_C_NO_NAME) { + gss_release_name(&minor_status, &nego->spn); + nego->spn = GSS_C_NO_NAME; + } + + /* Reset any variables */ + nego->status = 0; + nego->noauthpersist = FALSE; + nego->havenoauthpersist = FALSE; + nego->havenegdata = FALSE; + nego->havemultiplerequests = FALSE; +} + +#endif /* HAVE_GSSAPI && USE_SPNEGO */ diff --git a/third_party/curl/lib/vauth/spnego_sspi.c b/third_party/curl/lib/vauth/spnego_sspi.c new file mode 100644 index 0000000000..a2c7072c51 --- /dev/null +++ b/third_party/curl/lib/vauth/spnego_sspi.c @@ -0,0 +1,365 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + * RFC4178 Simple and Protected GSS-API Negotiation Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO) + +#include + +#include "vauth/vauth.h" +#include "urldata.h" +#include "curl_base64.h" +#include "warnless.h" +#include "curl_multibyte.h" +#include "sendf.h" +#include "strerror.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_auth_is_spnego_supported() + * + * This is used to evaluate if SPNEGO (Negotiate) is supported. + * + * Parameters: None + * + * Returns TRUE if Negotiate is supported by Windows SSPI. + */ +bool Curl_auth_is_spnego_supported(void) +{ + PSecPkgInfo SecurityPackage; + SECURITY_STATUS status; + + /* Query the security package for Negotiate */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) + TEXT(SP_NAME_NEGOTIATE), + &SecurityPackage); + + /* Release the package buffer as it is not required anymore */ + if(status == SEC_E_OK) { + s_pSecFn->FreeContextBuffer(SecurityPackage); + } + + + return (status == SEC_E_OK ? TRUE : FALSE); +} + +/* + * Curl_auth_decode_spnego_message() + * + * This is used to decode an already encoded SPNEGO (Negotiate) challenge + * message. + * + * Parameters: + * + * data [in] - The session handle. + * user [in] - The username in the format User or Domain\User. + * password [in] - The user's password. + * service [in] - The service type such as http, smtp, pop or imap. + * host [in] - The hostname. + * chlg64 [in] - The optional base64 encoded challenge message. + * nego [in/out] - The Negotiate data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, + const char *user, + const char *password, + const char *service, + const char *host, + const char *chlg64, + struct negotiatedata *nego) +{ + CURLcode result = CURLE_OK; + size_t chlglen = 0; + unsigned char *chlg = NULL; + PSecPkgInfo SecurityPackage; + SecBuffer chlg_buf[2]; + SecBuffer resp_buf; + SecBufferDesc chlg_desc; + SecBufferDesc resp_desc; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) data; +#endif + + if(nego->context && nego->status == SEC_E_OK) { + /* We finished successfully our part of authentication, but server + * rejected it (since we are again here). Exit with an error since we + * cannot invent anything better */ + Curl_auth_cleanup_spnego(nego); + return CURLE_LOGIN_DENIED; + } + + if(!nego->spn) { + /* Generate our SPN */ + nego->spn = Curl_auth_build_spn(service, host, NULL); + if(!nego->spn) + return CURLE_OUT_OF_MEMORY; + } + + if(!nego->output_token) { + /* Query the security package for Negotiate */ + nego->status = (DWORD)s_pSecFn->QuerySecurityPackageInfo((TCHAR *) + TEXT(SP_NAME_NEGOTIATE), + &SecurityPackage); + if(nego->status != SEC_E_OK) { + failf(data, "SSPI: could not get auth info"); + return CURLE_AUTH_ERROR; + } + + nego->token_max = SecurityPackage->cbMaxToken; + + /* Release the package buffer as it is not required anymore */ + s_pSecFn->FreeContextBuffer(SecurityPackage); + + /* Allocate our output buffer */ + nego->output_token = malloc(nego->token_max); + if(!nego->output_token) + return CURLE_OUT_OF_MEMORY; + } + + if(!nego->credentials) { + /* Do we have credentials to use or are we using single sign-on? */ + if(user && *user) { + /* Populate our identity structure */ + result = Curl_create_sspi_identity(user, password, &nego->identity); + if(result) + return result; + + /* Allow proper cleanup of the identity structure */ + nego->p_identity = &nego->identity; + } + else + /* Use the current Windows user */ + nego->p_identity = NULL; + + /* Allocate our credentials handle */ + nego->credentials = calloc(1, sizeof(CredHandle)); + if(!nego->credentials) + return CURLE_OUT_OF_MEMORY; + + /* Acquire our credentials handle */ + nego->status = (DWORD) + s_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *)TEXT(SP_NAME_NEGOTIATE), + SECPKG_CRED_OUTBOUND, NULL, + nego->p_identity, NULL, NULL, + nego->credentials, &expiry); + if(nego->status != SEC_E_OK) + return CURLE_AUTH_ERROR; + + /* Allocate our new context handle */ + nego->context = calloc(1, sizeof(CtxtHandle)); + if(!nego->context) + return CURLE_OUT_OF_MEMORY; + } + + if(chlg64 && *chlg64) { + /* Decode the base-64 encoded challenge message */ + if(*chlg64 != '=') { + result = Curl_base64_decode(chlg64, &chlg, &chlglen); + if(result) + return result; + } + + /* Ensure we have a valid challenge message */ + if(!chlg) { + infof(data, "SPNEGO handshake failure (empty challenge message)"); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Setup the challenge "input" security buffer */ + chlg_desc.ulVersion = SECBUFFER_VERSION; + chlg_desc.cBuffers = 1; + chlg_desc.pBuffers = &chlg_buf[0]; + chlg_buf[0].BufferType = SECBUFFER_TOKEN; + chlg_buf[0].pvBuffer = chlg; + chlg_buf[0].cbBuffer = curlx_uztoul(chlglen); + +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + /* ssl context comes from Schannel. + * When extended protection is used in IIS server, + * we have to pass a second SecBuffer to the SecBufferDesc + * otherwise IIS will not pass the authentication (401 response). + * Minimum supported version is Windows 7. + * https://docs.microsoft.com/en-us/security-updates + * /SecurityAdvisories/2009/973811 + */ + if(nego->sslContext) { + SEC_CHANNEL_BINDINGS channelBindings; + SecPkgContext_Bindings pkgBindings; + pkgBindings.Bindings = &channelBindings; + nego->status = (DWORD)s_pSecFn->QueryContextAttributes( + nego->sslContext, + SECPKG_ATTR_ENDPOINT_BINDINGS, + &pkgBindings + ); + if(nego->status == SEC_E_OK) { + chlg_desc.cBuffers++; + chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS; + chlg_buf[1].cbBuffer = pkgBindings.BindingsLength; + chlg_buf[1].pvBuffer = pkgBindings.Bindings; + } + } +#endif + } + + /* Setup the response "output" security buffer */ + resp_desc.ulVersion = SECBUFFER_VERSION; + resp_desc.cBuffers = 1; + resp_desc.pBuffers = &resp_buf; + resp_buf.BufferType = SECBUFFER_TOKEN; + resp_buf.pvBuffer = nego->output_token; + resp_buf.cbBuffer = curlx_uztoul(nego->token_max); + + /* Generate our challenge-response message */ + nego->status = (DWORD)s_pSecFn->InitializeSecurityContext(nego->credentials, + chlg ? nego->context : + NULL, + nego->spn, + ISC_REQ_CONFIDENTIALITY, + 0, SECURITY_NATIVE_DREP, + chlg ? &chlg_desc : NULL, + 0, nego->context, + &resp_desc, &attrs, + &expiry); + + /* Free the decoded challenge as it is not required anymore */ + free(chlg); + + if(GSS_ERROR(nego->status)) { + char buffer[STRERROR_LEN]; + failf(data, "InitializeSecurityContext failed: %s", + Curl_sspi_strerror((int)nego->status, buffer, sizeof(buffer))); + + if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; + } + + if(nego->status == SEC_I_COMPLETE_NEEDED || + nego->status == SEC_I_COMPLETE_AND_CONTINUE) { + nego->status = (DWORD)s_pSecFn->CompleteAuthToken(nego->context, + &resp_desc); + if(GSS_ERROR(nego->status)) { + char buffer[STRERROR_LEN]; + failf(data, "CompleteAuthToken failed: %s", + Curl_sspi_strerror((int)nego->status, buffer, sizeof(buffer))); + + if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; + } + } + + nego->output_token_length = resp_buf.cbBuffer; + + return result; +} + +/* + * Curl_auth_create_spnego_message() + * + * This is used to generate an already encoded SPNEGO (Negotiate) response + * message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * nego [in/out] - The Negotiate data struct being used and modified. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, + char **outptr, size_t *outlen) +{ + /* Base64 encode the already generated response */ + CURLcode result = Curl_base64_encode((const char *) nego->output_token, + nego->output_token_length, outptr, + outlen); + if(!result && (!*outptr || !*outlen)) { + free(*outptr); + result = CURLE_REMOTE_ACCESS_DENIED; + } + + return result; +} + +/* + * Curl_auth_cleanup_spnego() + * + * This is used to clean up the SPNEGO (Negotiate) specific data. + * + * Parameters: + * + * nego [in/out] - The Negotiate data struct being cleaned up. + * + */ +void Curl_auth_cleanup_spnego(struct negotiatedata *nego) +{ + /* Free our security context */ + if(nego->context) { + s_pSecFn->DeleteSecurityContext(nego->context); + free(nego->context); + nego->context = NULL; + } + + /* Free our credentials handle */ + if(nego->credentials) { + s_pSecFn->FreeCredentialsHandle(nego->credentials); + free(nego->credentials); + nego->credentials = NULL; + } + + /* Free our identity */ + Curl_sspi_free_identity(nego->p_identity); + nego->p_identity = NULL; + + /* Free the SPN and output token */ + Curl_safefree(nego->spn); + Curl_safefree(nego->output_token); + + /* Reset any variables */ + nego->status = 0; + nego->token_max = 0; + nego->noauthpersist = FALSE; + nego->havenoauthpersist = FALSE; + nego->havenegdata = FALSE; + nego->havemultiplerequests = FALSE; +} + +#endif /* USE_WINDOWS_SSPI && USE_SPNEGO */ diff --git a/third_party/curl/lib/vauth/vauth.c b/third_party/curl/lib/vauth/vauth.c new file mode 100644 index 0000000000..ace43c47d1 --- /dev/null +++ b/third_party/curl/lib/vauth/vauth.c @@ -0,0 +1,163 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * + * 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 + +#include "vauth.h" +#include "urldata.h" +#include "strcase.h" +#include "curl_multibyte.h" +#include "curl_printf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_auth_build_spn() + * + * This is used to build a SPN string in the following formats: + * + * service/host@realm (Not currently used) + * service/host (Not used by GSS-API) + * service@realm (Not used by Windows SSPI) + * + * Parameters: + * + * service [in] - The service type such as http, smtp, pop or imap. + * host [in] - The hostname. + * realm [in] - The realm. + * + * Returns a pointer to the newly allocated SPN. + */ +#if !defined(USE_WINDOWS_SSPI) +char *Curl_auth_build_spn(const char *service, const char *host, + const char *realm) +{ + char *spn = NULL; + + /* Generate our SPN */ + if(host && realm) + spn = aprintf("%s/%s@%s", service, host, realm); + else if(host) + spn = aprintf("%s/%s", service, host); + else if(realm) + spn = aprintf("%s@%s", service, realm); + + /* Return our newly allocated SPN */ + return spn; +} +#else +TCHAR *Curl_auth_build_spn(const char *service, const char *host, + const char *realm) +{ + char *utf8_spn = NULL; + TCHAR *tchar_spn = NULL; + TCHAR *dupe_tchar_spn = NULL; + + (void) realm; + + /* Note: We could use DsMakeSPN() or DsClientMakeSpnForTargetServer() rather + than doing this ourselves but the first is only available in Windows XP + and Windows Server 2003 and the latter is only available in Windows 2000 + but not Windows95/98/ME or Windows NT4.0 unless the Active Directory + Client Extensions are installed. As such it is far simpler for us to + formulate the SPN instead. */ + + /* Generate our UTF8 based SPN */ + utf8_spn = aprintf("%s/%s", service, host); + if(!utf8_spn) + return NULL; + + /* Allocate and return a TCHAR based SPN. Since curlx_convert_UTF8_to_tchar + must be freed by curlx_unicodefree we will dupe the result so that the + pointer this function returns can be normally free'd. */ + tchar_spn = curlx_convert_UTF8_to_tchar(utf8_spn); + free(utf8_spn); + if(!tchar_spn) + return NULL; + dupe_tchar_spn = _tcsdup(tchar_spn); + curlx_unicodefree(tchar_spn); + return dupe_tchar_spn; +} +#endif /* USE_WINDOWS_SSPI */ + +/* + * Curl_auth_user_contains_domain() + * + * This is used to test if the specified user contains a Windows domain name as + * follows: + * + * Domain\User (Down-level Logon Name) + * Domain/User (curl Down-level format - for compatibility with existing code) + * User@Domain (User Principal Name) + * + * Note: The username may be empty when using a GSS-API library or Windows + * SSPI as the user and domain are either obtained from the credentials cache + * when using GSS-API or via the currently logged in user's credentials when + * using Windows SSPI. + * + * Parameters: + * + * user [in] - The username. + * + * Returns TRUE on success; otherwise FALSE. + */ +bool Curl_auth_user_contains_domain(const char *user) +{ + bool valid = FALSE; + + if(user && *user) { + /* Check we have a domain name or UPN present */ + char *p = strpbrk(user, "\\/@"); + + valid = (p != NULL && p > user && p < user + strlen(user) - 1 ? TRUE : + FALSE); + } +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + else + /* User and domain are obtained from the GSS-API credentials cache or the + currently logged in user from Windows */ + valid = TRUE; +#endif + + return valid; +} + +/* + * Curl_auth_ollowed_to_host() tells if authentication, cookies or other + * "sensitive data" can (still) be sent to this host. + */ +bool Curl_auth_allowed_to_host(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + return (!data->state.this_is_a_follow || + data->set.allow_auth_to_other_hosts || + (data->state.first_host && + strcasecompare(data->state.first_host, conn->host.name) && + (data->state.first_remote_port == conn->remote_port) && + (data->state.first_remote_protocol == conn->handler->protocol))); +} diff --git a/third_party/curl/lib/vauth/vauth.h b/third_party/curl/lib/vauth/vauth.h new file mode 100644 index 0000000000..7e823484f6 --- /dev/null +++ b/third_party/curl/lib/vauth/vauth.h @@ -0,0 +1,236 @@ +#ifndef HEADER_CURL_VAUTH_H +#define HEADER_CURL_VAUTH_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * + * 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 + +#include "bufref.h" + +struct Curl_easy; + +#if !defined(CURL_DISABLE_DIGEST_AUTH) +struct digestdata; +#endif + +#if defined(USE_NTLM) +struct ntlmdata; +#endif + +#if defined(USE_KERBEROS5) +struct kerberos5data; +#endif + +#if (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) && defined(USE_SPNEGO) +struct negotiatedata; +#endif + +#if defined(USE_GSASL) +struct gsasldata; +#endif + +#if defined(USE_WINDOWS_SSPI) +#define GSS_ERROR(status) ((status) & 0x80000000) +#endif + +/* + * Curl_auth_allowed_to_host() tells if authentication, cookies or other + * "sensitive data" can (still) be sent to this host. + */ +bool Curl_auth_allowed_to_host(struct Curl_easy *data); + +/* This is used to build a SPN string */ +#if !defined(USE_WINDOWS_SSPI) +char *Curl_auth_build_spn(const char *service, const char *host, + const char *realm); +#else +TCHAR *Curl_auth_build_spn(const char *service, const char *host, + const char *realm); +#endif + +/* This is used to test if the user contains a Windows domain name */ +bool Curl_auth_user_contains_domain(const char *user); + +/* This is used to generate a PLAIN cleartext message */ +CURLcode Curl_auth_create_plain_message(const char *authzid, + const char *authcid, + const char *passwd, + struct bufref *out); + +/* This is used to generate a LOGIN cleartext message */ +void Curl_auth_create_login_message(const char *value, struct bufref *out); + +/* This is used to generate an EXTERNAL cleartext message */ +void Curl_auth_create_external_message(const char *user, struct bufref *out); + +#ifndef CURL_DISABLE_DIGEST_AUTH +/* This is used to generate a CRAM-MD5 response message */ +CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, + const char *userp, + const char *passwdp, + struct bufref *out); + +/* This is used to evaluate if DIGEST is supported */ +bool Curl_auth_is_digest_supported(void); + +/* This is used to generate a base64 encoded DIGEST-MD5 response message */ +CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, + const struct bufref *chlg, + const char *userp, + const char *passwdp, + const char *service, + struct bufref *out); + +/* This is used to decode an HTTP DIGEST challenge message */ +CURLcode Curl_auth_decode_digest_http_message(const char *chlg, + struct digestdata *digest); + +/* This is used to generate an HTTP DIGEST response message */ +CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uri, + struct digestdata *digest, + char **outptr, size_t *outlen); + +/* This is used to clean up the digest specific data */ +void Curl_auth_digest_cleanup(struct digestdata *digest); +#endif /* !CURL_DISABLE_DIGEST_AUTH */ + +#ifdef USE_GSASL +/* This is used to evaluate if MECH is supported by gsasl */ +bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, + const char *mech, + struct gsasldata *gsasl); +/* This is used to start a gsasl method */ +CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, + const char *userp, + const char *passwdp, + struct gsasldata *gsasl); + +/* This is used to process and generate a new SASL token */ +CURLcode Curl_auth_gsasl_token(struct Curl_easy *data, + const struct bufref *chlg, + struct gsasldata *gsasl, + struct bufref *out); + +/* This is used to clean up the gsasl specific data */ +void Curl_auth_gsasl_cleanup(struct gsasldata *digest); +#endif + +#if defined(USE_NTLM) +/* This is used to evaluate if NTLM is supported */ +bool Curl_auth_is_ntlm_supported(void); + +/* This is used to generate a base64 encoded NTLM type-1 message */ +CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + const char *service, + const char *host, + struct ntlmdata *ntlm, + struct bufref *out); + +/* This is used to decode a base64 encoded NTLM type-2 message */ +CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, + const struct bufref *type2, + struct ntlmdata *ntlm); + +/* This is used to generate a base64 encoded NTLM type-3 message */ +CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + struct ntlmdata *ntlm, + struct bufref *out); + +/* This is used to clean up the NTLM specific data */ +void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm); +#endif /* USE_NTLM */ + +/* This is used to generate a base64 encoded OAuth 2.0 message */ +CURLcode Curl_auth_create_oauth_bearer_message(const char *user, + const char *host, + const long port, + const char *bearer, + struct bufref *out); + +/* This is used to generate a base64 encoded XOAuth 2.0 message */ +CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, + const char *bearer, + struct bufref *out); + +#if defined(USE_KERBEROS5) +/* This is used to evaluate if GSSAPI (Kerberos V5) is supported */ +bool Curl_auth_is_gssapi_supported(void); + +/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token + message */ +CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + const char *service, + const char *host, + const bool mutual, + const struct bufref *chlg, + struct kerberos5data *krb5, + struct bufref *out); + +/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) security + token message */ +CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, + const char *authzid, + const struct bufref *chlg, + struct kerberos5data *krb5, + struct bufref *out); + +/* This is used to clean up the GSSAPI specific data */ +void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5); +#endif /* USE_KERBEROS5 */ + +#if defined(USE_SPNEGO) +/* This is used to evaluate if SPNEGO (Negotiate) is supported */ +bool Curl_auth_is_spnego_supported(void); + +/* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge + message */ +CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, + const char *user, + const char *password, + const char *service, + const char *host, + const char *chlg64, + struct negotiatedata *nego); + +/* This is used to generate a base64 encoded SPNEGO (Negotiate) response + message */ +CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, + char **outptr, size_t *outlen); + +/* This is used to clean up the SPNEGO specific data */ +void Curl_auth_cleanup_spnego(struct negotiatedata *nego); + +#endif /* USE_SPNEGO */ + +#endif /* HEADER_CURL_VAUTH_H */ diff --git a/third_party/curl/lib/version.c b/third_party/curl/lib/version.c new file mode 100644 index 0000000000..c0d822dc8e --- /dev/null +++ b/third_party/curl/lib/version.c @@ -0,0 +1,691 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_NGHTTP2 +#include +#endif + +#include +#include "urldata.h" +#include "vtls/vtls.h" +#include "http2.h" +#include "vssh/ssh.h" +#include "vquic/vquic.h" +#include "curl_printf.h" +#include "easy_lock.h" + +#ifdef USE_ARES +# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ + defined(_WIN32) +# define CARES_STATICLIB +# endif +# include +#endif + +#ifdef USE_LIBIDN2 +#include +#endif + +#ifdef USE_LIBPSL +#include +#endif + +#ifdef USE_LIBRTMP +#include +#include "curl_rtmp.h" +#endif + +#ifdef HAVE_LIBZ +#include +#endif + +#ifdef HAVE_BROTLI +#if defined(__GNUC__) +/* Ignore -Wvla warnings in brotli headers */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wvla" +#endif +#include +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +#endif + +#ifdef HAVE_ZSTD +#include +#endif + +#ifdef USE_GSASL +#include +#endif + +#ifdef USE_OPENLDAP +#include +#endif + +#ifdef HAVE_BROTLI +static void brotli_version(char *buf, size_t bufsz) +{ + uint32_t brotli_version = BrotliDecoderVersion(); + unsigned int major = brotli_version >> 24; + unsigned int minor = (brotli_version & 0x00FFFFFF) >> 12; + unsigned int patch = brotli_version & 0x00000FFF; + (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch); +} +#endif + +#ifdef HAVE_ZSTD +static void zstd_version(char *buf, size_t bufsz) +{ + unsigned long zstd_version = (unsigned long)ZSTD_versionNumber(); + unsigned int major = (unsigned int)(zstd_version / (100 * 100)); + unsigned int minor = (unsigned int)((zstd_version - + (major * 100 * 100)) / 100); + unsigned int patch = (unsigned int)(zstd_version - + (major * 100 * 100) - (minor * 100)); + (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch); +} +#endif + +/* + * curl_version() returns a pointer to a static buffer. + * + * It is implemented to work multi-threaded by making sure repeated invokes + * generate the exact same string and never write any temporary data like + * zeros in the data. + */ + +#define VERSION_PARTS 16 /* number of substrings we can concatenate */ + +char *curl_version(void) +{ + static char out[300]; + char *outp; + size_t outlen; + const char *src[VERSION_PARTS]; +#ifdef USE_SSL + char ssl_version[200]; +#endif +#ifdef HAVE_LIBZ + char z_version[40]; +#endif +#ifdef HAVE_BROTLI + char br_version[40] = "brotli/"; +#endif +#ifdef HAVE_ZSTD + char zst_version[40] = "zstd/"; +#endif +#ifdef USE_ARES + char cares_version[40]; +#endif +#if defined(USE_LIBIDN2) + char idn_version[40]; +#endif +#ifdef USE_LIBPSL + char psl_version[40]; +#endif +#ifdef USE_SSH + char ssh_version[40]; +#endif +#ifdef USE_NGHTTP2 + char h2_version[40]; +#endif +#ifdef USE_HTTP3 + char h3_version[40]; +#endif +#ifdef USE_LIBRTMP + char rtmp_version[40]; +#endif +#ifdef USE_HYPER + char hyper_buf[30]; +#endif +#ifdef USE_GSASL + char gsasl_buf[30]; +#endif +#ifdef USE_OPENLDAP + char ldap_buf[30]; +#endif + int i = 0; + int j; + +#ifdef DEBUGBUILD + /* Override version string when environment variable CURL_VERSION is set */ + const char *debugversion = getenv("CURL_VERSION"); + if(debugversion) { + msnprintf(out, sizeof(out), "%s", debugversion); + return out; + } +#endif + + src[i++] = LIBCURL_NAME "/" LIBCURL_VERSION; +#ifdef USE_SSL + Curl_ssl_version(ssl_version, sizeof(ssl_version)); + src[i++] = ssl_version; +#endif +#ifdef HAVE_LIBZ + msnprintf(z_version, sizeof(z_version), "zlib/%s", zlibVersion()); + src[i++] = z_version; +#endif +#ifdef HAVE_BROTLI + brotli_version(&br_version[7], sizeof(br_version) - 7); + src[i++] = br_version; +#endif +#ifdef HAVE_ZSTD + zstd_version(&zst_version[5], sizeof(zst_version) - 5); + src[i++] = zst_version; +#endif +#ifdef USE_ARES + msnprintf(cares_version, sizeof(cares_version), + "c-ares/%s", ares_version(NULL)); + src[i++] = cares_version; +#endif +#ifdef USE_LIBIDN2 + msnprintf(idn_version, sizeof(idn_version), + "libidn2/%s", idn2_check_version(NULL)); + src[i++] = idn_version; +#elif defined(USE_WIN32_IDN) + src[i++] = (char *)"WinIDN"; +#elif defined(USE_APPLE_IDN) + src[i++] = (char *)"AppleIDN"; +#endif + +#ifdef USE_LIBPSL + { +#if defined(PSL_VERSION_MAJOR) && (PSL_VERSION_MAJOR > 0 || \ + PSL_VERSION_MINOR >= 11) + int num = psl_check_version_number(0); + msnprintf(psl_version, sizeof(psl_version), "libpsl/%d.%d.%d", + num >> 16, (num >> 8) & 0xff, num & 0xff); +#else + msnprintf(psl_version, sizeof(psl_version), "libpsl/%s", + psl_get_version()); +#endif + src[i++] = psl_version; + } +#endif + +#ifdef USE_SSH + Curl_ssh_version(ssh_version, sizeof(ssh_version)); + src[i++] = ssh_version; +#endif +#ifdef USE_NGHTTP2 + Curl_http2_ver(h2_version, sizeof(h2_version)); + src[i++] = h2_version; +#endif +#ifdef USE_HTTP3 + Curl_quic_ver(h3_version, sizeof(h3_version)); + src[i++] = h3_version; +#endif +#ifdef USE_LIBRTMP + Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); + src[i++] = rtmp_version; +#endif +#ifdef USE_HYPER + msnprintf(hyper_buf, sizeof(hyper_buf), "Hyper/%s", hyper_version()); + src[i++] = hyper_buf; +#endif +#ifdef USE_GSASL + msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s", + gsasl_check_version(NULL)); + src[i++] = gsasl_buf; +#endif +#ifdef USE_OPENLDAP + { + LDAPAPIInfo api; + api.ldapai_info_version = LDAP_API_INFO_VERSION; + + if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == LDAP_OPT_SUCCESS) { + unsigned int patch = (unsigned int)(api.ldapai_vendor_version % 100); + unsigned int major = (unsigned int)(api.ldapai_vendor_version / 10000); + unsigned int minor = + (((unsigned int)api.ldapai_vendor_version - major * 10000) + - patch) / 100; + msnprintf(ldap_buf, sizeof(ldap_buf), "%s/%u.%u.%u", + api.ldapai_vendor_name, major, minor, patch); + src[i++] = ldap_buf; + ldap_memfree(api.ldapai_vendor_name); + ber_memvfree((void **)api.ldapai_extensions); + } + } +#endif + + DEBUGASSERT(i <= VERSION_PARTS); + + outp = &out[0]; + outlen = sizeof(out); + for(j = 0; j < i; j++) { + size_t n = strlen(src[j]); + /* we need room for a space, the string and the final zero */ + if(outlen <= (n + 2)) + break; + if(j) { + /* prepend a space if not the first */ + *outp++ = ' '; + outlen--; + } + memcpy(outp, src[j], n); + outp += n; + outlen -= n; + } + *outp = 0; + + return out; +} + +/* data for curl_version_info + + Keep the list sorted alphabetically. It is also written so that each + protocol line has its own #if line to make things easier on the eye. + */ + +static const char * const supported_protocols[] = { +#ifndef CURL_DISABLE_DICT + "dict", +#endif +#ifndef CURL_DISABLE_FILE + "file", +#endif +#ifndef CURL_DISABLE_FTP + "ftp", +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) + "ftps", +#endif +#ifndef CURL_DISABLE_GOPHER + "gopher", +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER) + "gophers", +#endif +#ifndef CURL_DISABLE_HTTP + "http", +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) + "https", +#endif +#ifndef CURL_DISABLE_IMAP + "imap", +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP) + "imaps", +#endif +#ifndef CURL_DISABLE_LDAP + "ldap", +#if !defined(CURL_DISABLE_LDAPS) && \ + ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ + (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) + "ldaps", +#endif +#endif +#ifndef CURL_DISABLE_MQTT + "mqtt", +#endif +#ifndef CURL_DISABLE_POP3 + "pop3", +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) + "pop3s", +#endif +#ifdef USE_LIBRTMP + "rtmp", + "rtmpe", + "rtmps", + "rtmpt", + "rtmpte", + "rtmpts", +#endif +#ifndef CURL_DISABLE_RTSP + "rtsp", +#endif +#if defined(USE_SSH) && !defined(USE_WOLFSSH) + "scp", +#endif +#ifdef USE_SSH + "sftp", +#endif +#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) + "smb", +# ifdef USE_SSL + "smbs", +# endif +#endif +#ifndef CURL_DISABLE_SMTP + "smtp", +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP) + "smtps", +#endif +#ifndef CURL_DISABLE_TELNET + "telnet", +#endif +#ifndef CURL_DISABLE_TFTP + "tftp", +#endif +#ifdef USE_WEBSOCKETS + "ws", +#endif +#if defined(USE_SSL) && defined(USE_WEBSOCKETS) + "wss", +#endif + + NULL +}; + +/* + * Feature presence runtime check functions. + * + * Warning: the value returned by these should not change between + * curl_global_init() and curl_global_cleanup() calls. + */ + +#if defined(USE_LIBIDN2) +static int idn_present(curl_version_info_data *info) +{ + return info->libidn != NULL; +} +#else +#define idn_present NULL +#endif + +#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \ + !defined(CURL_DISABLE_HTTP) +static int https_proxy_present(curl_version_info_data *info) +{ + (void) info; + return Curl_ssl_supports(NULL, SSLSUPP_HTTPS_PROXY); +} +#endif + +#if defined(USE_SSL) && defined(USE_ECH) +static int ech_present(curl_version_info_data *info) +{ + (void) info; + return Curl_ssl_supports(NULL, SSLSUPP_ECH); +} +#endif + +/* + * Features table. + * + * Keep the features alphabetically sorted. + * Use FEATURE() macro to define an entry: this allows documentation check. + */ + +#define FEATURE(name, present, bitmask) {(name), (present), (bitmask)} + +struct feat { + const char *name; + int (*present)(curl_version_info_data *info); + int bitmask; +}; + +static const struct feat features_table[] = { +#ifndef CURL_DISABLE_ALTSVC + FEATURE("alt-svc", NULL, CURL_VERSION_ALTSVC), +#endif +#ifdef CURLRES_ASYNCH + FEATURE("AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS), +#endif +#ifdef HAVE_BROTLI + FEATURE("brotli", NULL, CURL_VERSION_BROTLI), +#endif +#ifdef DEBUGBUILD + FEATURE("Debug", NULL, CURL_VERSION_DEBUG), +#endif +#if defined(USE_SSL) && defined(USE_ECH) + FEATURE("ECH", ech_present, 0), +#endif +#ifdef USE_GSASL + FEATURE("gsasl", NULL, CURL_VERSION_GSASL), +#endif +#ifdef HAVE_GSSAPI + FEATURE("GSS-API", NULL, CURL_VERSION_GSSAPI), +#endif +#ifndef CURL_DISABLE_HSTS + FEATURE("HSTS", NULL, CURL_VERSION_HSTS), +#endif +#if defined(USE_NGHTTP2) + FEATURE("HTTP2", NULL, CURL_VERSION_HTTP2), +#endif +#if defined(USE_HTTP3) + FEATURE("HTTP3", NULL, CURL_VERSION_HTTP3), +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \ + !defined(CURL_DISABLE_HTTP) + FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY), +#endif +#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) + FEATURE("IDN", idn_present, CURL_VERSION_IDN), +#endif +#ifdef USE_IPV6 + FEATURE("IPv6", NULL, CURL_VERSION_IPV6), +#endif +#ifdef USE_KERBEROS5 + FEATURE("Kerberos", NULL, CURL_VERSION_KERBEROS5), +#endif +#if (SIZEOF_CURL_OFF_T > 4) && \ + ( (SIZEOF_OFF_T > 4) || defined(USE_WIN32_LARGE_FILES) ) + FEATURE("Largefile", NULL, CURL_VERSION_LARGEFILE), +#endif +#ifdef HAVE_LIBZ + FEATURE("libz", NULL, CURL_VERSION_LIBZ), +#endif +#ifdef CURL_WITH_MULTI_SSL + FEATURE("MultiSSL", NULL, CURL_VERSION_MULTI_SSL), +#endif +#ifdef USE_NTLM + FEATURE("NTLM", NULL, CURL_VERSION_NTLM), +#endif +#if defined(USE_LIBPSL) + FEATURE("PSL", NULL, CURL_VERSION_PSL), +#endif +#ifdef USE_SPNEGO + FEATURE("SPNEGO", NULL, CURL_VERSION_SPNEGO), +#endif +#ifdef USE_SSL + FEATURE("SSL", NULL, CURL_VERSION_SSL), +#endif +#ifdef USE_WINDOWS_SSPI + FEATURE("SSPI", NULL, CURL_VERSION_SSPI), +#endif +#ifdef GLOBAL_INIT_IS_THREADSAFE + FEATURE("threadsafe", NULL, CURL_VERSION_THREADSAFE), +#endif +#ifdef USE_TLS_SRP + FEATURE("TLS-SRP", NULL, CURL_VERSION_TLSAUTH_SRP), +#endif +#ifdef CURLDEBUG + FEATURE("TrackMemory", NULL, CURL_VERSION_CURLDEBUG), +#endif +#if defined(_WIN32) && defined(UNICODE) && defined(_UNICODE) + FEATURE("Unicode", NULL, CURL_VERSION_UNICODE), +#endif +#ifdef USE_UNIX_SOCKETS + FEATURE("UnixSockets", NULL, CURL_VERSION_UNIX_SOCKETS), +#endif +#ifdef HAVE_ZSTD + FEATURE("zstd", NULL, CURL_VERSION_ZSTD), +#endif + {NULL, NULL, 0} +}; + +static const char *feature_names[sizeof(features_table) / + sizeof(features_table[0])] = {NULL}; + + +static curl_version_info_data version_info = { + CURLVERSION_NOW, + LIBCURL_VERSION, + LIBCURL_VERSION_NUM, + OS, /* as found by configure or set by hand at build-time */ + 0, /* features bitmask is built at runtime */ + NULL, /* ssl_version */ + 0, /* ssl_version_num, this is kept at zero */ + NULL, /* zlib_version */ + supported_protocols, + NULL, /* c-ares version */ + 0, /* c-ares version numerical */ + NULL, /* libidn version */ + 0, /* iconv version */ + NULL, /* ssh lib version */ + 0, /* brotli_ver_num */ + NULL, /* brotli version */ + 0, /* nghttp2 version number */ + NULL, /* nghttp2 version string */ + NULL, /* quic library string */ +#ifdef CURL_CA_BUNDLE + CURL_CA_BUNDLE, /* cainfo */ +#else + NULL, +#endif +#ifdef CURL_CA_PATH + CURL_CA_PATH, /* capath */ +#else + NULL, +#endif + 0, /* zstd_ver_num */ + NULL, /* zstd version */ + NULL, /* Hyper version */ + NULL, /* gsasl version */ + feature_names, + NULL /* rtmp version */ +}; + +curl_version_info_data *curl_version_info(CURLversion stamp) +{ + size_t n; + const struct feat *p; + int features = 0; + +#if defined(USE_SSH) + static char ssh_buffer[80]; +#endif +#ifdef USE_SSL +#ifdef CURL_WITH_MULTI_SSL + static char ssl_buffer[200]; +#else + static char ssl_buffer[80]; +#endif +#endif +#ifdef HAVE_BROTLI + static char brotli_buffer[80]; +#endif +#ifdef HAVE_ZSTD + static char zstd_buffer[80]; +#endif + + (void)stamp; /* avoid compiler warnings, we do not use this */ + +#ifdef USE_SSL + Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); + version_info.ssl_version = ssl_buffer; +#endif + +#ifdef HAVE_LIBZ + version_info.libz_version = zlibVersion(); + /* libz left NULL if non-existing */ +#endif +#ifdef USE_ARES + { + int aresnum; + version_info.ares = ares_version(&aresnum); + version_info.ares_num = aresnum; + } +#endif +#ifdef USE_LIBIDN2 + /* This returns a version string if we use the given version or later, + otherwise it returns NULL */ + version_info.libidn = idn2_check_version(IDN2_VERSION); +#endif + +#if defined(USE_SSH) + Curl_ssh_version(ssh_buffer, sizeof(ssh_buffer)); + version_info.libssh_version = ssh_buffer; +#endif + +#ifdef HAVE_BROTLI + version_info.brotli_ver_num = BrotliDecoderVersion(); + brotli_version(brotli_buffer, sizeof(brotli_buffer)); + version_info.brotli_version = brotli_buffer; +#endif + +#ifdef HAVE_ZSTD + version_info.zstd_ver_num = (unsigned int)ZSTD_versionNumber(); + zstd_version(zstd_buffer, sizeof(zstd_buffer)); + version_info.zstd_version = zstd_buffer; +#endif + +#ifdef USE_NGHTTP2 + { + nghttp2_info *h2 = nghttp2_version(0); + version_info.nghttp2_ver_num = (unsigned int)h2->version_num; + version_info.nghttp2_version = h2->version_str; + } +#endif + +#ifdef USE_HTTP3 + { + static char quicbuffer[80]; + Curl_quic_ver(quicbuffer, sizeof(quicbuffer)); + version_info.quic_version = quicbuffer; + } +#endif + +#ifdef USE_HYPER + { + static char hyper_buffer[30]; + msnprintf(hyper_buffer, sizeof(hyper_buffer), "Hyper/%s", hyper_version()); + version_info.hyper_version = hyper_buffer; + } +#endif + +#ifdef USE_GSASL + { + version_info.gsasl_version = gsasl_check_version(NULL); + } +#endif + + /* Get available features, build bitmask and names array. */ + n = 0; + for(p = features_table; p->name; p++) + if(!p->present || p->present(&version_info)) { + features |= p->bitmask; + feature_names[n++] = p->name; + } + + feature_names[n] = NULL; /* Terminate array. */ + version_info.features = features; + +#ifdef USE_LIBRTMP + { + static char rtmp_version[30]; + Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); + version_info.rtmp_version = rtmp_version; + } +#endif + + return &version_info; +} diff --git a/third_party/curl/lib/version_win32.c b/third_party/curl/lib/version_win32.c new file mode 100644 index 0000000000..10fd0b1f16 --- /dev/null +++ b/third_party/curl/lib/version_win32.c @@ -0,0 +1,321 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(_WIN32) + +#include +#include "version_win32.h" +#include "warnless.h" + +/* The last 2 #include files should be in this order */ +#ifdef BUILDING_LIBCURL +#include "curl_memory.h" +#endif +#include "memdebug.h" + +/* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW) + and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */ +struct OUR_OSVERSIONINFOEXW { + ULONG dwOSVersionInfoSize; + ULONG dwMajorVersion; + ULONG dwMinorVersion; + ULONG dwBuildNumber; + ULONG dwPlatformId; + WCHAR szCSDVersion[128]; + USHORT wServicePackMajor; + USHORT wServicePackMinor; + USHORT wSuiteMask; + UCHAR wProductType; + UCHAR wReserved; +}; + +/* + * curlx_verify_windows_version() + * + * This is used to verify if we are running on a specific windows version. + * + * Parameters: + * + * majorVersion [in] - The major version number. + * minorVersion [in] - The minor version number. + * buildVersion [in] - The build version number. If 0, this parameter is + * ignored. + * platform [in] - The optional platform identifier. + * condition [in] - The test condition used to specifier whether we are + * checking a version less than, equal to or greater than + * what is specified in the major and minor version + * numbers. + * + * Returns TRUE if matched; otherwise FALSE. + */ +bool curlx_verify_windows_version(const unsigned int majorVersion, + const unsigned int minorVersion, + const unsigned int buildVersion, + const PlatformIdentifier platform, + const VersionCondition condition) +{ + bool matched = FALSE; + +#if defined(CURL_WINDOWS_APP) + /* We have no way to determine the Windows version from Windows apps, + so let's assume we are running on the target Windows version. */ + const WORD fullVersion = MAKEWORD(minorVersion, majorVersion); + const WORD targetVersion = (WORD)_WIN32_WINNT; + + (void)buildVersion; + + switch(condition) { + case VERSION_LESS_THAN: + matched = targetVersion < fullVersion; + break; + + case VERSION_LESS_THAN_EQUAL: + matched = targetVersion <= fullVersion; + break; + + case VERSION_EQUAL: + matched = targetVersion == fullVersion; + break; + + case VERSION_GREATER_THAN_EQUAL: + matched = targetVersion >= fullVersion; + break; + + case VERSION_GREATER_THAN: + matched = targetVersion > fullVersion; + break; + } + + if(matched && (platform == PLATFORM_WINDOWS)) { + /* we are always running on PLATFORM_WINNT */ + matched = FALSE; + } +#elif !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_WIN2K) || \ + (_WIN32_WINNT < _WIN32_WINNT_WIN2K) + OSVERSIONINFO osver; + + memset(&osver, 0, sizeof(osver)); + osver.dwOSVersionInfoSize = sizeof(osver); + + /* Find out Windows version */ + if(GetVersionEx(&osver)) { + /* Verify the Operating System version number */ + switch(condition) { + case VERSION_LESS_THAN: + if(osver.dwMajorVersion < majorVersion || + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion < minorVersion) || + (buildVersion != 0 && + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion == minorVersion && + osver.dwBuildNumber < buildVersion))) + matched = TRUE; + break; + + case VERSION_LESS_THAN_EQUAL: + if(osver.dwMajorVersion < majorVersion || + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion < minorVersion) || + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion == minorVersion && + (buildVersion == 0 || + osver.dwBuildNumber <= buildVersion))) + matched = TRUE; + break; + + case VERSION_EQUAL: + if(osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion == minorVersion && + (buildVersion == 0 || + osver.dwBuildNumber == buildVersion)) + matched = TRUE; + break; + + case VERSION_GREATER_THAN_EQUAL: + if(osver.dwMajorVersion > majorVersion || + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion > minorVersion) || + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion == minorVersion && + (buildVersion == 0 || + osver.dwBuildNumber >= buildVersion))) + matched = TRUE; + break; + + case VERSION_GREATER_THAN: + if(osver.dwMajorVersion > majorVersion || + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion > minorVersion) || + (buildVersion != 0 && + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion == minorVersion && + osver.dwBuildNumber > buildVersion))) + matched = TRUE; + break; + } + + /* Verify the platform identifier (if necessary) */ + if(matched) { + switch(platform) { + case PLATFORM_WINDOWS: + if(osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) + matched = FALSE; + break; + + case PLATFORM_WINNT: + if(osver.dwPlatformId != VER_PLATFORM_WIN32_NT) + matched = FALSE; + break; + + default: /* like platform == PLATFORM_DONT_CARE */ + break; + } + } + } +#else + ULONGLONG cm = 0; + struct OUR_OSVERSIONINFOEXW osver; + BYTE majorCondition; + BYTE minorCondition; + BYTE buildCondition; + BYTE spMajorCondition; + BYTE spMinorCondition; + DWORD dwTypeMask = VER_MAJORVERSION | VER_MINORVERSION | + VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; + + typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN) + (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG); + static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo; + static bool onetime = true; /* safe because first call is during init */ + + if(onetime) { + pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN, + (GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo"))); + onetime = false; + } + + switch(condition) { + case VERSION_LESS_THAN: + majorCondition = VER_LESS; + minorCondition = VER_LESS; + buildCondition = VER_LESS; + spMajorCondition = VER_LESS_EQUAL; + spMinorCondition = VER_LESS_EQUAL; + break; + + case VERSION_LESS_THAN_EQUAL: + majorCondition = VER_LESS_EQUAL; + minorCondition = VER_LESS_EQUAL; + buildCondition = VER_LESS_EQUAL; + spMajorCondition = VER_LESS_EQUAL; + spMinorCondition = VER_LESS_EQUAL; + break; + + case VERSION_EQUAL: + majorCondition = VER_EQUAL; + minorCondition = VER_EQUAL; + buildCondition = VER_EQUAL; + spMajorCondition = VER_GREATER_EQUAL; + spMinorCondition = VER_GREATER_EQUAL; + break; + + case VERSION_GREATER_THAN_EQUAL: + majorCondition = VER_GREATER_EQUAL; + minorCondition = VER_GREATER_EQUAL; + buildCondition = VER_GREATER_EQUAL; + spMajorCondition = VER_GREATER_EQUAL; + spMinorCondition = VER_GREATER_EQUAL; + break; + + case VERSION_GREATER_THAN: + majorCondition = VER_GREATER; + minorCondition = VER_GREATER; + buildCondition = VER_GREATER; + spMajorCondition = VER_GREATER_EQUAL; + spMinorCondition = VER_GREATER_EQUAL; + break; + + default: + return FALSE; + } + + memset(&osver, 0, sizeof(osver)); + osver.dwOSVersionInfoSize = sizeof(osver); + osver.dwMajorVersion = majorVersion; + osver.dwMinorVersion = minorVersion; + osver.dwBuildNumber = buildVersion; + if(platform == PLATFORM_WINDOWS) + osver.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS; + else if(platform == PLATFORM_WINNT) + osver.dwPlatformId = VER_PLATFORM_WIN32_NT; + + cm = VerSetConditionMask(cm, VER_MAJORVERSION, majorCondition); + cm = VerSetConditionMask(cm, VER_MINORVERSION, minorCondition); + cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, spMajorCondition); + cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, spMinorCondition); + + if(platform != PLATFORM_DONT_CARE) { + cm = VerSetConditionMask(cm, VER_PLATFORMID, VER_EQUAL); + dwTypeMask |= VER_PLATFORMID; + } + + /* Later versions of Windows have version functions that may not return the + real version of Windows unless the application is so manifested. We prefer + the real version always, so we use the Rtl variant of the function when + possible. Note though the function signatures have underlying fundamental + types that are the same, the return values are different. */ + if(pRtlVerifyVersionInfo) + matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); + else + matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, dwTypeMask, cm); + + /* Compare the build number separately. VerifyVersionInfo normally compares + major.minor in hierarchical order (eg 1.9 is less than 2.0) but does not + do the same for build (eg 1.9 build 222 is not less than 2.0 build 111). + Build comparison is only needed when build numbers are equal (eg 1.9 is + always less than 2.0 so build comparison is not needed). */ + if(matched && buildVersion && + (condition == VERSION_EQUAL || + ((condition == VERSION_GREATER_THAN_EQUAL || + condition == VERSION_LESS_THAN_EQUAL) && + curlx_verify_windows_version(majorVersion, minorVersion, 0, + platform, VERSION_EQUAL)))) { + + cm = VerSetConditionMask(0, VER_BUILDNUMBER, buildCondition); + dwTypeMask = VER_BUILDNUMBER; + if(pRtlVerifyVersionInfo) + matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); + else + matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, + dwTypeMask, cm); + } + +#endif + + return matched; +} + +#endif /* _WIN32 */ diff --git a/third_party/curl/lib/version_win32.h b/third_party/curl/lib/version_win32.h new file mode 100644 index 0000000000..95c066112c --- /dev/null +++ b/third_party/curl/lib/version_win32.h @@ -0,0 +1,56 @@ +#ifndef HEADER_CURL_VERSION_WIN32_H +#define HEADER_CURL_VERSION_WIN32_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Steve Holme, . + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(_WIN32) + +/* Version condition */ +typedef enum { + VERSION_LESS_THAN, + VERSION_LESS_THAN_EQUAL, + VERSION_EQUAL, + VERSION_GREATER_THAN_EQUAL, + VERSION_GREATER_THAN +} VersionCondition; + +/* Platform identifier */ +typedef enum { + PLATFORM_DONT_CARE, + PLATFORM_WINDOWS, + PLATFORM_WINNT +} PlatformIdentifier; + +/* This is used to verify if we are running on a specific windows version */ +bool curlx_verify_windows_version(const unsigned int majorVersion, + const unsigned int minorVersion, + const unsigned int buildVersion, + const PlatformIdentifier platform, + const VersionCondition condition); + +#endif /* _WIN32 */ + +#endif /* HEADER_CURL_VERSION_WIN32_H */ diff --git a/third_party/curl/lib/vquic/curl_msh3.c b/third_party/curl/lib/vquic/curl_msh3.c new file mode 100644 index 0000000000..b707a6ebe3 --- /dev/null +++ b/third_party/curl/lib/vquic/curl_msh3.c @@ -0,0 +1,1116 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_MSH3 + +#include "urldata.h" +#include "hash.h" +#include "timeval.h" +#include "multiif.h" +#include "sendf.h" +#include "curl_trc.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "connect.h" +#include "progress.h" +#include "http1.h" +#include "curl_msh3.h" +#include "socketpair.h" +#include "vtls/vtls.h" +#include "vquic/vquic.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#ifdef CURL_DISABLE_SOCKETPAIR +#error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set" +#endif + +#define H3_STREAM_WINDOW_SIZE (128 * 1024) +#define H3_STREAM_CHUNK_SIZE (16 * 1024) +#define H3_STREAM_RECV_CHUNKS \ + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) + +#ifdef _WIN32 +#define msh3_lock CRITICAL_SECTION +#define msh3_lock_initialize(lock) InitializeCriticalSection(lock) +#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock) +#define msh3_lock_acquire(lock) EnterCriticalSection(lock) +#define msh3_lock_release(lock) LeaveCriticalSection(lock) +#else /* !_WIN32 */ +#include +#define msh3_lock pthread_mutex_t +#define msh3_lock_initialize(lock) do { \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init(&attr); \ + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ + pthread_mutex_init(lock, &attr); \ + pthread_mutexattr_destroy(&attr); \ +}while(0) +#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock) +#define msh3_lock_acquire(lock) pthread_mutex_lock(lock) +#define msh3_lock_release(lock) pthread_mutex_unlock(lock) +#endif /* _WIN32 */ + + +static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, + void *IfContext); +static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, + void *IfContext); +static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, + void *IfContext, + MSH3_REQUEST *Request); +static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, + void *IfContext, + const MSH3_HEADER *Header); +static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, + void *IfContext, uint32_t *Length, + const uint8_t *Data); +static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, + bool Aborted, uint64_t AbortError); +static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, + void *IfContext); +static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, + void *IfContext, void *SendContext); + + +void Curl_msh3_ver(char *p, size_t len) +{ + uint32_t v[4]; + MsH3Version(v); + (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]); +} + +#define SP_LOCAL 0 +#define SP_REMOTE 1 + +struct cf_msh3_ctx { + MSH3_API *api; + MSH3_CONNECTION *qconn; + struct Curl_sockaddr_ex addr; + curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */ + char l_ip[MAX_IPADR_LEN]; /* local IP as string */ + int l_port; /* local port number */ + struct cf_call_data call_data; + struct curltime connect_started; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */ + /* Flags written by msh3/msquic thread */ + bool handshake_complete; + bool handshake_succeeded; + bool connected; + /* Flags written by curl thread */ + BIT(verbose); + BIT(active); +}; + +static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data); + +/* How to access `call_data` from a cf_msh3 filter */ +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) \ + ((struct cf_msh3_ctx *)(cf)->ctx)->call_data + +/** + * All about the H3 internals of a stream + */ +struct stream_ctx { + struct MSH3_REQUEST *req; + struct bufq recvbuf; /* h3 response */ +#ifdef _WIN32 + CRITICAL_SECTION recv_lock; +#else /* !_WIN32 */ + pthread_mutex_t recv_lock; +#endif /* _WIN32 */ + uint64_t error3; /* HTTP/3 stream error code */ + int status_code; /* HTTP status code */ + CURLcode recv_error; + bool closed; + bool reset; + bool upload_done; + bool firstheader; /* FALSE until headers arrive */ + bool recv_header_complete; +}; + +#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)((data && ctx)? \ + Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + +static void h3_stream_ctx_free(struct stream_ctx *stream) +{ + Curl_bufq_free(&stream->recvbuf); + free(stream); +} + +static void h3_stream_hash_free(void *stream) +{ + DEBUGASSERT(stream); + h3_stream_ctx_free((struct stream_ctx *)stream); +} + +static CURLcode h3_data_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + if(stream) + return CURLE_OK; + + stream = calloc(1, sizeof(*stream)); + if(!stream) + return CURLE_OUT_OF_MEMORY; + + stream->req = ZERO_NULL; + msh3_lock_initialize(&stream->recv_lock); + Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE, + H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); + CURL_TRC_CF(data, cf, "data setup"); + + if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + h3_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + (void)cf; + if(stream) { + CURL_TRC_CF(data, cf, "easy handle is done"); + Curl_hash_offt_remove(&ctx->streams, data->id); + } +} + +static void drain_stream_from_other_thread(struct Curl_easy *data, + struct stream_ctx *stream) +{ + unsigned char bits; + + /* risky */ + bits = CURL_CSELECT_IN; + if(stream && !stream->upload_done) + bits |= CURL_CSELECT_OUT; + if(data->state.select_bits != bits) { + data->state.select_bits = bits; + /* cannot expire from other thread */ + } +} + +static void drain_stream(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + unsigned char bits; + + (void)cf; + bits = CURL_CSELECT_IN; + if(stream && !stream->upload_done) + bits |= CURL_CSELECT_OUT; + if(data->state.select_bits != bits) { + data->state.select_bits = bits; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } +} + +static const MSH3_CONNECTION_IF msh3_conn_if = { + msh3_conn_connected, + msh3_conn_shutdown_complete, + msh3_conn_new_request +}; + +static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, + void *IfContext) +{ + struct Curl_cfilter *cf = IfContext; + struct cf_msh3_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + (void)Connection; + + CURL_TRC_CF(data, cf, "[MSH3] connected"); + ctx->handshake_succeeded = true; + ctx->connected = true; + ctx->handshake_complete = true; +} + +static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, + void *IfContext) +{ + struct Curl_cfilter *cf = IfContext; + struct cf_msh3_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)Connection; + CURL_TRC_CF(data, cf, "[MSH3] shutdown complete"); + ctx->connected = false; + ctx->handshake_complete = true; +} + +static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, + void *IfContext, + MSH3_REQUEST *Request) +{ + (void)Connection; + (void)IfContext; + (void)Request; +} + +static const MSH3_REQUEST_IF msh3_request_if = { + msh3_header_received, + msh3_data_received, + msh3_complete, + msh3_shutdown_complete, + msh3_data_sent +}; + +/* Decode HTTP status code. Returns -1 if no valid status code was + decoded. (duplicate from http2.c) */ +static int decode_status_code(const char *value, size_t len) +{ + int i; + int res; + + if(len != 3) { + return -1; + } + + res = 0; + + for(i = 0; i < 3; ++i) { + char c = value[i]; + + if(c < '0' || c > '9') { + return -1; + } + + res *= 10; + res += c - '0'; + } + + return res; +} + +/* + * write_resp_raw() copies response data in raw format to the `data`'s + * receive buffer. If not enough space is available, it appends to the + * `data`'s overflow buffer. + */ +static CURLcode write_resp_raw(struct Curl_easy *data, + const void *mem, size_t memlen) +{ + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result = CURLE_OK; + ssize_t nwritten; + + if(!stream) + return CURLE_RECV_ERROR; + + nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); + if(nwritten < 0) { + return result; + } + + if((size_t)nwritten < memlen) { + /* This MUST not happen. Our recbuf is dimensioned to hold the + * full max_stream_window and then some for this very reason. */ + DEBUGASSERT(0); + return CURLE_RECV_ERROR; + } + return result; +} + +static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, + void *userp, + const MSH3_HEADER *hd) +{ + struct Curl_easy *data = userp; + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result; + (void)Request; + + DEBUGF(infof(data, "[MSH3] header received, stream=%d", !!stream)); + if(!stream || stream->recv_header_complete) { + return; + } + + msh3_lock_acquire(&stream->recv_lock); + + if((hd->NameLength == 7) && + !strncmp(HTTP_PSEUDO_STATUS, (char *)hd->Name, 7)) { + char line[14]; /* status line is always 13 characters long */ + size_t ncopy; + + DEBUGASSERT(!stream->firstheader); + stream->status_code = decode_status_code(hd->Value, hd->ValueLength); + DEBUGASSERT(stream->status_code != -1); + ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", + stream->status_code); + result = write_resp_raw(data, line, ncopy); + if(result) + stream->recv_error = result; + stream->firstheader = TRUE; + } + else { + /* store as an HTTP1-style header */ + DEBUGASSERT(stream->firstheader); + result = write_resp_raw(data, hd->Name, hd->NameLength); + if(!result) + result = write_resp_raw(data, ": ", 2); + if(!result) + result = write_resp_raw(data, hd->Value, hd->ValueLength); + if(!result) + result = write_resp_raw(data, "\r\n", 2); + if(result) { + stream->recv_error = result; + } + } + + drain_stream_from_other_thread(data, stream); + msh3_lock_release(&stream->recv_lock); +} + +static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, + void *IfContext, uint32_t *buflen, + const uint8_t *buf) +{ + struct Curl_easy *data = IfContext; + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result; + bool rv = FALSE; + + /* TODO: we would like to limit the amount of data we are buffer here. + * There seems to be no mechanism in msh3 to adjust flow control and + * it is undocumented what happens if we return FALSE here or less + * length (buflen is an inout parameter). + */ + (void)Request; + if(!stream) + return FALSE; + + msh3_lock_acquire(&stream->recv_lock); + + if(!stream->recv_header_complete) { + result = write_resp_raw(data, "\r\n", 2); + if(result) { + stream->recv_error = result; + goto out; + } + stream->recv_header_complete = true; + } + + result = write_resp_raw(data, buf, *buflen); + if(result) { + stream->recv_error = result; + } + rv = TRUE; + +out: + msh3_lock_release(&stream->recv_lock); + return rv; +} + +static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, + bool aborted, uint64_t error) +{ + struct Curl_easy *data = IfContext; + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + (void)Request; + if(!stream) + return; + msh3_lock_acquire(&stream->recv_lock); + stream->closed = TRUE; + stream->recv_header_complete = true; + if(error) + stream->error3 = error; + if(aborted) + stream->reset = TRUE; + msh3_lock_release(&stream->recv_lock); +} + +static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, + void *IfContext) +{ + struct Curl_easy *data = IfContext; + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + if(!stream) + return; + (void)Request; + (void)stream; +} + +static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, + void *IfContext, void *SendContext) +{ + struct Curl_easy *data = IfContext; + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + if(!stream) + return; + (void)Request; + (void)stream; + (void)SendContext; +} + +static ssize_t recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + CURLcode *err) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + ssize_t nread = -1; + + if(!stream) { + *err = CURLE_RECV_ERROR; + return -1; + } + (void)cf; + if(stream->reset) { + failf(data, "HTTP/3 stream reset by server"); + *err = CURLE_PARTIAL_FILE; + CURL_TRC_CF(data, cf, "cf_recv, was reset -> %d", *err); + goto out; + } + else if(stream->error3) { + failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)", + (ssize_t)stream->error3); + *err = CURLE_HTTP3; + CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err); + goto out; + } + else { + CURL_TRC_CF(data, cf, "cf_recv, closed ok -> %d", *err); + } + *err = CURLE_OK; + nread = 0; + +out: + return nread; +} + +static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + /* we have no indication from msh3 when it would be a good time + * to juggle the connection again. So, we compromise by calling + * us again every some milliseconds. */ + (void)cf; + if(stream && stream->req && !stream->closed) { + Curl_expire(data, 10, EXPIRE_QUIC); + } + else { + Curl_expire(data, 50, EXPIRE_QUIC); + } +} + +static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + ssize_t nread = -1; + struct cf_call_data save; + + CURL_TRC_CF(data, cf, "cf_recv(len=%zu), stream=%d", len, !!stream); + if(!stream) { + *err = CURLE_RECV_ERROR; + return -1; + } + CF_DATA_SAVE(save, cf, data); + + msh3_lock_acquire(&stream->recv_lock); + + if(stream->recv_error) { + failf(data, "request aborted"); + *err = stream->recv_error; + goto out; + } + + *err = CURLE_OK; + + if(!Curl_bufq_is_empty(&stream->recvbuf)) { + nread = Curl_bufq_read(&stream->recvbuf, + (unsigned char *)buf, len, err); + CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d", + len, nread, *err); + if(nread < 0) + goto out; + if(stream->closed) + drain_stream(cf, data); + } + else if(stream->closed) { + nread = recv_closed_stream(cf, data, err); + goto out; + } + else { + CURL_TRC_CF(data, cf, "req: nothing here, call again"); + *err = CURLE_AGAIN; + } + +out: + msh3_lock_release(&stream->recv_lock); + set_quic_expire(cf, data); + CF_DATA_RESTORE(cf, save); + return nread; +} + +static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct h1_req_parser h1; + struct dynhds h2_headers; + MSH3_HEADER *nva = NULL; + size_t nheader, i; + ssize_t nwritten = -1; + struct cf_call_data save; + bool eos; + + CF_DATA_SAVE(save, cf, data); + + Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + + /* Sizes must match for cast below to work" */ + DEBUGASSERT(stream); + CURL_TRC_CF(data, cf, "req: send %zu bytes", len); + + if(!stream->req) { + /* The first send on the request contains the headers and possibly some + data. Parse out the headers and create the request, then if there is + any data left over go ahead and send it too. */ + nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); + if(nwritten < 0) + goto out; + DEBUGASSERT(h1.done); + DEBUGASSERT(h1.req); + + *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); + if(*err) { + nwritten = -1; + goto out; + } + + nheader = Curl_dynhds_count(&h2_headers); + nva = malloc(sizeof(MSH3_HEADER) * nheader); + if(!nva) { + *err = CURLE_OUT_OF_MEMORY; + nwritten = -1; + goto out; + } + + for(i = 0; i < nheader; ++i) { + struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); + nva[i].Name = e->name; + nva[i].NameLength = e->namelen; + nva[i].Value = e->value; + nva[i].ValueLength = e->valuelen; + } + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: + /* known request body size or -1 */ + eos = FALSE; + break; + default: + /* there is not request body */ + eos = TRUE; + stream->upload_done = TRUE; + break; + } + + CURL_TRC_CF(data, cf, "req: send %zu headers", nheader); + stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data, + nva, nheader, + eos ? MSH3_REQUEST_FLAG_FIN : + MSH3_REQUEST_FLAG_NONE); + if(!stream->req) { + failf(data, "request open failed"); + *err = CURLE_SEND_ERROR; + goto out; + } + *err = CURLE_OK; + nwritten = len; + goto out; + } + else { + /* request is open */ + CURL_TRC_CF(data, cf, "req: send %zu body bytes", len); + if(len > 0xFFFFFFFF) { + len = 0xFFFFFFFF; + } + + if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_NONE, buf, + (uint32_t)len, stream)) { + *err = CURLE_SEND_ERROR; + goto out; + } + + /* TODO - msh3/msquic will hold onto this memory until the send complete + event. How do we make sure curl does not free it until then? */ + *err = CURLE_OK; + nwritten = len; + } + +out: + set_quic_expire(cf, data); + free(nva); + Curl_h1_req_parse_free(&h1); + Curl_dynhds_free(&h2_headers); + CF_DATA_RESTORE(cf, save); + return nwritten; +} + +static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { + if(stream->recv_error) { + Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]); + drain_stream(cf, data); + } + else if(stream->req) { + Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]); + drain_stream(cf, data); + } + } +} + +static bool cf_msh3_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + bool pending = FALSE; + + CF_DATA_SAVE(save, cf, data); + + (void)cf; + if(stream && stream->req) { + msh3_lock_acquire(&stream->recv_lock); + CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu", + Curl_bufq_len(&stream->recvbuf)); + pending = !Curl_bufq_is_empty(&stream->recvbuf); + msh3_lock_release(&stream->recv_lock); + if(pending) + drain_stream(cf, (struct Curl_easy *)data); + } + + CF_DATA_RESTORE(cf, save); + return pending; +} + +static CURLcode h3_data_pause(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool pause) +{ + if(!pause) { + drain_stream(cf, data); + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + return CURLE_OK; +} + +static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_SETUP: + result = h3_data_setup(cf, data); + break; + case CF_CTRL_DATA_PAUSE: + result = h3_data_pause(cf, data, (arg1 != 0)); + break; + case CF_CTRL_DATA_DONE: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE_SEND: + CURL_TRC_CF(data, cf, "req: send done"); + if(stream) { + stream->upload_done = TRUE; + if(stream->req) { + char buf[1]; + if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, + buf, 0, data)) { + result = CURLE_SEND_ERROR; + } + } + } + break; + default: + break; + } + + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode cf_connect_start(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct ssl_primary_config *conn_config; + MSH3_ADDR addr = {0}; + CURLcode result; + bool verify; + + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; + verify = !!conn_config->verifypeer; + + memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen); + MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port); + + if(verify && (conn_config->CAfile || conn_config->CApath)) { + /* TODO: need a way to provide trust anchors to MSH3 */ +#ifdef DEBUGBUILD + /* we need this for our test cases to run */ + CURL_TRC_CF(data, cf, "non-standard CA not supported, " + "switching off verifypeer in DEBUG mode"); + verify = 0; +#else + CURL_TRC_CF(data, cf, "non-standard CA not supported, " + "attempting with built-in verification"); +#endif + } + + CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)", + cf->conn->host.name, (int)cf->conn->remote_port, verify); + + ctx->api = MsH3ApiOpen(); + if(!ctx->api) { + failf(data, "cannot create msh3 api"); + return CURLE_FAILED_INIT; + } + + ctx->qconn = MsH3ConnectionOpen(ctx->api, + &msh3_conn_if, + cf, + cf->conn->host.name, + &addr, + !verify); + if(!ctx->qconn) { + failf(data, "cannot create msh3 connection"); + if(ctx->api) { + MsH3ApiClose(ctx->api); + ctx->api = NULL; + } + return CURLE_FAILED_INIT; + } + + result = h3_data_setup(cf, data); + if(result) + return result; + + return CURLE_OK; +} + +static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct cf_call_data save; + CURLcode result = CURLE_OK; + + (void)blocking; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + + if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) { + if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0], FALSE) < 0) { + ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; + ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; + return CURLE_COULDNT_CONNECT; + } + } + + *done = FALSE; + if(!ctx->qconn) { + ctx->connect_started = Curl_now(); + result = cf_connect_start(cf, data); + if(result) + goto out; + } + + if(ctx->handshake_complete) { + ctx->handshake_at = Curl_now(); + if(ctx->handshake_succeeded) { + CURL_TRC_CF(data, cf, "handshake succeeded"); + cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + cf->conn->httpversion = 30; + cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; + cf->connected = TRUE; + cf->conn->alpn = CURL_HTTP_VERSION_3; + *done = TRUE; + connkeep(cf->conn, "HTTP/3 default"); + Curl_pgrsTime(data, TIMER_APPCONNECT); + } + else { + failf(data, "failed to connect, handshake failed"); + result = CURLE_COULDNT_CONNECT; + } + } + +out: + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct cf_call_data save; + + (void)data; + CF_DATA_SAVE(save, cf, data); + + if(ctx) { + CURL_TRC_CF(data, cf, "destroying"); + if(ctx->qconn) { + MsH3ConnectionClose(ctx->qconn); + ctx->qconn = NULL; + } + if(ctx->api) { + MsH3ApiClose(ctx->api); + ctx->api = NULL; + } + Curl_hash_destroy(&ctx->streams); + + if(ctx->active) { + /* We share our socket at cf->conn->sock[cf->sockindex] when active. + * If it is no longer there, someone has stolen (and hopefully + * closed it) and we just forget about it. + */ + ctx->active = FALSE; + if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) { + CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active", + (int)ctx->sock[SP_LOCAL]); + cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; + } + else { + CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at " + "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]); + ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; + } + if(cf->sockindex == FIRSTSOCKET) + cf->conn->remote_addr = NULL; + } + if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { + sclose(ctx->sock[SP_LOCAL]); + } + if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) { + sclose(ctx->sock[SP_REMOTE]); + } + ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; + ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; + } + CF_DATA_RESTORE(cf, save); +} + +static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + cf_msh3_close(cf, data); + free(cf->ctx); + cf->ctx = NULL; + /* no CF_DATA_RESTORE(cf, save); its gone */ + +} + +static CURLcode cf_msh3_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: { + /* TODO: we do not have access to this so far, fake it */ + (void)ctx; + *pres1 = 100; + return CURLE_OK; + } + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + /* we do not know when the first byte arrived */ + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + + (void)data; + *input_pending = FALSE; + return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn && + ctx->connected; +} + +struct Curl_cftype Curl_cft_http3 = { + "HTTP/3", + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + 0, + cf_msh3_destroy, + cf_msh3_connect, + cf_msh3_close, + Curl_cf_def_shutdown, + Curl_cf_def_get_host, + cf_msh3_adjust_pollset, + cf_msh3_data_pending, + cf_msh3_send, + cf_msh3_recv, + cf_msh3_data_event, + cf_msh3_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_msh3_query, +}; + +static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data) +{ + if(data && data->conn) { + struct Curl_cfilter *cf = data->conn->cfilter[FIRSTSOCKET]; + while(cf) { + if(cf->cft == &Curl_cft_http3) + return cf->ctx; + cf = cf->next; + } + } + DEBUGF(infof(data, "no filter context found")); + return NULL; +} + +CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai) +{ + struct cf_msh3_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + (void)data; + (void)conn; + (void)ai; /* TODO: msh3 resolves itself? */ + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC); + ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; + ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; + + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + +out: + *pcf = (!result)? cf : NULL; + if(result) { + Curl_safefree(cf); + Curl_safefree(ctx); + } + + return result; +} + +bool Curl_conn_is_msh3(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; + + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_http3) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + } + return FALSE; +} + +#endif /* USE_MSH3 */ diff --git a/third_party/curl/lib/vquic/curl_msh3.h b/third_party/curl/lib/vquic/curl_msh3.h new file mode 100644 index 0000000000..33931f59bb --- /dev/null +++ b/third_party/curl/lib/vquic/curl_msh3.h @@ -0,0 +1,46 @@ +#ifndef HEADER_CURL_VQUIC_CURL_MSH3_H +#define HEADER_CURL_VQUIC_CURL_MSH3_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_MSH3 + +#include + +void Curl_msh3_ver(char *p, size_t len); + +CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai); + +bool Curl_conn_is_msh3(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); + +#endif /* USE_MSQUIC */ + +#endif /* HEADER_CURL_VQUIC_CURL_MSH3_H */ diff --git a/third_party/curl/lib/vquic/curl_ngtcp2.c b/third_party/curl/lib/vquic/curl_ngtcp2.c new file mode 100644 index 0000000000..1f67f2362e --- /dev/null +++ b/third_party/curl/lib/vquic/curl_ngtcp2.c @@ -0,0 +1,2555 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) +#include +#include + +#ifdef USE_OPENSSL +#include +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#include +#else +#include +#endif +#include "vtls/openssl.h" +#elif defined(USE_GNUTLS) +#include +#include "vtls/gtls.h" +#elif defined(USE_WOLFSSL) +#include +#endif + +#include "urldata.h" +#include "hash.h" +#include "sendf.h" +#include "strdup.h" +#include "rand.h" +#include "multiif.h" +#include "strcase.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "connect.h" +#include "progress.h" +#include "strerror.h" +#include "dynbuf.h" +#include "http1.h" +#include "select.h" +#include "inet_pton.h" +#include "transfer.h" +#include "vquic.h" +#include "vquic_int.h" +#include "vquic-tls.h" +#include "vtls/keylog.h" +#include "vtls/vtls.h" +#include "curl_ngtcp2.h" + +#include "warnless.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +#define QUIC_MAX_STREAMS (256*1024) +#define QUIC_MAX_DATA (1*1024*1024) +#define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS) + +/* A stream window is the maximum amount we need to buffer for + * each active transfer. We use HTTP/3 flow control and only ACK + * when we take things out of the buffer. + * Chunk size is large enough to take a full DATA frame */ +#define H3_STREAM_WINDOW_SIZE (128 * 1024) +#define H3_STREAM_CHUNK_SIZE (16 * 1024) +/* The pool keeps spares around and half of a full stream windows + * seems good. More does not seem to improve performance. + * The benefit of the pool is that stream buffer to not keep + * spares. Memory consumption goes down when streams run empty, + * have a large upload done, etc. */ +#define H3_STREAM_POOL_SPARES \ + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 +/* Receive and Send max number of chunks just follows from the + * chunk size and window size */ +#define H3_STREAM_RECV_CHUNKS \ + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) +#define H3_STREAM_SEND_CHUNKS \ + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) + + +/* + * Store ngtcp2 version info in this buffer. + */ +void Curl_ngtcp2_ver(char *p, size_t len) +{ + const ngtcp2_info *ng2 = ngtcp2_version(0); + const nghttp3_info *ht3 = nghttp3_version(0); + (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s", + ng2->version_str, ht3->version_str); +} + +struct cf_ngtcp2_ctx { + struct cf_quic_ctx q; + struct ssl_peer peer; + struct curl_tls_ctx tls; + ngtcp2_path connected_path; + ngtcp2_conn *qconn; + ngtcp2_cid dcid; + ngtcp2_cid scid; + uint32_t version; + ngtcp2_settings settings; + ngtcp2_transport_params transport_params; + ngtcp2_ccerr last_error; + ngtcp2_crypto_conn_ref conn_ref; + struct cf_call_data call_data; + nghttp3_conn *h3conn; + nghttp3_settings h3settings; + struct curltime started_at; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct curltime reconnect_at; /* time the next attempt should start */ + struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct dynbuf scratch; /* temp buffer for header construction */ + struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */ + size_t max_stream_window; /* max flow window for one stream */ + uint64_t max_idle_ms; /* max idle time for QUIC connection */ + uint64_t used_bidi_streams; /* bidi streams we have opened */ + uint64_t max_bidi_streams; /* max bidi streams we can open */ + int qlogfd; + BIT(shutdown_started); /* queued shutdown packets */ +}; + +/* How to access `call_data` from a cf_ngtcp2 filter */ +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) \ + ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data + +struct pkt_io_ctx; +static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct pkt_io_ctx *pktx); +static CURLcode cf_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct pkt_io_ctx *pktx); + +/** + * All about the H3 internals of a stream + */ +struct h3_stream_ctx { + curl_int64_t id; /* HTTP/3 protocol identifier */ + struct bufq sendbuf; /* h3 request body */ + struct h1_req_parser h1; /* h1 request parsing */ + size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ + curl_uint64_t error3; /* HTTP/3 stream error code */ + curl_off_t upload_left; /* number of request bytes left to upload */ + int status_code; /* HTTP status code */ + CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ + bool resp_hds_complete; /* we have a complete, final response */ + bool closed; /* TRUE on stream close */ + bool reset; /* TRUE on stream reset */ + bool send_closed; /* stream is local closed */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ +}; + +#define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\ + data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) +#define H3_STREAM_CTX_ID(ctx,id) ((struct h3_stream_ctx *)(\ + Curl_hash_offt_get(&(ctx)->streams, (id)))) + +static void h3_stream_ctx_free(struct h3_stream_ctx *stream) +{ + Curl_bufq_free(&stream->sendbuf); + Curl_h1_req_parse_free(&stream->h1); + free(stream); +} + +static void h3_stream_hash_free(void *stream) +{ + DEBUGASSERT(stream); + h3_stream_ctx_free((struct h3_stream_ctx *)stream); +} + +static CURLcode h3_data_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + if(!data) { + failf(data, "initialization failure, transfer not http initialized"); + return CURLE_FAILED_INIT; + } + + if(stream) + return CURLE_OK; + + stream = calloc(1, sizeof(*stream)); + if(!stream) + return CURLE_OUT_OF_MEMORY; + + stream->id = -1; + /* on send, we control how much we put into the buffer */ + Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, + H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); + stream->sendbuf_len_in_flight = 0; + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); + + if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + h3_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +static void cf_ngtcp2_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + DEBUGASSERT(data); + DEBUGASSERT(stream); + if(!stream->closed && ctx->qconn && ctx->h3conn) { + CURLcode result; + + nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); + ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); + stream->closed = TRUE; + (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id, + NGHTTP3_H3_REQUEST_CANCELLED); + result = cf_progress_egress(cf, data, NULL); + if(result) + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cancel stream -> %d", + stream->id, result); + } +} + +static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)cf; + if(stream) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] easy handle is done", + stream->id); + cf_ngtcp2_stream_close(cf, data, stream); + Curl_hash_offt_remove(&ctx->streams, data->id); + } +} + +static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, + struct Curl_easy *data, + int64_t stream_id, + struct h3_stream_ctx **pstream) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *sdata; + struct h3_stream_ctx *stream; + + (void)cf; + stream = H3_STREAM_CTX(ctx, data); + if(stream && stream->id == stream_id) { + *pstream = stream; + return data; + } + else { + DEBUGASSERT(data->multi); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if(sdata->conn != data->conn) + continue; + stream = H3_STREAM_CTX(ctx, sdata); + if(stream && stream->id == stream_id) { + *pstream = stream; + return sdata; + } + } + } + *pstream = NULL; + return NULL; +} + +static void h3_drain_stream(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + unsigned char bits; + + (void)cf; + bits = CURL_CSELECT_IN; + if(stream && stream->upload_left && !stream->send_closed) + bits |= CURL_CSELECT_OUT; + if(data->state.select_bits != bits) { + data->state.select_bits = bits; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } +} + +/* ngtcp2 default congestion controller does not perform pacing. Limit + the maximum packet burst to MAX_PKT_BURST packets. */ +#define MAX_PKT_BURST 10 + +struct pkt_io_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; + ngtcp2_tstamp ts; + size_t pkt_count; + ngtcp2_path_storage ps; +}; + +static void pktx_update_time(struct pkt_io_ctx *pktx, + struct Curl_cfilter *cf) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + + vquic_ctx_update_time(&ctx->q); + pktx->ts = (ngtcp2_tstamp)ctx->q.last_op.tv_sec * NGTCP2_SECONDS + + (ngtcp2_tstamp)ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS; +} + +static void pktx_init(struct pkt_io_ctx *pktx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + pktx->cf = cf; + pktx->data = data; + pktx->pkt_count = 0; + ngtcp2_path_storage_zero(&pktx->ps); + pktx_update_time(pktx, cf); +} + +static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data); + +static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) +{ + struct Curl_cfilter *cf = conn_ref->user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + return ctx->qconn; +} + +#ifdef DEBUG_NGTCP2 +static void quic_printf(void *user_data, const char *fmt, ...) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + + (void)ctx; /* TODO: need an easy handle to infof() message */ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} +#endif + +static void qlog_callback(void *user_data, uint32_t flags, + const void *data, size_t datalen) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + (void)flags; + if(ctx->qlogfd != -1) { + ssize_t rc = write(ctx->qlogfd, data, datalen); + if(rc == -1) { + /* on write error, stop further write attempts */ + close(ctx->qlogfd); + ctx->qlogfd = -1; + } + } + +} + +static void quic_settings(struct cf_ngtcp2_ctx *ctx, + struct Curl_easy *data, + struct pkt_io_ctx *pktx) +{ + ngtcp2_settings *s = &ctx->settings; + ngtcp2_transport_params *t = &ctx->transport_params; + + ngtcp2_settings_default(s); + ngtcp2_transport_params_default(t); +#ifdef DEBUG_NGTCP2 + s->log_printf = quic_printf; +#else + s->log_printf = NULL; +#endif + + (void)data; + s->initial_ts = pktx->ts; + s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT; + s->max_window = 100 * ctx->max_stream_window; + s->max_stream_window = ctx->max_stream_window; + + t->initial_max_data = 10 * ctx->max_stream_window; + t->initial_max_stream_data_bidi_local = ctx->max_stream_window; + t->initial_max_stream_data_bidi_remote = ctx->max_stream_window; + t->initial_max_stream_data_uni = ctx->max_stream_window; + t->initial_max_streams_bidi = QUIC_MAX_STREAMS; + t->initial_max_streams_uni = QUIC_MAX_STREAMS; + t->max_idle_timeout = (ctx->max_idle_ms * NGTCP2_MILLISECONDS); + if(ctx->qlogfd != -1) { + s->qlog_write = qlog_callback; + } +} + +static CURLcode init_ngh3_conn(struct Curl_cfilter *cf); + +static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) +{ + (void)user_data; + (void)tconn; + return 0; +} + +static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data); + +static bool cf_ngtcp2_err_is_fatal(int code) +{ + return (NGTCP2_ERR_FATAL >= code) || + (NGTCP2_ERR_DROP_CONN == code) || + (NGTCP2_ERR_IDLE_CLOSE == code); +} + +static void cf_ngtcp2_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + if(!ctx->last_error.error_code) { + if(NGTCP2_ERR_CRYPTO == code) { + ngtcp2_ccerr_set_tls_alert(&ctx->last_error, + ngtcp2_conn_get_tls_alert(ctx->qconn), + NULL, 0); + } + else { + ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0); + } + } + if(cf_ngtcp2_err_is_fatal(code)) + cf_ngtcp2_conn_close(cf, data); +} + +static bool cf_ngtcp2_h3_err_is_fatal(int code) +{ + return (NGHTTP3_ERR_FATAL >= code) || + (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code); +} + +static void cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + if(!ctx->last_error.error_code) { + ngtcp2_ccerr_set_application_error(&ctx->last_error, + nghttp3_err_infer_quic_app_error_code(code), NULL, 0); + } + if(cf_ngtcp2_h3_err_is_fatal(code)) + cf_ngtcp2_conn_close(cf, data); +} + +static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, + int64_t sid, uint64_t offset, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)sid; + nghttp3_ssize nconsumed; + int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; + struct Curl_easy *data = stream_user_data; + (void)offset; + (void)data; + + nconsumed = + nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); + if(!data) + data = CF_DATA_CURRENT(cf); + if(data) + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read_stream(len=%zu) -> %zd", + stream_id, buflen, nconsumed); + if(nconsumed < 0) { + struct h3_stream_ctx *stream = H3_STREAM_CTX_ID(ctx, stream_id); + if(data && stream) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] error on known stream, " + "reset=%d, closed=%d", + stream_id, stream->reset, stream->closed); + } + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + /* number of bytes inside buflen which consists of framing overhead + * including QPACK HEADERS. In other words, it does not consume payload of + * DATA frame. */ + ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, (uint64_t)nconsumed); + ngtcp2_conn_extend_max_offset(tconn, (uint64_t)nconsumed); + + return 0; +} + +static int +cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t offset, uint64_t datalen, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + (void)stream_id; + (void)tconn; + (void)offset; + (void)datalen; + (void)stream_user_data; + + rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, + int64_t sid, uint64_t app_error_code, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + curl_int64_t stream_id = (curl_int64_t)sid; + int rv; + + (void)tconn; + /* stream is closed... */ + if(!data) + data = CF_DATA_CURRENT(cf); + if(!data) + return NGTCP2_ERR_CALLBACK_FAILURE; + + if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { + app_error_code = NGHTTP3_H3_NO_ERROR; + } + + rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] quic close(app_error=%" + CURL_PRIu64 ") -> %d", stream_id, (curl_uint64_t)app_error_code, + rv); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + cf_ngtcp2_h3_err_set(cf, data, rv); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_reset(ngtcp2_conn *tconn, int64_t sid, + uint64_t final_size, uint64_t app_error_code, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)sid; + struct Curl_easy *data = stream_user_data; + int rv; + (void)tconn; + (void)final_size; + (void)app_error_code; + (void)data; + + rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + (void)tconn; + (void)app_error_code; + (void)stream_user_data; + + rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, + uint64_t max_streams, + void *user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)tconn; + ctx->max_bidi_streams = max_streams; + if(data) + CURL_TRC_CF(data, cf, "max bidi streams now %" CURL_PRIu64 + ", used %" CURL_PRIu64, (curl_uint64_t)ctx->max_bidi_streams, + (curl_uint64_t)ctx->used_bidi_streams); + return 0; +} + +static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t sid, + uint64_t max_data, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)sid; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct Curl_easy *s_data; + struct h3_stream_ctx *stream; + int rv; + (void)tconn; + (void)max_data; + (void)stream_user_data; + + rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + s_data = get_stream_easy(cf, data, stream_id, &stream); + if(s_data && stream && stream->quic_flow_blocked) { + CURL_TRC_CF(s_data, cf, "[%" CURL_PRId64 "] unblock quic flow", + stream_id); + stream->quic_flow_blocked = FALSE; + h3_drain_stream(cf, s_data); + } + return 0; +} + +static void cb_rand(uint8_t *dest, size_t destlen, + const ngtcp2_rand_ctx *rand_ctx) +{ + CURLcode result; + (void)rand_ctx; + + result = Curl_rand(NULL, dest, destlen); + if(result) { + /* cb_rand is only used for non-cryptographic context. If Curl_rand + failed, just fill 0 and call it *random*. */ + memset(dest, 0, destlen); + } +} + +static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, + uint8_t *token, size_t cidlen, + void *user_data) +{ + CURLcode result; + (void)tconn; + (void)user_data; + + result = Curl_rand(NULL, cid->data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} + +static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, + void *user_data) +{ + struct Curl_cfilter *cf = user_data; + (void)tconn; + + if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) { + return 0; + } + + if(init_ngh3_conn(cf) != CURLE_OK) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static ngtcp2_callbacks ng_callbacks = { + ngtcp2_crypto_client_initial_cb, + NULL, /* recv_client_initial */ + ngtcp2_crypto_recv_crypto_data_cb, + cb_handshake_completed, + NULL, /* recv_version_negotiation */ + ngtcp2_crypto_encrypt_cb, + ngtcp2_crypto_decrypt_cb, + ngtcp2_crypto_hp_mask_cb, + cb_recv_stream_data, + cb_acked_stream_data_offset, + NULL, /* stream_open */ + cb_stream_close, + NULL, /* recv_stateless_reset */ + ngtcp2_crypto_recv_retry_cb, + cb_extend_max_local_streams_bidi, + NULL, /* extend_max_local_streams_uni */ + cb_rand, + cb_get_new_connection_id, + NULL, /* remove_connection_id */ + ngtcp2_crypto_update_key_cb, /* update_key */ + NULL, /* path_validation */ + NULL, /* select_preferred_addr */ + cb_stream_reset, + NULL, /* extend_max_remote_streams_bidi */ + NULL, /* extend_max_remote_streams_uni */ + cb_extend_max_stream_data, + NULL, /* dcid_status */ + NULL, /* handshake_confirmed */ + NULL, /* recv_new_token */ + ngtcp2_crypto_delete_crypto_aead_ctx_cb, + ngtcp2_crypto_delete_crypto_cipher_ctx_cb, + NULL, /* recv_datagram */ + NULL, /* ack_datagram */ + NULL, /* lost_datagram */ + ngtcp2_crypto_get_path_challenge_data_cb, + cb_stream_stop_sending, + NULL, /* version_negotiation */ + cb_recv_rx_key, + NULL, /* recv_tx_key */ + NULL, /* early_data_rejected */ +}; + +/** + * Connection maintenance like timeouts on packet ACKs etc. are done by us, not + * the OS like for TCP. POLL events on the socket therefore are not + * sufficient. + * ngtcp2 tells us when it wants to be invoked again. We handle that via + * the `Curl_expire()` mechanisms. + */ +static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct pkt_io_ctx *pktx) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct pkt_io_ctx local_pktx; + ngtcp2_tstamp expiry; + + if(!pktx) { + pktx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + pktx_update_time(pktx, cf); + } + + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + if(expiry != UINT64_MAX) { + if(expiry <= pktx->ts) { + CURLcode result; + int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); + if(rv) { + failf(data, "ngtcp2_conn_handle_expiry returned error: %s", + ngtcp2_strerror(rv)); + cf_ngtcp2_err_set(cf, data, rv); + return CURLE_SEND_ERROR; + } + result = cf_progress_ingress(cf, data, pktx); + if(result) + return result; + result = cf_progress_egress(cf, data, pktx); + if(result) + return result; + /* ask again, things might have changed */ + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + } + + if(expiry > pktx->ts) { + ngtcp2_duration timeout = expiry - pktx->ts; + if(timeout % NGTCP2_MILLISECONDS) { + timeout += NGTCP2_MILLISECONDS; + } + Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), + EXPIRE_QUIC); + } + } + return CURLE_OK; +} + +static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + bool want_recv, want_send; + + if(!ctx->qconn) + return; + + Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); + if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) + want_send = TRUE; + + if(want_recv || want_send) { + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + bool c_exhaust, s_exhaust; + + CF_DATA_SAVE(save, cf, data); + c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || + !ngtcp2_conn_get_max_data_left(ctx->qconn)); + s_exhaust = want_send && stream && stream->id >= 0 && + stream->quic_flow_blocked; + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); + + Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } +} + +static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + curl_int64_t stream_id = (curl_int64_t)sid; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)conn; + (void)stream_id; + + /* we might be called by nghttp3 after we already cleaned up */ + if(!stream) + return 0; + + stream->closed = TRUE; + stream->error3 = (curl_uint64_t)app_error_code; + if(stream->error3 != NGHTTP3_H3_NO_ERROR) { + stream->reset = TRUE; + stream->send_closed = TRUE; + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] RESET: error %" CURL_PRIu64, + stream->id, stream->error3); + } + else { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] CLOSED", stream->id); + } + h3_drain_stream(cf, data); + return 0; +} + +static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + const char *buf, size_t blen, bool eos) +{ + + /* If we already encountered an error, skip further writes */ + if(!stream->xfer_result) { + stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); + if(stream->xfer_result) + CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] error %d writing %zu " + "bytes of headers", stream->id, stream->xfer_result, blen); + } +} + +static void h3_xfer_write_resp(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + const char *buf, size_t blen, bool eos) +{ + + /* If we already encountered an error, skip further writes */ + if(!stream->xfer_result) { + stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); + /* If the transfer write is errored, we do not want any more data */ + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] error %d writing %zu bytes " + "of data", stream->id, stream->xfer_result, blen); + } + } +} + +static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, + const uint8_t *buf, size_t blen, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + (void)conn; + (void)stream3_id; + + if(!stream) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + h3_xfer_write_resp(cf, data, stream, (char *)buf, blen, FALSE); + if(blen) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] ACK %zu bytes of DATA", + stream->id, blen); + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, blen); + ngtcp2_conn_extend_max_offset(ctx->qconn, blen); + } + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu", stream->id, blen); + return 0; +} + +static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, + size_t consumed, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + (void)conn; + (void)stream_user_data; + + /* nghttp3 has consumed bytes on the QUIC stream and we need to + * tell the QUIC connection to increase its flow control */ + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed); + ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); + return 0; +} + +static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, + int fin, void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + curl_int64_t stream_id = (curl_int64_t)sid; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)conn; + (void)stream_id; + (void)fin; + (void)cf; + + if(!stream) + return 0; + /* add a CRLF only if we have received some headers */ + h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); + + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d", + stream_id, stream->status_code); + if(stream->status_code / 100 != 1) { + stream->resp_hds_complete = TRUE; + } + h3_drain_stream(cf, data); + return 0; +} + +static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, + int32_t token, nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)sid; + nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); + nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result = CURLE_OK; + (void)conn; + (void)stream_id; + (void)token; + (void)flags; + (void)cf; + + /* we might have cleaned up this transfer already */ + if(!stream) + return 0; + + if(token == NGHTTP3_QPACK_TOKEN__STATUS) { + + result = Curl_http_decode_status(&stream->status_code, + (const char *)h3val.base, h3val.len); + if(result) + return -1; + Curl_dyn_reset(&ctx->scratch); + result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 ")); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, + (const char *)h3val.base, h3val.len); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); + if(!result) + h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch), + Curl_dyn_len(&ctx->scratch), FALSE); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", + stream_id, Curl_dyn_ptr(&ctx->scratch)); + if(result) { + return -1; + } + } + else { + /* store as an HTTP1-style header */ + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] header: %.*s: %.*s", + stream_id, (int)h3name.len, h3name.base, + (int)h3val.len, h3val.base); + Curl_dyn_reset(&ctx->scratch); + result = Curl_dyn_addn(&ctx->scratch, + (const char *)h3name.base, h3name.len); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST(": ")); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, + (const char *)h3val.base, h3val.len); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n")); + if(!result) + h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch), + Curl_dyn_len(&ctx->scratch), FALSE); + } + return 0; +} + +static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + (void)conn; + (void)stream_user_data; + + rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, + app_error_code); + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid, + uint64_t app_error_code, void *user_data, + void *stream_user_data) { + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)sid; + struct Curl_easy *data = stream_user_data; + int rv; + (void)conn; + (void)data; + + rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, + app_error_code); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static nghttp3_callbacks ngh3_callbacks = { + cb_h3_acked_req_body, /* acked_stream_data */ + cb_h3_stream_close, + cb_h3_recv_data, + cb_h3_deferred_consume, + NULL, /* begin_headers */ + cb_h3_recv_header, + cb_h3_end_headers, + NULL, /* begin_trailers */ + cb_h3_recv_header, + NULL, /* end_trailers */ + cb_h3_stop_sending, + NULL, /* end_stream */ + cb_h3_reset_stream, + NULL, /* shutdown */ + NULL /* recv_settings */ +}; + +static CURLcode init_ngh3_conn(struct Curl_cfilter *cf) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result; + int rc; + int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; + + if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { + return CURLE_QUIC_CONNECT_ERROR; + } + + nghttp3_settings_default(&ctx->h3settings); + + rc = nghttp3_conn_client_new(&ctx->h3conn, + &ngh3_callbacks, + &ctx->h3settings, + nghttp3_mem_default(), + cf); + if(rc) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, + qpack_dec_stream_id); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + return CURLE_OK; +fail: + + return result; +} + +static ssize_t recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + CURLcode *err) +{ + ssize_t nread = -1; + + (void)cf; + if(stream->reset) { + failf(data, + "HTTP/3 stream %" CURL_PRId64 " reset by server", stream->id); + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; + goto out; + } + else if(!stream->resp_hds_complete) { + failf(data, + "HTTP/3 stream %" CURL_PRId64 " was closed cleanly, but before " + "getting all response header fields, treated as error", + stream->id); + *err = CURLE_HTTP3; + goto out; + } + *err = CURLE_OK; + nread = 0; + +out: + return nread; +} + +/* incoming data frames on the h3 stream */ +static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t blen, CURLcode *err) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + ssize_t nread = -1; + struct cf_call_data save; + struct pkt_io_ctx pktx; + + (void)ctx; + (void)buf; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + *err = CURLE_OK; + + pktx_init(&pktx, cf, data); + + if(!stream || ctx->shutdown_started) { + *err = CURLE_RECV_ERROR; + goto out; + } + + if(cf_progress_ingress(cf, data, &pktx)) { + *err = CURLE_RECV_ERROR; + nread = -1; + goto out; + } + + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] xfer write failed", stream->id); + cf_ngtcp2_stream_close(cf, data, stream); + *err = stream->xfer_result; + nread = -1; + goto out; + } + else if(stream->closed) { + nread = recv_closed_stream(cf, data, stream, err); + goto out; + } + *err = CURLE_AGAIN; + nread = -1; + +out: + if(cf_progress_egress(cf, data, &pktx)) { + *err = CURLE_SEND_ERROR; + nread = -1; + } + else { + CURLcode result2 = check_and_set_expiry(cf, data, &pktx); + if(result2) { + *err = result2; + nread = -1; + } + } + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_recv(blen=%zu) -> %zd, %d", + stream? stream->id : -1, blen, nread, *err); + CF_DATA_RESTORE(cf, save); + return nread; +} + +static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + size_t skiplen; + + (void)cf; + if(!stream) + return 0; + /* The server acknowledged `datalen` of bytes from our request body. + * This is a delta. We have kept this data in `sendbuf` for + * re-transmissions and can free it now. */ + if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) + skiplen = stream->sendbuf_len_in_flight; + else + skiplen = (size_t)datalen; + Curl_bufq_skip(&stream->sendbuf, skiplen); + stream->sendbuf_len_in_flight -= skiplen; + + /* Resume upload processing if we have more data to send */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + int rv = nghttp3_conn_resume_stream(conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static nghttp3_ssize +cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, + nghttp3_vec *vec, size_t veccnt, + uint32_t *pflags, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + ssize_t nwritten = 0; + size_t nvecs = 0; + (void)cf; + (void)conn; + (void)stream_id; + (void)user_data; + (void)veccnt; + + if(!stream) + return NGHTTP3_ERR_CALLBACK_FAILURE; + /* nghttp3 keeps references to the sendbuf data until it is ACKed + * by the server (see `cb_h3_acked_req_body()` for updates). + * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` + * that we have already passed to nghttp3, but which have not been + * ACKed yet. + * Any amount beyond `sendbuf_len_in_flight` we need still to pass + * to nghttp3. Do that now, if we can. */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + nvecs = 0; + while(nvecs < veccnt && + Curl_bufq_peek_at(&stream->sendbuf, + stream->sendbuf_len_in_flight, + (const unsigned char **)&vec[nvecs].base, + &vec[nvecs].len)) { + stream->sendbuf_len_in_flight += vec[nvecs].len; + nwritten += vec[nvecs].len; + ++nvecs; + } + DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ + } + + if(nwritten > 0 && stream->upload_left != -1) + stream->upload_left -= nwritten; + + /* When we stopped sending and everything in `sendbuf` is "in flight", + * we are at the end of the request body. */ + if(stream->upload_left == 0) { + *pflags = NGHTTP3_DATA_FLAG_EOF; + stream->send_closed = TRUE; + } + else if(!nwritten) { + /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> AGAIN", + stream->id); + return NGHTTP3_ERR_WOULDBLOCK; + } + + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> " + "%d vecs%s with %zu (buffered=%zu, left=%" + CURL_FORMAT_CURL_OFF_T ")", + stream->id, (int)nvecs, + *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", + nwritten, Curl_bufq_len(&stream->sendbuf), + stream->upload_left); + return (nghttp3_ssize)nvecs; +} + +/* Index where :authority header field will appear in request header + field list. */ +#define AUTHORITY_DST_IDX 3 + +static ssize_t h3_stream_open(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t len, + CURLcode *err) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = NULL; + int64_t sid; + struct dynhds h2_headers; + size_t nheader; + nghttp3_nv *nva = NULL; + int rc = 0; + unsigned int i; + ssize_t nwritten = -1; + nghttp3_data_reader reader; + nghttp3_data_reader *preader = NULL; + + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + + *err = h3_data_setup(cf, data); + if(*err) + goto out; + stream = H3_STREAM_CTX(ctx, data); + DEBUGASSERT(stream); + if(!stream) { + *err = CURLE_FAILED_INIT; + goto out; + } + + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); + if(nwritten < 0) + goto out; + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); + + *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); + if(*err) { + nwritten = -1; + goto out; + } + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); + + nheader = Curl_dynhds_count(&h2_headers); + nva = malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + *err = CURLE_OUT_OF_MEMORY; + nwritten = -1; + goto out; + } + + for(i = 0; i < nheader; ++i) { + struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + } + + rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); + if(rc) { + failf(data, "can get bidi streams"); + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + stream->id = (curl_int64_t)sid; + ++ctx->used_bidi_streams; + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: + /* known request body size or -1 */ + if(data->state.infilesize != -1) + stream->upload_left = data->state.infilesize; + else + /* data sending without specifying the data amount up front */ + stream->upload_left = -1; /* unknown */ + break; + default: + /* there is not request body */ + stream->upload_left = 0; /* no request body */ + break; + } + + stream->send_closed = (stream->upload_left == 0); + if(!stream->send_closed) { + reader.read_data = cb_h3_read_req_body; + preader = &reader; + } + + rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id, + nva, nheader, preader, data); + if(rc) { + switch(rc) { + case NGHTTP3_ERR_CONN_CLOSING: + CURL_TRC_CF(data, cf, "h3sid[%" CURL_PRId64 "] failed to send, " + "connection is closing", stream->id); + break; + default: + CURL_TRC_CF(data, cf, "h3sid[%" CURL_PRId64 "] failed to send -> " + "%d (%s)", stream->id, rc, ngtcp2_strerror(rc)); + break; + } + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + + if(Curl_trc_is_verbose(data)) { + infof(data, "[HTTP/3] [%" CURL_PRId64 "] OPENED stream for %s", + stream->id, data->state.url); + for(i = 0; i < nheader; ++i) { + infof(data, "[HTTP/3] [%" CURL_PRId64 "] [%.*s: %.*s]", stream->id, + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + } + +out: + free(nva); + Curl_dynhds_free(&h2_headers); + return nwritten; +} + +static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + ssize_t sent = 0; + struct cf_call_data save; + struct pkt_io_ctx pktx; + CURLcode result; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + pktx_init(&pktx, cf, data); + *err = CURLE_OK; + + result = cf_progress_ingress(cf, data, &pktx); + if(result) { + *err = result; + sent = -1; + } + + if(!stream || stream->id < 0) { + if(ctx->shutdown_started) { + CURL_TRC_CF(data, cf, "cannot open stream on closed connection"); + *err = CURLE_SEND_ERROR; + sent = -1; + goto out; + } + sent = h3_stream_open(cf, data, buf, len, err); + if(sent < 0) { + CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err); + goto out; + } + stream = H3_STREAM_CTX(ctx, data); + } + else if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] xfer write failed", stream->id); + cf_ngtcp2_stream_close(cf, data, stream); + *err = stream->xfer_result; + sent = -1; + goto out; + } + else if(stream->closed) { + if(stream->resp_hds_complete) { + /* Server decided to close the stream after having sent us a final + * response. This is valid if it is not interested in the request + * body. This happens on 30x or 40x responses. + * We silently discard the data sent, since this is not a transport + * error situation. */ + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] discarding data" + "on closed stream with response", stream->id); + *err = CURLE_OK; + sent = (ssize_t)len; + goto out; + } + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send_body(len=%zu) " + "-> stream closed", stream->id, len); + *err = CURLE_HTTP3; + sent = -1; + goto out; + } + else if(ctx->shutdown_started) { + CURL_TRC_CF(data, cf, "cannot send on closed connection"); + *err = CURLE_SEND_ERROR; + sent = -1; + goto out; + } + else { + sent = Curl_bufq_write(&stream->sendbuf, buf, len, err); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send, add to " + "sendbuf(len=%zu) -> %zd, %d", + stream->id, len, sent, *err); + if(sent < 0) { + goto out; + } + + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); + } + + result = cf_progress_egress(cf, data, &pktx); + if(result) { + *err = result; + sent = -1; + } + +out: + result = check_and_set_expiry(cf, data, &pktx); + if(result) { + *err = result; + sent = -1; + } + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d", + stream? stream->id : -1, len, sent, *err); + CF_DATA_RESTORE(cf, save); + return sent; +} + +static CURLcode qng_verify_peer(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + + cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + cf->conn->httpversion = 30; + cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; + + return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); +} + +static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, + struct sockaddr_storage *remote_addr, + socklen_t remote_addrlen, int ecn, + void *userp) +{ + struct pkt_io_ctx *pktx = userp; + struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx; + ngtcp2_pkt_info pi; + ngtcp2_path path; + int rv; + + ++pktx->pkt_count; + ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, + (socklen_t)ctx->q.local_addrlen); + ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, + remote_addrlen); + pi.ecn = (uint8_t)ecn; + + rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts); + if(rv) { + CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", + ngtcp2_strerror(rv), rv); + cf_ngtcp2_err_set(pktx->cf, pktx->data, rv); + + if(rv == NGTCP2_ERR_CRYPTO) + /* this is a "TLS problem", but a failed certificate verification + is a common reason for this */ + return CURLE_PEER_FAILED_VERIFICATION; + return CURLE_RECV_ERROR; + } + + return CURLE_OK; +} + +static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct pkt_io_ctx *pktx) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct pkt_io_ctx local_pktx; + size_t pkts_chunk = 128, i; + CURLcode result = CURLE_OK; + + if(!pktx) { + pktx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + pktx_update_time(pktx, cf); + } + + result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); + if(result) + return result; + + for(i = 0; i < 4; ++i) { + if(i) + pktx_update_time(pktx, cf); + pktx->pkt_count = 0; + result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk, + recv_pkt, pktx); + if(result || !pktx->pkt_count) /* error or got nothing */ + break; + } + return result; +} + +/** + * Read a network packet to send from ngtcp2 into `buf`. + * Return number of bytes written or -1 with *err set. + */ +static ssize_t read_pkt_to_send(void *userp, + unsigned char *buf, size_t buflen, + CURLcode *err) +{ + struct pkt_io_ctx *x = userp; + struct cf_ngtcp2_ctx *ctx = x->cf->ctx; + nghttp3_vec vec[16]; + nghttp3_ssize veccnt; + ngtcp2_ssize ndatalen; + uint32_t flags; + int64_t stream_id; + int fin; + ssize_t nwritten, n; + veccnt = 0; + stream_id = -1; + fin = 0; + + /* ngtcp2 may want to put several frames from different streams into + * this packet. `NGTCP2_WRITE_STREAM_FLAG_MORE` tells it to do so. + * When `NGTCP2_ERR_WRITE_MORE` is returned, we *need* to make + * another iteration. + * When ngtcp2 is happy (because it has no other frame that would fit + * or it has nothing more to send), it returns the total length + * of the assembled packet. This may be 0 if there was nothing to send. */ + nwritten = 0; + *err = CURLE_OK; + for(;;) { + + if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) { + veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec, + sizeof(vec) / sizeof(vec[0])); + if(veccnt < 0) { + failf(x->data, "nghttp3_conn_writev_stream returned error: %s", + nghttp3_strerror((int)veccnt)); + cf_ngtcp2_h3_err_set(x->cf, x->data, (int)veccnt); + *err = CURLE_SEND_ERROR; + return -1; + } + } + + flags = NGTCP2_WRITE_STREAM_FLAG_MORE | + (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); + n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path, + NULL, buf, buflen, + &ndatalen, flags, stream_id, + (const ngtcp2_vec *)vec, veccnt, x->ts); + if(n == 0) { + /* nothing to send */ + *err = CURLE_AGAIN; + nwritten = -1; + goto out; + } + else if(n < 0) { + switch(n) { + case NGTCP2_ERR_STREAM_DATA_BLOCKED: { + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); + DEBUGASSERT(ndatalen == -1); + nghttp3_conn_block_stream(ctx->h3conn, stream_id); + CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] block quic flow", + (curl_int64_t)stream_id); + DEBUGASSERT(stream); + if(stream) + stream->quic_flow_blocked = TRUE; + n = 0; + break; + } + case NGTCP2_ERR_STREAM_SHUT_WR: + DEBUGASSERT(ndatalen == -1); + nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); + n = 0; + break; + case NGTCP2_ERR_WRITE_MORE: + /* ngtcp2 wants to send more. update the flow of the stream whose data + * is in the buffer and continue */ + DEBUGASSERT(ndatalen >= 0); + n = 0; + break; + default: + DEBUGASSERT(ndatalen == -1); + failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", + ngtcp2_strerror((int)n)); + cf_ngtcp2_err_set(x->cf, x->data, (int)n); + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + } + + if(ndatalen >= 0) { + /* we add the amount of data bytes to the flow windows */ + int rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); + if(rv) { + failf(x->data, "nghttp3_conn_add_write_offset returned error: %s\n", + nghttp3_strerror(rv)); + return CURLE_SEND_ERROR; + } + } + + if(n > 0) { + /* packet assembled, leave */ + nwritten = n; + goto out; + } + } +out: + return nwritten; +} + +static CURLcode cf_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct pkt_io_ctx *pktx) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + ssize_t nread; + size_t max_payload_size, path_max_payload_size, max_pktcnt; + size_t pktcnt = 0; + size_t gsolen = 0; /* this disables gso until we have a clue */ + CURLcode curlcode; + struct pkt_io_ctx local_pktx; + + if(!pktx) { + pktx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + pktx_update_time(pktx, cf); + ngtcp2_path_storage_zero(&pktx->ps); + } + + curlcode = vquic_flush(cf, data, &ctx->q); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + + /* In UDP, there is a maximum theoretical packet paload length and + * a minimum payload length that is "guaranteed" to work. + * To detect if this minimum payload can be increased, ngtcp2 sends + * now and then a packet payload larger than the minimum. It that + * is ACKed by the peer, both parties know that it works and + * the subsequent packets can use a larger one. + * This is called PMTUD (Path Maximum Transmission Unit Discovery). + * Since a PMTUD might be rejected right on send, we do not want it + * be followed by other packets of lesser size. Because those would + * also fail then. So, if we detect a PMTUD while buffering, we flush. + */ + max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); + path_max_payload_size = + ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); + /* maximum number of packets buffered before we flush to the socket */ + max_pktcnt = CURLMIN(MAX_PKT_BURST, + ctx->q.sendbuf.chunk_size / max_payload_size); + + for(;;) { + /* add the next packet to send, if any, to our buffer */ + nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, + read_pkt_to_send, pktx, &curlcode); + if(nread < 0) { + if(curlcode != CURLE_AGAIN) + return curlcode; + /* Nothing more to add, flush and leave */ + curlcode = vquic_send(cf, data, &ctx->q, gsolen); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + goto out; + } + + DEBUGASSERT(nread > 0); + if(pktcnt == 0) { + /* first packet in buffer. This is either of a known, "good" + * payload size or it is a PMTUD. We will see. */ + gsolen = (size_t)nread; + } + else if((size_t)nread > gsolen || + (gsolen > path_max_payload_size && (size_t)nread != gsolen)) { + /* The just added packet is a PMTUD *or* the one(s) before the + * just added were PMTUD and the last one is smaller. + * Flush the buffer before the last add. */ + curlcode = vquic_send_tail_split(cf, data, &ctx->q, + gsolen, nread, nread); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + pktcnt = 0; + continue; + } + + if(++pktcnt >= max_pktcnt || (size_t)nread < gsolen) { + /* Reached MAX_PKT_BURST *or* + * the capacity of our buffer *or* + * last add was shorter than the previous ones, flush */ + curlcode = vquic_send(cf, data, &ctx->q, gsolen); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + /* pktbuf has been completely sent */ + pktcnt = 0; + } + } + +out: + return CURLE_OK; +} + +/* + * Called from transfer.c:data_pending to know if we should keep looping + * to receive more data from the connection. + */ +static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + (void)cf; + (void)data; + return FALSE; +} + +static CURLcode h3_data_pause(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool pause) +{ + /* TODO: there seems right now no API in ngtcp2 to shrink/enlarge + * the streams windows. As we do in HTTP/2. */ + if(!pause) { + h3_drain_stream(cf, data); + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + return CURLE_OK; +} + +static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_SETUP: + break; + case CF_CTRL_DATA_PAUSE: + result = h3_data_pause(cf, data, (arg1 != 0)); + break; + case CF_CTRL_DATA_DETACH: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE_SEND: { + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + if(stream && !stream->send_closed) { + stream->send_closed = TRUE; + stream->upload_left = Curl_bufq_len(&stream->sendbuf) - + stream->sendbuf_len_in_flight; + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); + } + break; + } + case CF_CTRL_DATA_IDLE: { + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURL_TRC_CF(data, cf, "data idle"); + if(stream && !stream->closed) { + result = check_and_set_expiry(cf, data, NULL); + if(result) + CURL_TRC_CF(data, cf, "data idle, check_and_set_expiry -> %d", result); + } + break; + } + default: + break; + } + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) +{ + struct cf_call_data save = ctx->call_data; + + if(ctx->qlogfd != -1) { + close(ctx->qlogfd); + } + Curl_vquic_tls_cleanup(&ctx->tls); + vquic_ctx_free(&ctx->q); + if(ctx->h3conn) + nghttp3_conn_del(ctx->h3conn); + if(ctx->qconn) + ngtcp2_conn_del(ctx->qconn); + Curl_bufcp_free(&ctx->stream_bufcp); + Curl_dyn_free(&ctx->scratch); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); + Curl_ssl_peer_cleanup(&ctx->peer); + + memset(ctx, 0, sizeof(*ctx)); + ctx->qlogfd = -1; + ctx->call_data = save; +} + +static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_call_data save; + struct pkt_io_ctx pktx; + CURLcode result = CURLE_OK; + + if(cf->shutdown || !ctx->qconn) { + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + *done = FALSE; + pktx_init(&pktx, cf, data); + + if(!ctx->shutdown_started) { + char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; + ngtcp2_ssize nwritten; + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); + result = cf_progress_egress(cf, data, &pktx); + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); + *done = TRUE; + goto out; + } + } + + ctx->shutdown_started = TRUE; + nwritten = ngtcp2_conn_write_connection_close( + ctx->qconn, NULL, /* path */ + NULL, /* pkt_info */ + (uint8_t *)buffer, sizeof(buffer), + &ctx->last_error, pktx.ts); + CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" + CURL_PRIu64 ") -> %d", ctx->last_error.type, + (curl_uint64_t)ctx->last_error.error_code, (int)nwritten); + if(nwritten > 0) { + Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, + (size_t)nwritten, &result); + if(result) { + CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " + "aborting shutdown", result); + goto out; + } + ctx->q.no_gso = TRUE; + ctx->q.gsolen = (size_t)nwritten; + ctx->q.split_len = 0; + } + } + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing egress"); + result = vquic_flush(cf, data, &ctx->q); + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); + *done = TRUE; + goto out; + } + } + + if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* Sent everything off. ngtcp2 seems to have no support for graceful + * shutdowns. So, we are done. */ + CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); + *done = TRUE; + result = CURLE_OK; + } +out: + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + bool done; + cf_ngtcp2_shutdown(cf, data, &done); +} + +static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + if(ctx && ctx->qconn) { + cf_ngtcp2_conn_close(cf, data); + cf_ngtcp2_ctx_clear(ctx); + CURL_TRC_CF(data, cf, "close"); + } + cf->connected = FALSE; + CF_DATA_RESTORE(cf, save); +} + +static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + CURL_TRC_CF(data, cf, "destroy"); + if(ctx) { + cf_ngtcp2_ctx_clear(ctx); + free(ctx); + } + cf->ctx = NULL; + /* No CF_DATA_RESTORE(cf, save) possible */ + (void)save; +} + +#ifdef USE_OPENSSL +/* The "new session" callback must return zero if the session can be removed + * or non-zero if the session has been put into the session cache. + */ +static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) +{ + struct Curl_cfilter *cf; + struct cf_ngtcp2_ctx *ctx; + struct Curl_easy *data; + ngtcp2_crypto_conn_ref *cref; + + cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); + cf = cref? cref->user_data : NULL; + ctx = cf? cf->ctx : NULL; + data = cf? CF_DATA_CURRENT(cf) : NULL; + if(cf && data && ctx) { + Curl_ossl_add_session(cf, data, &ctx->peer, ssl_sessionid); + return 1; + } + return 0; +} +#endif /* USE_OPENSSL */ + +static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data) +{ + struct curl_tls_ctx *ctx = user_data; + (void)cf; +#ifdef USE_OPENSSL +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) + if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) + != 0) { + failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#else + if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ + /* Enable the session cache because it is a prerequisite for the + * "new session" callback. Use the "external storage" mode to prevent + * OpenSSL from creating an internal session cache. + */ + SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, quic_ossl_new_session_cb); + +#elif defined(USE_GNUTLS) + if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) { + failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); + return CURLE_FAILED_INIT; + } +#elif defined(USE_WOLFSSL) + if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ctx) != 0) { + failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#endif + return CURLE_OK; +} + +/* + * Might be called twice for happy eyeballs. + */ +static CURLcode cf_connect_start(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct pkt_io_ctx *pktx) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rc; + int rv; + CURLcode result; + const struct Curl_sockaddr_ex *sockaddr = NULL; + int qfd; + + ctx->version = NGTCP2_PROTO_VER_MAX; + ctx->max_stream_window = H3_STREAM_WINDOW_SIZE; + ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; + Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, + H3_STREAM_POOL_SPARES); + Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + + result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); + if(result) + return result; + +#define H3_ALPN "\x2h3\x5h3-29" + result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, + H3_ALPN, sizeof(H3_ALPN) - 1, + tls_ctx_setup, &ctx->tls, &ctx->conn_ref); + if(result) + return result; + +#ifdef USE_OPENSSL + SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); +#endif + + ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; + + ctx->scid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; + + (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); + ctx->qlogfd = qfd; /* -1 if failure above */ + quic_settings(ctx, data, pktx); + + result = vquic_ctx_init(&ctx->q); + if(result) + return result; + + Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL); + if(!sockaddr) + return CURLE_QUIC_CONNECT_ERROR; + ctx->q.local_addrlen = sizeof(ctx->q.local_addr); + rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, + &ctx->q.local_addrlen); + if(rv == -1) + return CURLE_QUIC_CONNECT_ERROR; + + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &sockaddr->sa_addr, (socklen_t)sockaddr->addrlen); + + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + NULL, cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; + +#ifdef USE_OPENSSL + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); +#elif defined(USE_GNUTLS) + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); +#else + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.handle); +#endif + + ngtcp2_ccerr_default(&ctx->last_error); + + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; + + return CURLE_OK; +} + +static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct cf_call_data save; + struct curltime now; + struct pkt_io_ctx pktx; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* Connect the UDP filter first */ + if(!cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + } + + *done = FALSE; + now = Curl_now(); + pktx_init(&pktx, cf, data); + + CF_DATA_SAVE(save, cf, data); + + if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { + /* Not time yet to attempt the next connect */ + CURL_TRC_CF(data, cf, "waiting for reconnect time"); + goto out; + } + + if(!ctx->qconn) { + ctx->started_at = now; + result = cf_connect_start(cf, data, &pktx); + if(result) + goto out; + result = cf_progress_egress(cf, data, &pktx); + /* we do not expect to be able to recv anything yet */ + goto out; + } + + result = cf_progress_ingress(cf, data, &pktx); + if(result) + goto out; + + result = cf_progress_egress(cf, data, &pktx); + if(result) + goto out; + + if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { + ctx->handshake_at = now; + CURL_TRC_CF(data, cf, "handshake complete after %dms", + (int)Curl_timediff(now, ctx->started_at)); + result = qng_verify_peer(cf, data); + if(!result) { + CURL_TRC_CF(data, cf, "peer verified"); + cf->connected = TRUE; + cf->conn->alpn = CURL_HTTP_VERSION_3; + *done = TRUE; + connkeep(cf->conn, "HTTP/3 default"); + } + } + +out: + if(result == CURLE_RECV_ERROR && ctx->qconn && + ngtcp2_conn_in_draining_period(ctx->qconn)) { + /* When a QUIC server instance is shutting down, it may send us a + * CONNECTION_CLOSE right away. Our connection then enters the DRAINING + * state. The CONNECT may work in the near future again. Indicate + * that as a "weird" reply. */ + result = CURLE_WEIRD_SERVER_REPLY; + } + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(result) { + struct ip_quadruple ip; + + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); + infof(data, "QUIC connect to %s port %u failed: %s", + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); + } +#endif + if(!result && ctx->qconn) { + result = check_and_set_expiry(cf, data, &pktx); + } + if(result || *done) + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_call_data save; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: { + DEBUGASSERT(pres1); + CF_DATA_SAVE(save, cf, data); + /* Set after transport params arrived and continually updated + * by callback. QUIC counts the number over the lifetime of the + * connection, ever increasing. + * We count the *open* transfers plus the budget for new ones. */ + if(!ctx->qconn || ctx->shutdown_started) { + *pres1 = 0; + } + else if(ctx->max_bidi_streams) { + uint64_t avail_bidi_streams = 0; + uint64_t max_streams = CONN_INUSE(cf->conn); + if(ctx->max_bidi_streams > ctx->used_bidi_streams) + avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams; + max_streams += avail_bidi_streams; + *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams; + } + else /* transport params not arrived yet? take our default. */ + *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi); + CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: " + "MAX_CONCURRENT -> %d (%zu in use)", + cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn)); + CF_DATA_RESTORE(cf, save); + return CURLE_OK; + } + case CF_QUERY_CONNECT_REPLY_MS: + if(ctx->q.got_first_byte) { + timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at); + *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; + } + else + *pres1 = -1; + return CURLE_OK; + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + if(ctx->q.got_first_byte) + *when = ctx->q.first_byte_at; + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + bool alive = FALSE; + const ngtcp2_transport_params *rp; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + *input_pending = FALSE; + if(!ctx->qconn || ctx->shutdown_started) + goto out; + + /* Both sides of the QUIC connection announce they max idle times in + * the transport parameters. Look at the minimum of both and if + * we exceed this, regard the connection as dead. The other side + * may have completely purged it and will no longer respond + * to any packets from us. */ + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + if(rp) { + timediff_t idletime; + uint64_t idle_ms = ctx->max_idle_ms; + + if(rp->max_idle_timeout && + (rp->max_idle_timeout / NGTCP2_MILLISECONDS) < idle_ms) + idle_ms = (rp->max_idle_timeout / NGTCP2_MILLISECONDS); + idletime = Curl_timediff(Curl_now(), ctx->q.last_io); + if(idletime > 0 && (uint64_t)idletime > idle_ms) + goto out; + } + + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + goto out; + + alive = TRUE; + if(*input_pending) { + CURLcode result; + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, + only "protocol frames" */ + *input_pending = FALSE; + result = cf_progress_ingress(cf, data, NULL); + CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); + alive = result? FALSE : TRUE; + } + +out: + CF_DATA_RESTORE(cf, save); + return alive; +} + +struct Curl_cftype Curl_cft_http3 = { + "HTTP/3", + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + 0, + cf_ngtcp2_destroy, + cf_ngtcp2_connect, + cf_ngtcp2_close, + cf_ngtcp2_shutdown, + Curl_cf_def_get_host, + cf_ngtcp2_adjust_pollset, + cf_ngtcp2_data_pending, + cf_ngtcp2_send, + cf_ngtcp2_recv, + cf_ngtcp2_data_event, + cf_ngtcp2_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_ngtcp2_query, +}; + +CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai) +{ + struct cf_ngtcp2_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL, *udp_cf = NULL; + CURLcode result; + + (void)data; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->qlogfd = -1; + cf_ngtcp2_ctx_clear(ctx); + + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + + result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); + if(result) + goto out; + + cf->conn = conn; + udp_cf->conn = cf->conn; + udp_cf->sockindex = cf->sockindex; + cf->next = udp_cf; + +out: + *pcf = (!result)? cf : NULL; + if(result) { + if(udp_cf) + Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); + Curl_safefree(cf); + Curl_safefree(ctx); + } + return result; +} + +bool Curl_conn_is_ngtcp2(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; + + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_http3) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + } + return FALSE; +} + +#endif diff --git a/third_party/curl/lib/vquic/curl_ngtcp2.h b/third_party/curl/lib/vquic/curl_ngtcp2.h new file mode 100644 index 0000000000..db3e611bd0 --- /dev/null +++ b/third_party/curl/lib/vquic/curl_ngtcp2.h @@ -0,0 +1,61 @@ +#ifndef HEADER_CURL_VQUIC_CURL_NGTCP2_H +#define HEADER_CURL_VQUIC_CURL_NGTCP2_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + +#ifdef HAVE_NETINET_UDP_H +#include +#endif + +#include +#include +#ifdef USE_OPENSSL +#include +#elif defined(USE_WOLFSSL) +#include +#include +#include +#endif + +struct Curl_cfilter; + +#include "urldata.h" + +void Curl_ngtcp2_ver(char *p, size_t len); + +CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai); + +bool Curl_conn_is_ngtcp2(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); +#endif + +#endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */ diff --git a/third_party/curl/lib/vquic/curl_osslq.c b/third_party/curl/lib/vquic/curl_osslq.c new file mode 100644 index 0000000000..dafde44f26 --- /dev/null +++ b/third_party/curl/lib/vquic/curl_osslq.c @@ -0,0 +1,2379 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) + +#include +#include +#include +#include + +#include "urldata.h" +#include "hash.h" +#include "sendf.h" +#include "strdup.h" +#include "rand.h" +#include "multiif.h" +#include "strcase.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "connect.h" +#include "progress.h" +#include "strerror.h" +#include "dynbuf.h" +#include "http1.h" +#include "select.h" +#include "inet_pton.h" +#include "vquic.h" +#include "vquic_int.h" +#include "vquic-tls.h" +#include "vtls/keylog.h" +#include "vtls/vtls.h" +#include "vtls/openssl.h" +#include "curl_osslq.h" + +#include "warnless.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* A stream window is the maximum amount we need to buffer for + * each active transfer. We use HTTP/3 flow control and only ACK + * when we take things out of the buffer. + * Chunk size is large enough to take a full DATA frame */ +#define H3_STREAM_WINDOW_SIZE (128 * 1024) +#define H3_STREAM_CHUNK_SIZE (16 * 1024) +/* The pool keeps spares around and half of a full stream window + * seems good. More does not seem to improve performance. + * The benefit of the pool is that stream buffer to not keep + * spares. Memory consumption goes down when streams run empty, + * have a large upload done, etc. */ +#define H3_STREAM_POOL_SPARES \ + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 +/* Receive and Send max number of chunks just follows from the + * chunk size and window size */ +#define H3_STREAM_RECV_CHUNKS \ + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) +#define H3_STREAM_SEND_CHUNKS \ + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +typedef uint32_t sslerr_t; +#else +typedef unsigned long sslerr_t; +#endif + + +/* How to access `call_data` from a cf_osslq filter */ +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) \ + ((struct cf_osslq_ctx *)(cf)->ctx)->call_data + +static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data); + +static const char *osslq_SSL_ERROR_to_str(int err) +{ + switch(err) { + case SSL_ERROR_NONE: + return "SSL_ERROR_NONE"; + case SSL_ERROR_SSL: + return "SSL_ERROR_SSL"; + case SSL_ERROR_WANT_READ: + return "SSL_ERROR_WANT_READ"; + case SSL_ERROR_WANT_WRITE: + return "SSL_ERROR_WANT_WRITE"; + case SSL_ERROR_WANT_X509_LOOKUP: + return "SSL_ERROR_WANT_X509_LOOKUP"; + case SSL_ERROR_SYSCALL: + return "SSL_ERROR_SYSCALL"; + case SSL_ERROR_ZERO_RETURN: + return "SSL_ERROR_ZERO_RETURN"; + case SSL_ERROR_WANT_CONNECT: + return "SSL_ERROR_WANT_CONNECT"; + case SSL_ERROR_WANT_ACCEPT: + return "SSL_ERROR_WANT_ACCEPT"; +#if defined(SSL_ERROR_WANT_ASYNC) + case SSL_ERROR_WANT_ASYNC: + return "SSL_ERROR_WANT_ASYNC"; +#endif +#if defined(SSL_ERROR_WANT_ASYNC_JOB) + case SSL_ERROR_WANT_ASYNC_JOB: + return "SSL_ERROR_WANT_ASYNC_JOB"; +#endif +#if defined(SSL_ERROR_WANT_EARLY) + case SSL_ERROR_WANT_EARLY: + return "SSL_ERROR_WANT_EARLY"; +#endif + default: + return "SSL_ERROR unknown"; + } +} + +/* Return error string for last OpenSSL error */ +static char *osslq_strerror(unsigned long error, char *buf, size_t size) +{ + DEBUGASSERT(size); + *buf = '\0'; + +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) + ERR_error_string_n((uint32_t)error, buf, size); +#else + ERR_error_string_n(error, buf, size); +#endif + + if(!*buf) { + const char *msg = error ? "Unknown error" : "No error"; + if(strlen(msg) < size) + strcpy(buf, msg); + } + + return buf; +} + +static CURLcode make_bio_addr(BIO_ADDR **pbio_addr, + const struct Curl_sockaddr_ex *addr) +{ + BIO_ADDR *ba; + CURLcode result = CURLE_FAILED_INIT; + + ba = BIO_ADDR_new(); + if(!ba) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + switch(addr->family) { + case AF_INET: { + struct sockaddr_in * const sin = + (struct sockaddr_in * const)(void *)&addr->sa_addr; + if(!BIO_ADDR_rawmake(ba, AF_INET, &sin->sin_addr, + sizeof(sin->sin_addr), sin->sin_port)) { + goto out; + } + result = CURLE_OK; + break; + } +#ifdef USE_IPV6 + case AF_INET6: { + struct sockaddr_in6 * const sin = + (struct sockaddr_in6 * const)(void *)&addr->sa_addr; + if(!BIO_ADDR_rawmake(ba, AF_INET6, &sin->sin6_addr, + sizeof(sin->sin6_addr), sin->sin6_port)) { + } + result = CURLE_OK; + break; + } +#endif /* USE_IPV6 */ + default: + /* sunsupported */ + DEBUGASSERT(0); + break; + } + +out: + if(result && ba) { + BIO_ADDR_free(ba); + ba = NULL; + } + *pbio_addr = ba; + return result; +} + +/* QUIC stream (not necessarily H3) */ +struct cf_osslq_stream { + curl_int64_t id; + SSL *ssl; + struct bufq recvbuf; /* QUIC war data recv buffer */ + BIT(recvd_eos); + BIT(closed); + BIT(reset); + BIT(send_blocked); +}; + +static CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s, + SSL *conn, + uint64_t flags, + struct bufc_pool *bufcp, + void *user_data) +{ + DEBUGASSERT(!s->ssl); + Curl_bufq_initp(&s->recvbuf, bufcp, 1, BUFQ_OPT_NONE); + s->ssl = SSL_new_stream(conn, flags); + if(!s->ssl) { + return CURLE_FAILED_INIT; + } + s->id = (curl_int64_t)SSL_get_stream_id(s->ssl); + SSL_set_app_data(s->ssl, user_data); + return CURLE_OK; +} + +static void cf_osslq_stream_cleanup(struct cf_osslq_stream *s) +{ + if(s->ssl) { + SSL_set_app_data(s->ssl, NULL); + SSL_free(s->ssl); + } + Curl_bufq_free(&s->recvbuf); + memset(s, 0, sizeof(*s)); +} + +static void cf_osslq_stream_close(struct cf_osslq_stream *s) +{ + if(s->ssl) { + SSL_free(s->ssl); + s->ssl = NULL; + } +} + +struct cf_osslq_h3conn { + nghttp3_conn *conn; + nghttp3_settings settings; + struct cf_osslq_stream s_ctrl; + struct cf_osslq_stream s_qpack_enc; + struct cf_osslq_stream s_qpack_dec; + struct cf_osslq_stream remote_ctrl[3]; /* uni streams opened by the peer */ + size_t remote_ctrl_n; /* number of peer streams opened */ +}; + +static void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3) +{ + size_t i; + + if(h3->conn) + nghttp3_conn_del(h3->conn); + cf_osslq_stream_cleanup(&h3->s_ctrl); + cf_osslq_stream_cleanup(&h3->s_qpack_enc); + cf_osslq_stream_cleanup(&h3->s_qpack_dec); + for(i = 0; i < h3->remote_ctrl_n; ++i) { + cf_osslq_stream_cleanup(&h3->remote_ctrl[i]); + } +} + +struct cf_osslq_ctx { + struct cf_quic_ctx q; + struct ssl_peer peer; + struct curl_tls_ctx tls; + struct cf_call_data call_data; + struct cf_osslq_h3conn h3; + struct curltime started_at; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct curltime first_byte_at; /* when first byte was recvd */ + struct curltime reconnect_at; /* time the next attempt should start */ + struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */ + size_t max_stream_window; /* max flow window for one stream */ + uint64_t max_idle_ms; /* max idle time for QUIC connection */ + BIT(got_first_byte); /* if first byte was received */ + BIT(x509_store_setup); /* if x509 store has been set up */ + BIT(protocol_shutdown); /* QUIC connection is shut down */ + BIT(need_recv); /* QUIC connection needs to receive */ + BIT(need_send); /* QUIC connection needs to send */ +}; + +static void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx) +{ + struct cf_call_data save = ctx->call_data; + + cf_osslq_h3conn_cleanup(&ctx->h3); + Curl_vquic_tls_cleanup(&ctx->tls); + vquic_ctx_free(&ctx->q); + Curl_bufcp_free(&ctx->stream_bufcp); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); + Curl_ssl_peer_cleanup(&ctx->peer); + + memset(ctx, 0, sizeof(*ctx)); + ctx->call_data = save; +} + +static CURLcode cf_osslq_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct cf_call_data save; + CURLcode result = CURLE_OK; + int rc; + + CF_DATA_SAVE(save, cf, data); + + if(cf->shutdown || ctx->protocol_shutdown) { + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + *done = FALSE; + ctx->need_send = FALSE; + ctx->need_recv = FALSE; + + rc = SSL_shutdown_ex(ctx->tls.ossl.ssl, + SSL_SHUTDOWN_FLAG_NO_BLOCK, NULL, 0); + if(rc == 0) { /* ongoing */ + CURL_TRC_CF(data, cf, "shutdown ongoing"); + ctx->need_recv = TRUE; + goto out; + } + else if(rc == 1) { /* done */ + CURL_TRC_CF(data, cf, "shutdown finished"); + *done = TRUE; + goto out; + } + else { + long sslerr; + char err_buffer[256]; + int err = SSL_get_error(ctx->tls.ossl.ssl, rc); + + switch(err) { + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + CURL_TRC_CF(data, cf, "shutdown not received, but closed"); + *done = TRUE; + goto out; + case SSL_ERROR_WANT_READ: + /* SSL has send its notify and now wants to read the reply + * from the server. We are not really interested in that. */ + CURL_TRC_CF(data, cf, "shutdown sent, want receive"); + ctx->need_recv = TRUE; + goto out; + case SSL_ERROR_WANT_WRITE: + CURL_TRC_CF(data, cf, "shutdown send blocked"); + ctx->need_send = TRUE; + goto out; + default: + /* We give up on this. */ + sslerr = ERR_get_error(); + CURL_TRC_CF(data, cf, "shutdown, ignore recv error: '%s', errno %d", + (sslerr ? + osslq_strerror(sslerr, err_buffer, sizeof(err_buffer)) : + osslq_SSL_ERROR_to_str(err)), + SOCKERRNO); + *done = TRUE; + result = CURLE_OK; + goto out; + } + } +out: + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + if(ctx && ctx->tls.ossl.ssl) { + CURL_TRC_CF(data, cf, "cf_osslq_close()"); + if(!cf->shutdown && !ctx->protocol_shutdown) { + /* last best effort, which OpenSSL calls a "rapid" shutdown. */ + SSL_shutdown_ex(ctx->tls.ossl.ssl, + (SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID), + NULL, 0); + } + cf_osslq_ctx_clear(ctx); + } + + cf->connected = FALSE; + CF_DATA_RESTORE(cf, save); +} + +static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + CURL_TRC_CF(data, cf, "destroy"); + if(ctx) { + CURL_TRC_CF(data, cf, "cf_osslq_destroy()"); + cf_osslq_ctx_clear(ctx); + free(ctx); + } + cf->ctx = NULL; + /* No CF_DATA_RESTORE(cf, save) possible */ + (void)save; +} + +static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, + SSL *stream_ssl, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)SSL_get_stream_id(stream_ssl); + + if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) { + /* rejected, we are full */ + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] rejecting remote stream", + stream_id); + SSL_free(stream_ssl); + return CURLE_FAILED_INIT; + } + switch(SSL_get_stream_type(stream_ssl)) { + case SSL_STREAM_TYPE_READ: { + struct cf_osslq_stream *nstream = &h3->remote_ctrl[h3->remote_ctrl_n++]; + nstream->id = stream_id; + nstream->ssl = stream_ssl; + Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] accepted remote uni stream", + stream_id); + break; + } + default: + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reject remote non-uni-read" + " stream", stream_id); + SSL_free(stream_ssl); + return CURLE_FAILED_INIT; + } + return CURLE_OK; + +} + +static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, + struct Curl_easy *data, + int detail, CURLcode def_result) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + CURLcode result = def_result; + sslerr_t errdetail; + char ebuf[256] = "unknown"; + const char *err_descr = ebuf; + long lerr; + int lib; + int reason; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + + errdetail = ERR_get_error(); + lib = ERR_GET_LIB(errdetail); + reason = ERR_GET_REASON(errdetail); + + if((lib == ERR_LIB_SSL) && + ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) || + (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { + result = CURLE_PEER_FAILED_VERIFICATION; + + lerr = SSL_get_verify_result(ctx->tls.ossl.ssl); + if(lerr != X509_V_OK) { + ssl_config->certverifyresult = lerr; + msnprintf(ebuf, sizeof(ebuf), + "SSL certificate problem: %s", + X509_verify_cert_error_string(lerr)); + } + else + err_descr = "SSL certificate verification failed"; + } +#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED) + /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on + OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */ + else if((lib == ERR_LIB_SSL) && + (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) { + /* If client certificate is required, communicate the + error to client */ + result = CURLE_SSL_CLIENTCERT; + osslq_strerror(errdetail, ebuf, sizeof(ebuf)); + } +#endif + else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) { + ctx->protocol_shutdown = TRUE; + err_descr = "QUIC connection has been shut down"; + result = def_result; + } + else { + result = def_result; + osslq_strerror(errdetail, ebuf, sizeof(ebuf)); + } + + /* detail is already set to the SSL error above */ + + /* If we e.g. use SSLv2 request-method and the server does not like us + * (RST connection, etc.), OpenSSL gives no explanation whatsoever and + * the SO_ERROR is also lost. + */ + if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { + char extramsg[80]=""; + int sockerr = SOCKERRNO; + struct ip_quadruple ip; + + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); + if(sockerr && detail == SSL_ERROR_SYSCALL) + Curl_strerror(sockerr, extramsg, sizeof(extramsg)); + failf(data, "QUIC connect: %s in connection to %s:%d (%s)", + extramsg[0] ? extramsg : osslq_SSL_ERROR_to_str(detail), + ctx->peer.dispname, ip.remote_port, ip.remote_ip); + } + else { + /* Could be a CERT problem */ + failf(data, "%s", err_descr); + } + return result; +} + +static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + + cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + cf->conn->httpversion = 30; + cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; + + return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); +} + +/** + * All about the H3 internals of a stream + */ +struct h3_stream_ctx { + struct cf_osslq_stream s; + struct bufq sendbuf; /* h3 request body */ + struct bufq recvbuf; /* h3 response body */ + struct h1_req_parser h1; /* h1 request parsing */ + size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ + size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */ + curl_uint64_t error3; /* HTTP/3 stream error code */ + curl_off_t upload_left; /* number of request bytes left to upload */ + curl_off_t download_recvd; /* number of response DATA bytes received */ + int status_code; /* HTTP status code */ + bool resp_hds_complete; /* we have a complete, final response */ + bool closed; /* TRUE on stream close */ + bool reset; /* TRUE on stream reset */ + bool send_closed; /* stream is local closed */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ +}; + +#define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\ + data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + +static void h3_stream_ctx_free(struct h3_stream_ctx *stream) +{ + cf_osslq_stream_cleanup(&stream->s); + Curl_bufq_free(&stream->sendbuf); + Curl_bufq_free(&stream->recvbuf); + Curl_h1_req_parse_free(&stream->h1); + free(stream); +} + +static void h3_stream_hash_free(void *stream) +{ + DEBUGASSERT(stream); + h3_stream_ctx_free((struct h3_stream_ctx *)stream); +} + +static CURLcode h3_data_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + if(!data) { + failf(data, "initialization failure, transfer not http initialized"); + return CURLE_FAILED_INIT; + } + + if(stream) + return CURLE_OK; + + stream = calloc(1, sizeof(*stream)); + if(!stream) + return CURLE_OUT_OF_MEMORY; + + stream->s.id = -1; + /* on send, we control how much we put into the buffer */ + Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, + H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); + stream->sendbuf_len_in_flight = 0; + /* on recv, we need a flexible buffer limit since we also write + * headers to it that are not counted against the nghttp3 flow limits. */ + Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, + H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); + stream->recv_buf_nonflow = 0; + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); + + if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + h3_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + (void)cf; + if(stream) { + CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] easy handle is done", + stream->s.id); + if(ctx->h3.conn && !stream->closed) { + nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id); + nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id, + NGHTTP3_H3_REQUEST_CANCELLED); + nghttp3_conn_set_stream_user_data(ctx->h3.conn, stream->s.id, NULL); + stream->closed = TRUE; + } + + Curl_hash_offt_remove(&ctx->streams, data->id); + } +} + +static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf, + struct Curl_easy *data, + int64_t stream_id) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct Curl_easy *sdata; + + if(stream && stream->s.id == stream_id) { + return &stream->s; + } + else if(ctx->h3.s_ctrl.id == stream_id) { + return &ctx->h3.s_ctrl; + } + else if(ctx->h3.s_qpack_enc.id == stream_id) { + return &ctx->h3.s_qpack_enc; + } + else if(ctx->h3.s_qpack_dec.id == stream_id) { + return &ctx->h3.s_qpack_dec; + } + else { + DEBUGASSERT(data->multi); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if(sdata->conn != data->conn) + continue; + stream = H3_STREAM_CTX(ctx, sdata); + if(stream && stream->s.id == stream_id) { + return &stream->s; + } + } + } + return NULL; +} + +static void h3_drain_stream(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + unsigned char bits; + + (void)cf; + bits = CURL_CSELECT_IN; + if(stream && stream->upload_left && !stream->send_closed) + bits |= CURL_CSELECT_OUT; + if(data->state.select_bits != bits) { + data->state.select_bits = bits; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } +} + +static CURLcode h3_data_pause(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool pause) +{ + if(!pause) { + /* unpaused. make it run again right away */ + h3_drain_stream(cf, data); + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + return CURLE_OK; +} + +static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_osslq_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)conn; + (void)stream_id; + + /* we might be called by nghttp3 after we already cleaned up */ + if(!stream) + return 0; + + stream->closed = TRUE; + stream->error3 = app_error_code; + if(stream->error3 != NGHTTP3_H3_NO_ERROR) { + stream->reset = TRUE; + stream->send_closed = TRUE; + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] RESET: error %" CURL_PRIu64, + stream->s.id, stream->error3); + } + else { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] CLOSED", stream->s.id); + } + h3_drain_stream(cf, data); + return 0; +} + +/* + * write_resp_raw() copies response data in raw format to the `data`'s + * receive buffer. If not enough space is available, it appends to the + * `data`'s overflow buffer. + */ +static CURLcode write_resp_raw(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, size_t memlen, + bool flow) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result = CURLE_OK; + ssize_t nwritten; + + (void)cf; + if(!stream) { + return CURLE_RECV_ERROR; + } + nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); + if(nwritten < 0) { + return result; + } + + if(!flow) + stream->recv_buf_nonflow += (size_t)nwritten; + + if((size_t)nwritten < memlen) { + /* This MUST not happen. Our recbuf is dimensioned to hold the + * full max_stream_window and then some for this very reason. */ + DEBUGASSERT(0); + return CURLE_RECV_ERROR; + } + return result; +} + +static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_osslq_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result; + + (void)conn; + (void)stream3_id; + + if(!stream) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + result = write_resp_raw(cf, data, buf, buflen, TRUE); + if(result) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu, ERROR %d", + stream->s.id, buflen, result); + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + stream->download_recvd += (curl_off_t)buflen; + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu, total=%zd", + stream->s.id, buflen, stream->download_recvd); + h3_drain_stream(cf, data); + return 0; +} + +static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, + size_t consumed, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_osslq_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + (void)conn; + (void)stream_id; + if(stream) + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] deferred consume %zu bytes", + stream->s.id, consumed); + return 0; +} + +static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, + int32_t token, nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + curl_int64_t stream_id = sid; + struct cf_osslq_ctx *ctx = cf->ctx; + nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); + nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result = CURLE_OK; + (void)conn; + (void)stream_id; + (void)token; + (void)flags; + (void)cf; + + /* we might have cleaned up this transfer already */ + if(!stream) + return 0; + + if(token == NGHTTP3_QPACK_TOKEN__STATUS) { + char line[14]; /* status line is always 13 characters long */ + size_t ncopy; + + result = Curl_http_decode_status(&stream->status_code, + (const char *)h3val.base, h3val.len); + if(result) + return -1; + ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", + stream->status_code); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", stream_id, line); + result = write_resp_raw(cf, data, line, ncopy, FALSE); + if(result) { + return -1; + } + } + else { + /* store as an HTTP1-style header */ + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] header: %.*s: %.*s", + stream_id, (int)h3name.len, h3name.base, + (int)h3val.len, h3val.base); + result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); + if(result) { + return -1; + } + result = write_resp_raw(cf, data, ": ", 2, FALSE); + if(result) { + return -1; + } + result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE); + if(result) { + return -1; + } + result = write_resp_raw(cf, data, "\r\n", 2, FALSE); + if(result) { + return -1; + } + } + return 0; +} + +static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, + int fin, void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_osslq_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + curl_int64_t stream_id = sid; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result = CURLE_OK; + (void)conn; + (void)stream_id; + (void)fin; + (void)cf; + + if(!stream) + return 0; + /* add a CRLF only if we have received some headers */ + result = write_resp_raw(cf, data, "\r\n", 2, FALSE); + if(result) { + return -1; + } + + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d", + stream_id, stream->status_code); + if(stream->status_code / 100 != 1) { + stream->resp_hds_complete = TRUE; + } + h3_drain_stream(cf, data); + return 0; +} + +static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t sid, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_osslq_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + curl_int64_t stream_id = sid; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)conn; + (void)app_error_code; + + if(!stream || !stream->s.ssl) + return 0; + + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] stop_sending", stream_id); + cf_osslq_stream_close(&stream->s); + return 0; +} + +static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid, + uint64_t app_error_code, void *user_data, + void *stream_user_data) { + struct Curl_cfilter *cf = user_data; + struct cf_osslq_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + curl_int64_t stream_id = sid; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + int rv; + (void)conn; + + if(stream && stream->s.ssl) { + SSL_STREAM_RESET_ARGS args = {0}; + args.quic_error_code = app_error_code; + rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args)); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); + if(!rv) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static nghttp3_ssize +cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, + nghttp3_vec *vec, size_t veccnt, + uint32_t *pflags, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_osslq_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + ssize_t nwritten = 0; + size_t nvecs = 0; + (void)cf; + (void)conn; + (void)stream_id; + (void)user_data; + (void)veccnt; + + if(!stream) + return NGHTTP3_ERR_CALLBACK_FAILURE; + /* nghttp3 keeps references to the sendbuf data until it is ACKed + * by the server (see `cb_h3_acked_req_body()` for updates). + * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` + * that we have already passed to nghttp3, but which have not been + * ACKed yet. + * Any amount beyond `sendbuf_len_in_flight` we need still to pass + * to nghttp3. Do that now, if we can. */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + nvecs = 0; + while(nvecs < veccnt && + Curl_bufq_peek_at(&stream->sendbuf, + stream->sendbuf_len_in_flight, + (const unsigned char **)&vec[nvecs].base, + &vec[nvecs].len)) { + stream->sendbuf_len_in_flight += vec[nvecs].len; + nwritten += vec[nvecs].len; + ++nvecs; + } + DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ + } + + if(nwritten > 0 && stream->upload_left != -1) + stream->upload_left -= nwritten; + + /* When we stopped sending and everything in `sendbuf` is "in flight", + * we are at the end of the request body. */ + if(stream->upload_left == 0) { + *pflags = NGHTTP3_DATA_FLAG_EOF; + stream->send_closed = TRUE; + } + else if(!nwritten) { + /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> AGAIN", + stream->s.id); + return NGHTTP3_ERR_WOULDBLOCK; + } + + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> " + "%d vecs%s with %zu (buffered=%zu, left=%" + CURL_FORMAT_CURL_OFF_T ")", + stream->s.id, (int)nvecs, + *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", + nwritten, Curl_bufq_len(&stream->sendbuf), + stream->upload_left); + return (nghttp3_ssize)nvecs; +} + +static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_osslq_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + size_t skiplen; + + (void)cf; + if(!stream) + return 0; + /* The server acknowledged `datalen` of bytes from our request body. + * This is a delta. We have kept this data in `sendbuf` for + * re-transmissions and can free it now. */ + if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) + skiplen = stream->sendbuf_len_in_flight; + else + skiplen = (size_t)datalen; + Curl_bufq_skip(&stream->sendbuf, skiplen); + stream->sendbuf_len_in_flight -= skiplen; + + /* Resume upload processing if we have more data to send */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + int rv = nghttp3_conn_resume_stream(conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static nghttp3_callbacks ngh3_callbacks = { + cb_h3_acked_stream_data, + cb_h3_stream_close, + cb_h3_recv_data, + cb_h3_deferred_consume, + NULL, /* begin_headers */ + cb_h3_recv_header, + cb_h3_end_headers, + NULL, /* begin_trailers */ + cb_h3_recv_header, + NULL, /* end_trailers */ + cb_h3_stop_sending, + NULL, /* end_stream */ + cb_h3_reset_stream, + NULL, /* shutdown */ + NULL /* recv_settings */ +}; + +static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn, + void *user_data) +{ + struct cf_osslq_h3conn *h3 = &ctx->h3; + CURLcode result; + int rc; + + nghttp3_settings_default(&h3->settings); + rc = nghttp3_conn_client_new(&h3->conn, + &ngh3_callbacks, + &h3->settings, + nghttp3_mem_default(), + user_data); + if(rc) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = cf_osslq_stream_open(&h3->s_ctrl, conn, + SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI, + &ctx->stream_bufcp, NULL); + if(result) { + result = CURLE_QUIC_CONNECT_ERROR; + goto out; + } + result = cf_osslq_stream_open(&h3->s_qpack_enc, conn, + SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI, + &ctx->stream_bufcp, NULL); + if(result) { + result = CURLE_QUIC_CONNECT_ERROR; + goto out; + } + result = cf_osslq_stream_open(&h3->s_qpack_dec, conn, + SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI, + &ctx->stream_bufcp, NULL); + if(result) { + result = CURLE_QUIC_CONNECT_ERROR; + goto out; + } + + rc = nghttp3_conn_bind_control_stream(h3->conn, h3->s_ctrl.id); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto out; + } + rc = nghttp3_conn_bind_qpack_streams(h3->conn, h3->s_qpack_enc.id, + h3->s_qpack_dec.id); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto out; + } + + result = CURLE_OK; +out: + return result; +} + +static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + CURLcode result; + int rv; + const struct Curl_sockaddr_ex *peer_addr = NULL; + BIO *bio = NULL; + BIO_ADDR *baddr = NULL; + + Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, + H3_STREAM_POOL_SPARES); + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); + if(result) + goto out; + +#define H3_ALPN "\x2h3" + result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, + H3_ALPN, sizeof(H3_ALPN) - 1, + NULL, NULL, NULL); + if(result) + goto out; + + result = vquic_ctx_init(&ctx->q); + if(result) + goto out; + + result = CURLE_QUIC_CONNECT_ERROR; + Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &peer_addr, NULL); + if(!peer_addr) + goto out; + + ctx->q.local_addrlen = sizeof(ctx->q.local_addr); + rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, + &ctx->q.local_addrlen); + if(rv == -1) + goto out; + + result = make_bio_addr(&baddr, peer_addr); + if(result) { + failf(data, "error creating BIO_ADDR from sockaddr"); + goto out; + } + + /* Type conversions, see #12861: OpenSSL wants an `int`, but on 64-bit + * Win32 systems, Microsoft defines SOCKET as `unsigned long long`. + */ +#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) + if(ctx->q.sockfd > INT_MAX) { + failf(data, "Windows socket identifier larger than MAX_INT, " + "unable to set in OpenSSL dgram API."); + result = CURLE_QUIC_CONNECT_ERROR; + goto out; + } + bio = BIO_new_dgram((int)ctx->q.sockfd, BIO_NOCLOSE); +#else + bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE); +#endif + if(!bio) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(!SSL_set1_initial_peer_addr(ctx->tls.ossl.ssl, baddr)) { + failf(data, "failed to set the initial peer address"); + result = CURLE_FAILED_INIT; + goto out; + } + if(!SSL_set_blocking_mode(ctx->tls.ossl.ssl, 0)) { + failf(data, "failed to turn off blocking mode"); + result = CURLE_FAILED_INIT; + goto out; + } + +#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT + /* Added in OpenSSL v3.3.x */ + if(!SSL_set_feature_request_uint(ctx->tls.ossl.ssl, + SSL_VALUE_QUIC_IDLE_TIMEOUT, + CURL_QUIC_MAX_IDLE_MS)) { + CURL_TRC_CF(data, cf, "error setting idle timeout, "); + result = CURLE_FAILED_INIT; + goto out; + } +#endif + + SSL_set_bio(ctx->tls.ossl.ssl, bio, bio); + bio = NULL; + SSL_set_connect_state(ctx->tls.ossl.ssl); + SSL_set_incoming_stream_policy(ctx->tls.ossl.ssl, + SSL_INCOMING_STREAM_POLICY_ACCEPT, 0); + /* setup the H3 things on top of the QUIC connection */ + result = cf_osslq_h3conn_init(ctx, ctx->tls.ossl.ssl, cf); + +out: + if(bio) + BIO_free(bio); + if(baddr) + BIO_ADDR_free(baddr); + CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result); + return result; +} + +struct h3_quic_recv_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; + struct cf_osslq_stream *s; +}; + +static ssize_t h3_quic_recv(void *reader_ctx, + unsigned char *buf, size_t len, + CURLcode *err) +{ + struct h3_quic_recv_ctx *x = reader_ctx; + size_t nread; + int rv; + + *err = CURLE_OK; + rv = SSL_read_ex(x->s->ssl, buf, len, &nread); + if(rv <= 0) { + int detail = SSL_get_error(x->s->ssl, rv); + if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) { + *err = CURLE_AGAIN; + return -1; + } + else if(detail == SSL_ERROR_ZERO_RETURN) { + CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] h3_quic_recv -> EOS", + x->s->id); + x->s->recvd_eos = TRUE; + return 0; + } + else if(SSL_get_stream_read_state(x->s->ssl) == + SSL_STREAM_STATE_RESET_REMOTE) { + uint64_t app_error_code = NGHTTP3_H3_NO_ERROR; + SSL_get_stream_read_error_code(x->s->ssl, &app_error_code); + CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] h3_quic_recv -> RESET, " + "rv=%d, app_err=%" CURL_PRIu64, + x->s->id, rv, (curl_uint64_t)app_error_code); + if(app_error_code != NGHTTP3_H3_NO_ERROR) { + x->s->reset = TRUE; + } + x->s->recvd_eos = TRUE; + return 0; + } + else { + *err = cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR); + return -1; + } + } + return (ssize_t)nread; +} + +static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + ssize_t nread; + struct h3_quic_recv_ctx x; + int rv, eagain = FALSE; + size_t total_recv_len = 0; + + DEBUGASSERT(s); + if(s->closed) + return CURLE_OK; + + x.cf = cf; + x.data = data; + x.s = s; + while(s->ssl && !s->closed && !eagain && + (total_recv_len < H3_STREAM_CHUNK_SIZE)) { + if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) { + while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) { + nread = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &result); + if(nread < 0) { + if(result != CURLE_AGAIN) + goto out; + result = CURLE_OK; + eagain = TRUE; + } + } + } + + /* Forward what we have to nghttp3 */ + if(!Curl_bufq_is_empty(&s->recvbuf)) { + const unsigned char *buf; + size_t blen; + + while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) { + nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id, + buf, blen, 0); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] forward %zu bytes " + "to nghttp3 -> %zd", s->id, blen, nread); + if(nread < 0) { + failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s", + blen, nghttp3_strerror((int)nread)); + result = CURLE_RECV_ERROR; + goto out; + } + /* success, `nread` is the flow for QUIC to count as "consumed", + * not sure how that will work with OpenSSL. Anyways, without error, + * all data that we passed is not owned by nghttp3. */ + Curl_bufq_skip(&s->recvbuf, blen); + total_recv_len += blen; + } + } + + /* When we forwarded everything, handle RESET/EOS */ + if(Curl_bufq_is_empty(&s->recvbuf) && !s->closed) { + result = CURLE_OK; + if(s->reset) { + uint64_t app_error; + if(!SSL_get_stream_read_error_code(s->ssl, &app_error)) { + failf(data, "SSL_get_stream_read_error_code returned error"); + result = CURLE_RECV_ERROR; + goto out; + } + rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, app_error); + s->closed = TRUE; + if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + failf(data, "nghttp3_conn_close_stream returned error: %s", + nghttp3_strerror(rv)); + result = CURLE_RECV_ERROR; + goto out; + } + } + else if(s->recvd_eos) { + rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, + NGHTTP3_H3_NO_ERROR); + s->closed = TRUE; + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] close nghttp3 stream -> %d", + s->id, rv); + if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + failf(data, "nghttp3_conn_close_stream returned error: %s", + nghttp3_strerror(rv)); + result = CURLE_RECV_ERROR; + goto out; + } + } + } + } +out: + if(result) + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_osslq_stream_recv -> %d", + s->id, result); + return result; +} + +static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(!ctx->tls.ossl.ssl) + goto out; + + ERR_clear_error(); + + /* 1. Check for new incoming streams */ + while(1) { + SSL *snew = SSL_accept_stream(ctx->tls.ossl.ssl, + SSL_ACCEPT_STREAM_NO_BLOCK); + if(!snew) + break; + + (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data); + } + + if(!SSL_handle_events(ctx->tls.ossl.ssl)) { + int detail = SSL_get_error(ctx->tls.ossl.ssl, 0); + result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR); + } + + if(ctx->h3.conn) { + size_t i; + for(i = 0; i < ctx->h3.remote_ctrl_n; ++i) { + result = cf_osslq_stream_recv(&ctx->h3.remote_ctrl[i], cf, data); + if(result) + goto out; + } + } + + if(ctx->h3.conn) { + struct Curl_easy *sdata; + struct h3_stream_ctx *stream; + /* PULL all open streams */ + DEBUGASSERT(data->multi); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) { + stream = H3_STREAM_CTX(ctx, sdata); + if(stream && !stream->closed && + !Curl_bufq_is_full(&stream->recvbuf)) { + result = cf_osslq_stream_recv(&stream->s, cf, sdata); + if(result) + goto out; + } + } + } + } + +out: + CURL_TRC_CF(data, cf, "progress_ingress -> %d", result); + return result; +} + +/* Iterate over all streams and check if blocked can be unblocked */ +static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct Curl_easy *sdata; + struct h3_stream_ctx *stream; + + if(ctx->h3.conn) { + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if(sdata->conn == data->conn) { + stream = H3_STREAM_CTX(ctx, sdata); + if(stream && stream->s.ssl && stream->s.send_blocked && + !SSL_want_write(stream->s.ssl)) { + nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id); + stream->s.send_blocked = FALSE; + h3_drain_stream(cf, sdata); + CURL_TRC_CF(sdata, cf, "unblocked"); + } + } + } + } + return CURLE_OK; +} + +static CURLcode h3_send_streams(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(!ctx->tls.ossl.ssl || !ctx->h3.conn) + goto out; + + for(;;) { + struct cf_osslq_stream *s = NULL; + nghttp3_vec vec[16]; + nghttp3_ssize n, i; + int64_t stream_id; + size_t written; + int eos, ok, rv; + size_t total_len, acked_len = 0; + bool blocked = FALSE, eos_written = FALSE; + + n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos, + vec, ARRAYSIZE(vec)); + if(n < 0) { + failf(data, "nghttp3_conn_writev_stream returned error: %s", + nghttp3_strerror((int)n)); + result = CURLE_SEND_ERROR; + goto out; + } + if(stream_id < 0) { + result = CURLE_OK; + goto out; + } + + /* Get the stream for this data */ + s = cf_osslq_get_qstream(cf, data, stream_id); + if(!s) { + failf(data, "nghttp3_conn_writev_stream gave unknown stream %" + CURL_PRId64, (curl_int64_t)stream_id); + result = CURLE_SEND_ERROR; + goto out; + } + /* Now write the data to the stream's SSL*, it may not all fit! */ + DEBUGASSERT(s->id == stream_id); + for(i = 0, total_len = 0; i < n; ++i) { + total_len += vec[i].len; + } + for(i = 0; (i < n) && !blocked; ++i) { + /* Without stream->s.ssl, we closed that already, so + * pretend the write did succeed. */ + uint64_t flags = (eos && ((i + 1) == n))? SSL_WRITE_FLAG_CONCLUDE : 0; + written = vec[i].len; + ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags, + &written); + if(ok && flags & SSL_WRITE_FLAG_CONCLUDE) + eos_written = TRUE; + if(ok) { + /* As OpenSSL buffers the data, we count this as acknowledged + * from nghttp3's point of view */ + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send %zu bytes to QUIC ok", + s->id, vec[i].len); + acked_len += vec[i].len; + } + else { + int detail = SSL_get_error(s->ssl, 0); + switch(detail) { + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + /* QUIC blocked us from writing more */ + CURL_TRC_CF(data, cf, "[%"CURL_PRId64 "] send %zu bytes to " + "QUIC blocked", s->id, vec[i].len); + written = 0; + nghttp3_conn_block_stream(ctx->h3.conn, s->id); + s->send_blocked = blocked = TRUE; + break; + default: + failf(data, "[%"CURL_PRId64 "] send %zu bytes to QUIC, SSL error %d", + s->id, vec[i].len, detail); + result = cf_osslq_ssl_err(cf, data, detail, CURLE_HTTP3); + goto out; + } + } + } + + if(acked_len > 0 || (eos && !s->send_blocked)) { + /* Since QUIC buffers the data written internally, we can tell + * nghttp3 that it can move forward on it */ + ctx->q.last_io = Curl_now(); + rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", + nghttp3_strerror(rv)); + result = CURLE_SEND_ERROR; + goto out; + } + rv = nghttp3_conn_add_ack_offset(ctx->h3.conn, s->id, acked_len); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + failf(data, "nghttp3_conn_add_ack_offset returned error: %s\n", + nghttp3_strerror(rv)); + result = CURLE_SEND_ERROR; + goto out; + } + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] forwarded %zu/%zu h3 bytes " + "to QUIC, eos=%d", s->id, acked_len, total_len, eos); + } + + if(eos && !s->send_blocked && !eos_written) { + /* wrote everything and H3 indicates end of stream */ + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] closing QUIC stream", s->id); + SSL_stream_conclude(s->ssl, 0); + } + } + +out: + CURL_TRC_CF(data, cf, "h3_send_streams -> %d", result); + return result; +} + +static CURLcode cf_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(!ctx->tls.ossl.ssl) + goto out; + + ERR_clear_error(); + result = h3_send_streams(cf, data); + if(result) + goto out; + + if(!SSL_handle_events(ctx->tls.ossl.ssl)) { + int detail = SSL_get_error(ctx->tls.ossl.ssl, 0); + result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR); + } + + result = cf_osslq_check_and_unblock(cf, data); + +out: + CURL_TRC_CF(data, cf, "progress_egress -> %d", result); + return result; +} + +static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct timeval tv; + timediff_t timeoutms; + int is_infinite = TRUE; + + if(ctx->tls.ossl.ssl && + SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite) && + !is_infinite) { + timeoutms = curlx_tvtoms(&tv); + /* QUIC want to be called again latest at the returned timeout */ + if(timeoutms <= 0) { + result = cf_progress_ingress(cf, data); + if(result) + goto out; + result = cf_progress_egress(cf, data); + if(result) + goto out; + if(SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite)) { + timeoutms = curlx_tvtoms(&tv); + } + } + if(!is_infinite) { + Curl_expire(data, timeoutms, EXPIRE_QUIC); + CURL_TRC_CF(data, cf, "QUIC expiry in %ldms", (long)timeoutms); + } + } +out: + return result; +} + +static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct cf_call_data save; + struct curltime now; + int err; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* Connect the UDP filter first */ + if(!cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + } + + *done = FALSE; + now = Curl_now(); + CF_DATA_SAVE(save, cf, data); + + if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { + /* Not time yet to attempt the next connect */ + CURL_TRC_CF(data, cf, "waiting for reconnect time"); + goto out; + } + + if(!ctx->tls.ossl.ssl) { + ctx->started_at = now; + result = cf_osslq_ctx_start(cf, data); + if(result) + goto out; + } + + if(!ctx->got_first_byte) { + int readable = SOCKET_READABLE(ctx->q.sockfd, 0); + if(readable > 0 && (readable & CURL_CSELECT_IN)) { + ctx->got_first_byte = TRUE; + ctx->first_byte_at = Curl_now(); + } + } + + ERR_clear_error(); + err = SSL_do_handshake(ctx->tls.ossl.ssl); + + if(err == 1) { + /* connected */ + ctx->handshake_at = now; + ctx->q.last_io = now; + CURL_TRC_CF(data, cf, "handshake complete after %dms", + (int)Curl_timediff(now, ctx->started_at)); + result = cf_osslq_verify_peer(cf, data); + if(!result) { + CURL_TRC_CF(data, cf, "peer verified"); + cf->connected = TRUE; + cf->conn->alpn = CURL_HTTP_VERSION_3; + *done = TRUE; + connkeep(cf->conn, "HTTP/3 default"); + } + } + else { + int detail = SSL_get_error(ctx->tls.ossl.ssl, err); + switch(detail) { + case SSL_ERROR_WANT_READ: + ctx->q.last_io = now; + CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV"); + result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); + goto out; + case SSL_ERROR_WANT_WRITE: + ctx->q.last_io = now; + CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND"); + result = CURLE_OK; + goto out; +#ifdef SSL_ERROR_WANT_ASYNC + case SSL_ERROR_WANT_ASYNC: + ctx->q.last_io = now; + CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC"); + result = CURLE_OK; + goto out; +#endif +#ifdef SSL_ERROR_WANT_RETRY_VERIFY + case SSL_ERROR_WANT_RETRY_VERIFY: + result = CURLE_OK; + goto out; +#endif + default: + result = cf_osslq_ssl_err(cf, data, detail, CURLE_COULDNT_CONNECT); + goto out; + } + } + +out: + if(result == CURLE_RECV_ERROR && ctx->tls.ossl.ssl && + ctx->protocol_shutdown) { + /* When a QUIC server instance is shutting down, it may send us a + * CONNECTION_CLOSE right away. Our connection then enters the DRAINING + * state. The CONNECT may work in the near future again. Indicate + * that as a "weird" reply. */ + result = CURLE_WEIRD_SERVER_REPLY; + } + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(result) { + struct ip_quadruple ip; + + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); + infof(data, "QUIC connect to %s port %u failed: %s", + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); + } +#endif + if(!result) + result = check_and_set_expiry(cf, data); + if(result || *done) + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); + CF_DATA_RESTORE(cf, save); + return result; +} + +static ssize_t h3_stream_open(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t len, + CURLcode *err) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = NULL; + struct dynhds h2_headers; + size_t nheader; + nghttp3_nv *nva = NULL; + int rc = 0; + unsigned int i; + ssize_t nwritten = -1; + nghttp3_data_reader reader; + nghttp3_data_reader *preader = NULL; + + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + + *err = h3_data_setup(cf, data); + if(*err) + goto out; + stream = H3_STREAM_CTX(ctx, data); + DEBUGASSERT(stream); + if(!stream) { + *err = CURLE_FAILED_INIT; + goto out; + } + + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); + if(nwritten < 0) + goto out; + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); + + *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); + if(*err) { + nwritten = -1; + goto out; + } + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); + + nheader = Curl_dynhds_count(&h2_headers); + nva = malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + *err = CURLE_OUT_OF_MEMORY; + nwritten = -1; + goto out; + } + + for(i = 0; i < nheader; ++i) { + struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + } + + DEBUGASSERT(stream->s.id == -1); + *err = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0, + &ctx->stream_bufcp, data); + if(*err) { + failf(data, "cannot get bidi streams"); + *err = CURLE_SEND_ERROR; + goto out; + } + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: + /* known request body size or -1 */ + if(data->state.infilesize != -1) + stream->upload_left = data->state.infilesize; + else + /* data sending without specifying the data amount up front */ + stream->upload_left = -1; /* unknown */ + break; + default: + /* there is not request body */ + stream->upload_left = 0; /* no request body */ + break; + } + + stream->send_closed = (stream->upload_left == 0); + if(!stream->send_closed) { + reader.read_data = cb_h3_read_req_body; + preader = &reader; + } + + rc = nghttp3_conn_submit_request(ctx->h3.conn, stream->s.id, + nva, nheader, preader, data); + if(rc) { + switch(rc) { + case NGHTTP3_ERR_CONN_CLOSING: + CURL_TRC_CF(data, cf, "h3sid[%"CURL_PRId64"] failed to send, " + "connection is closing", stream->s.id); + break; + default: + CURL_TRC_CF(data, cf, "h3sid[%"CURL_PRId64 "] failed to send -> %d (%s)", + stream->s.id, rc, nghttp3_strerror(rc)); + break; + } + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + + if(Curl_trc_is_verbose(data)) { + infof(data, "[HTTP/3] [%" CURL_PRId64 "] OPENED stream for %s", + stream->s.id, data->state.url); + for(i = 0; i < nheader; ++i) { + infof(data, "[HTTP/3] [%" CURL_PRId64 "] [%.*s: %.*s]", + stream->s.id, + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + } + +out: + free(nva); + Curl_dynhds_free(&h2_headers); + return nwritten; +} + +static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + ssize_t nwritten; + CURLcode result; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx->tls.ossl.ssl); + DEBUGASSERT(ctx->h3.conn); + *err = CURLE_OK; + + result = cf_progress_ingress(cf, data); + if(result) { + *err = result; + nwritten = -1; + goto out; + } + + result = cf_progress_egress(cf, data); + if(result) { + *err = result; + nwritten = -1; + goto out; + } + + if(!stream || stream->s.id < 0) { + nwritten = h3_stream_open(cf, data, buf, len, err); + if(nwritten < 0) { + CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err); + goto out; + } + stream = H3_STREAM_CTX(ctx, data); + } + else if(stream->closed) { + if(stream->resp_hds_complete) { + /* Server decided to close the stream after having sent us a final + * response. This is valid if it is not interested in the request + * body. This happens on 30x or 40x responses. + * We silently discard the data sent, since this is not a transport + * error situation. */ + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] discarding data" + "on closed stream with response", stream->s.id); + *err = CURLE_OK; + nwritten = (ssize_t)len; + goto out; + } + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send_body(len=%zu) " + "-> stream closed", stream->s.id, len); + *err = CURLE_HTTP3; + nwritten = -1; + goto out; + } + else { + nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send, add to " + "sendbuf(len=%zu) -> %zd, %d", + stream->s.id, len, nwritten, *err); + if(nwritten < 0) { + goto out; + } + + (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id); + } + + result = cf_progress_egress(cf, data); + if(result) { + *err = result; + nwritten = -1; + } + +out: + result = check_and_set_expiry(cf, data); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d", + stream? stream->s.id : -1, len, nwritten, *err); + CF_DATA_RESTORE(cf, save); + return nwritten; +} + +static ssize_t recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + CURLcode *err) +{ + ssize_t nread = -1; + + (void)cf; + if(stream->reset) { + failf(data, + "HTTP/3 stream %" CURL_PRId64 " reset by server", + stream->s.id); + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; + goto out; + } + else if(!stream->resp_hds_complete) { + failf(data, + "HTTP/3 stream %" CURL_PRId64 + " was closed cleanly, but before getting" + " all response header fields, treated as error", + stream->s.id); + *err = CURLE_HTTP3; + goto out; + } + *err = CURLE_OK; + nread = 0; + +out: + return nread; +} + +static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + ssize_t nread = -1; + struct cf_call_data save; + CURLcode result; + + (void)ctx; + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx); + DEBUGASSERT(ctx->tls.ossl.ssl); + DEBUGASSERT(ctx->h3.conn); + *err = CURLE_OK; + + if(!stream) { + *err = CURLE_RECV_ERROR; + goto out; + } + + if(!Curl_bufq_is_empty(&stream->recvbuf)) { + nread = Curl_bufq_read(&stream->recvbuf, + (unsigned char *)buf, len, err); + if(nread < 0) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->s.id, len, nread, *err); + goto out; + } + } + + result = cf_progress_ingress(cf, data); + if(result) { + *err = result; + nread = -1; + goto out; + } + + /* recvbuf had nothing before, maybe after progressing ingress? */ + if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { + nread = Curl_bufq_read(&stream->recvbuf, + (unsigned char *)buf, len, err); + if(nread < 0) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->s.id, len, nread, *err); + goto out; + } + } + + if(nread > 0) { + h3_drain_stream(cf, data); + } + else { + if(stream->closed) { + nread = recv_closed_stream(cf, data, stream, err); + goto out; + } + *err = CURLE_AGAIN; + nread = -1; + } + +out: + if(cf_progress_egress(cf, data)) { + *err = CURLE_SEND_ERROR; + nread = -1; + } + else { + CURLcode result2 = check_and_set_expiry(cf, data); + if(result2) { + *err = result2; + nread = -1; + } + } + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_recv(len=%zu) -> %zd, %d", + stream? stream->s.id : -1, len, nread, *err); + CF_DATA_RESTORE(cf, save); + return nread; +} + +/* + * Called from transfer.c:data_pending to know if we should keep looping + * to receive more data from the connection. + */ +static bool cf_osslq_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + const struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)cf; + return stream && !Curl_bufq_is_empty(&stream->recvbuf); +} + +static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_SETUP: + break; + case CF_CTRL_DATA_PAUSE: + result = h3_data_pause(cf, data, (arg1 != 0)); + break; + case CF_CTRL_DATA_DETACH: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE_SEND: { + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + if(stream && !stream->send_closed) { + stream->send_closed = TRUE; + stream->upload_left = Curl_bufq_len(&stream->sendbuf) - + stream->sendbuf_len_in_flight; + (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id); + } + break; + } + case CF_CTRL_DATA_IDLE: { + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURL_TRC_CF(data, cf, "data idle"); + if(stream && !stream->closed) { + result = check_and_set_expiry(cf, data); + } + break; + } + default: + break; + } + CF_DATA_RESTORE(cf, save); + return result; +} + +static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + bool alive = FALSE; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + *input_pending = FALSE; + if(!ctx->tls.ossl.ssl) + goto out; + +#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT + /* Added in OpenSSL v3.3.x */ + { + timediff_t idletime; + uint64_t idle_ms = ctx->max_idle_ms; + if(!SSL_get_value_uint(ctx->tls.ossl.ssl, + SSL_VALUE_CLASS_FEATURE_NEGOTIATED, + SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) { + CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, " + "assume connection is dead."); + goto out; + } + CURL_TRC_CF(data, cf, "negotiated idle timeout: %zums", (size_t)idle_ms); + idletime = Curl_timediff(Curl_now(), ctx->q.last_io); + if(idletime > 0 && (uint64_t)idletime > idle_ms) + goto out; + } + +#endif + + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + goto out; + + alive = TRUE; + if(*input_pending) { + CURLcode result; + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, + only "protocol frames" */ + *input_pending = FALSE; + result = cf_progress_ingress(cf, data); + CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); + alive = result? FALSE : TRUE; + } + +out: + CF_DATA_RESTORE(cf, save); + return alive; +} + +static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + + if(!ctx->tls.ossl.ssl) { + /* NOP */ + } + else if(!cf->connected) { + /* during handshake, transfer has not started yet. we always + * add our socket for polling if SSL wants to send/recv */ + Curl_pollset_set(data, ps, ctx->q.sockfd, + SSL_net_read_desired(ctx->tls.ossl.ssl), + SSL_net_write_desired(ctx->tls.ossl.ssl)); + } + else { + /* once connected, we only modify the socket if it is present. + * this avoids adding it for paused transfers. */ + bool want_recv, want_send; + Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); + if(want_recv || want_send) { + Curl_pollset_set(data, ps, ctx->q.sockfd, + SSL_net_read_desired(ctx->tls.ossl.ssl), + SSL_net_write_desired(ctx->tls.ossl.ssl)); + } + else if(ctx->need_recv || ctx->need_send) { + Curl_pollset_set(data, ps, ctx->q.sockfd, + ctx->need_recv, ctx->need_send); + } + } +} + +static CURLcode cf_osslq_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: { +#ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL + /* Added in OpenSSL v3.3.x */ + uint64_t v; + if(!SSL_get_value_uint(ctx->tls.ossl.ssl, SSL_VALUE_CLASS_GENERIC, + SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) { + CURL_TRC_CF(data, cf, "error getting available local bidi streams"); + return CURLE_HTTP3; + } + /* we report avail + in_use */ + v += CONN_INUSE(cf->conn); + *pres1 = (v > INT_MAX)? INT_MAX : (int)v; +#else + *pres1 = 100; +#endif + CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1); + return CURLE_OK; + } + case CF_QUERY_CONNECT_REPLY_MS: + if(ctx->got_first_byte) { + timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); + *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; + } + else + *pres1 = -1; + return CURLE_OK; + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + if(ctx->got_first_byte) + *when = ctx->first_byte_at; + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +struct Curl_cftype Curl_cft_http3 = { + "HTTP/3", + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + 0, + cf_osslq_destroy, + cf_osslq_connect, + cf_osslq_close, + cf_osslq_shutdown, + Curl_cf_def_get_host, + cf_osslq_adjust_pollset, + cf_osslq_data_pending, + cf_osslq_send, + cf_osslq_recv, + cf_osslq_data_event, + cf_osslq_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_osslq_query, +}; + +CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai) +{ + struct cf_osslq_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL, *udp_cf = NULL; + CURLcode result; + + (void)data; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf_osslq_ctx_clear(ctx); + + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + + result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); + if(result) + goto out; + + cf->conn = conn; + udp_cf->conn = cf->conn; + udp_cf->sockindex = cf->sockindex; + cf->next = udp_cf; + +out: + *pcf = (!result)? cf : NULL; + if(result) { + if(udp_cf) + Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); + Curl_safefree(cf); + Curl_safefree(ctx); + } + return result; +} + +bool Curl_conn_is_osslq(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; + + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_http3) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + } + return FALSE; +} + +/* + * Store ngtcp2 version info in this buffer. + */ +void Curl_osslq_ver(char *p, size_t len) +{ + const nghttp3_info *ht3 = nghttp3_version(0); + (void)msnprintf(p, len, "nghttp3/%s", ht3->version_str); +} + +#endif /* USE_OPENSSL_QUIC && USE_NGHTTP3 */ diff --git a/third_party/curl/lib/vquic/curl_osslq.h b/third_party/curl/lib/vquic/curl_osslq.h new file mode 100644 index 0000000000..0e12d7023e --- /dev/null +++ b/third_party/curl/lib/vquic/curl_osslq.h @@ -0,0 +1,51 @@ +#ifndef HEADER_CURL_VQUIC_CURL_OSSLQ_H +#define HEADER_CURL_VQUIC_CURL_OSSLQ_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) + +#ifdef HAVE_NETINET_UDP_H +#include +#endif + +struct Curl_cfilter; + +#include "urldata.h" + +void Curl_osslq_ver(char *p, size_t len); + +CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai); + +bool Curl_conn_is_osslq(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); +#endif + +#endif /* HEADER_CURL_VQUIC_CURL_OSSLQ_H */ diff --git a/third_party/curl/lib/vquic/curl_quiche.c b/third_party/curl/lib/vquic/curl_quiche.c new file mode 100644 index 0000000000..6f01c19aa5 --- /dev/null +++ b/third_party/curl/lib/vquic/curl_quiche.c @@ -0,0 +1,1694 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_QUICHE +#include +#include +#include +#include "bufq.h" +#include "hash.h" +#include "urldata.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "sendf.h" +#include "strdup.h" +#include "rand.h" +#include "strcase.h" +#include "multiif.h" +#include "connect.h" +#include "progress.h" +#include "strerror.h" +#include "http1.h" +#include "vquic.h" +#include "vquic_int.h" +#include "vquic-tls.h" +#include "curl_quiche.h" +#include "transfer.h" +#include "inet_pton.h" +#include "vtls/openssl.h" +#include "vtls/keylog.h" +#include "vtls/vtls.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* HTTP/3 error values defined in RFC 9114, ch. 8.1 */ +#define CURL_H3_NO_ERROR (0x0100) + +#define QUIC_MAX_STREAMS (100) + +#define H3_STREAM_WINDOW_SIZE (128 * 1024) +#define H3_STREAM_CHUNK_SIZE (16 * 1024) +/* The pool keeps spares around and half of a full stream windows seems good. + * More does not seem to improve performance. The benefit of the pool is that + * stream buffer to not keep spares. Memory consumption goes down when streams + * run empty, have a large upload done, etc. */ +#define H3_STREAM_POOL_SPARES \ + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 +/* Receive and Send max number of chunks just follows from the + * chunk size and window size */ +#define H3_STREAM_RECV_CHUNKS \ + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) +#define H3_STREAM_SEND_CHUNKS \ + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) + +/* + * Store quiche version info in this buffer. + */ +void Curl_quiche_ver(char *p, size_t len) +{ + (void)msnprintf(p, len, "quiche/%s", quiche_version()); +} + +struct cf_quiche_ctx { + struct cf_quic_ctx q; + struct ssl_peer peer; + struct curl_tls_ctx tls; + quiche_conn *qconn; + quiche_config *cfg; + quiche_h3_conn *h3c; + quiche_h3_config *h3config; + uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; + struct curltime started_at; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct curltime reconnect_at; /* time the next attempt should start */ + struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */ + curl_off_t data_recvd; + BIT(goaway); /* got GOAWAY from server */ + BIT(x509_store_setup); /* if x509 store has been set up */ + BIT(shutdown_started); /* queued shutdown packets */ +}; + +#ifdef DEBUG_QUICHE +static void quiche_debug_log(const char *line, void *argp) +{ + (void)argp; + fprintf(stderr, "%s\n", line); +} +#endif + +static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) +{ + if(ctx) { + if(ctx->h3c) + quiche_h3_conn_free(ctx->h3c); + if(ctx->h3config) + quiche_h3_config_free(ctx->h3config); + if(ctx->qconn) + quiche_conn_free(ctx->qconn); + if(ctx->cfg) + quiche_config_free(ctx->cfg); + /* quiche just freed it */ + ctx->tls.ossl.ssl = NULL; + Curl_vquic_tls_cleanup(&ctx->tls); + Curl_ssl_peer_cleanup(&ctx->peer); + vquic_ctx_free(&ctx->q); + Curl_bufcp_free(&ctx->stream_bufcp); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); + + memset(ctx, 0, sizeof(*ctx)); + } +} + +static CURLcode cf_flush_egress(struct Curl_cfilter *cf, + struct Curl_easy *data); + +/** + * All about the H3 internals of a stream + */ +struct stream_ctx { + curl_uint64_t id; /* HTTP/3 protocol stream identifier */ + struct bufq recvbuf; /* h3 response */ + struct h1_req_parser h1; /* h1 request parsing */ + curl_uint64_t error3; /* HTTP/3 stream error code */ + curl_off_t upload_left; /* number of request bytes left to upload */ + BIT(opened); /* TRUE after stream has been opened */ + BIT(closed); /* TRUE on stream close */ + BIT(reset); /* TRUE on stream reset */ + BIT(send_closed); /* stream is locally closed */ + BIT(resp_hds_complete); /* final response has been received */ + BIT(resp_got_header); /* TRUE when h3 stream has recvd some HEADER */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ +}; + +#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)(\ + data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + +static void h3_stream_ctx_free(struct stream_ctx *stream) +{ + Curl_bufq_free(&stream->recvbuf); + Curl_h1_req_parse_free(&stream->h1); + free(stream); +} + +static void h3_stream_hash_free(void *stream) +{ + DEBUGASSERT(stream); + h3_stream_ctx_free((struct stream_ctx *)stream); +} + +static void check_resumes(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct Curl_easy *sdata; + struct stream_ctx *stream; + + DEBUGASSERT(data->multi); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if(sdata->conn == data->conn) { + stream = H3_STREAM_CTX(ctx, sdata); + if(stream && stream->quic_flow_blocked) { + stream->quic_flow_blocked = FALSE; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] unblock", stream->id); + } + } + } +} + +static CURLcode h3_data_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + if(stream) + return CURLE_OK; + + stream = calloc(1, sizeof(*stream)); + if(!stream) + return CURLE_OUT_OF_MEMORY; + + stream->id = -1; + Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, + H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); + + if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + h3_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result; + + (void)cf; + if(stream) { + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] easy handle is done", stream->id); + if(ctx->qconn && !stream->closed) { + quiche_conn_stream_shutdown(ctx->qconn, stream->id, + QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR); + if(!stream->send_closed) { + quiche_conn_stream_shutdown(ctx->qconn, stream->id, + QUICHE_SHUTDOWN_WRITE, CURL_H3_NO_ERROR); + stream->send_closed = TRUE; + } + stream->closed = TRUE; + result = cf_flush_egress(cf, data); + if(result) + CURL_TRC_CF(data, cf, "data_done, flush egress -> %d", result); + } + Curl_hash_offt_remove(&ctx->streams, data->id); + } +} + +static void drain_stream(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + unsigned char bits; + + (void)cf; + bits = CURL_CSELECT_IN; + if(stream && !stream->send_closed && stream->upload_left) + bits |= CURL_CSELECT_OUT; + if(data->state.select_bits != bits) { + data->state.select_bits = bits; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } +} + +static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_uint64_t stream_id, + struct stream_ctx **pstream) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct Curl_easy *sdata; + struct stream_ctx *stream; + + (void)cf; + stream = H3_STREAM_CTX(ctx, data); + if(stream && stream->id == stream_id) { + *pstream = stream; + return data; + } + else { + DEBUGASSERT(data->multi); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if(sdata->conn != data->conn) + continue; + stream = H3_STREAM_CTX(ctx, sdata); + if(stream && stream->id == stream_id) { + *pstream = stream; + return sdata; + } + } + } + *pstream = NULL; + return NULL; +} + +static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct Curl_easy *sdata; + + DEBUGASSERT(data->multi); + CURL_TRC_CF(data, cf, "conn closed, expire all transfers"); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if(sdata == data || sdata->conn != data->conn) + continue; + CURL_TRC_CF(sdata, cf, "conn closed, expire transfer"); + Curl_expire(sdata, 0, EXPIRE_RUN_NOW); + } +} + +/* + * write_resp_raw() copies response data in raw format to the `data`'s + * receive buffer. If not enough space is available, it appends to the + * `data`'s overflow buffer. + */ +static CURLcode write_resp_raw(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, size_t memlen) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result = CURLE_OK; + ssize_t nwritten; + + (void)cf; + if(!stream) + return CURLE_RECV_ERROR; + nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); + if(nwritten < 0) + return result; + + if((size_t)nwritten < memlen) { + /* This MUST not happen. Our recbuf is dimensioned to hold the + * full max_stream_window and then some for this very reason. */ + DEBUGASSERT(0); + return CURLE_RECV_ERROR; + } + return result; +} + +struct cb_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; +}; + +static int cb_each_header(uint8_t *name, size_t name_len, + uint8_t *value, size_t value_len, + void *argp) +{ + struct cb_ctx *x = argp; + struct cf_quiche_ctx *ctx = x->cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); + CURLcode result; + + if(!stream) + return CURLE_OK; + + if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) { + CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] status: %.*s", + stream->id, (int)value_len, value); + result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1); + if(!result) + result = write_resp_raw(x->cf, x->data, value, value_len); + if(!result) + result = write_resp_raw(x->cf, x->data, " \r\n", 3); + } + else { + CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] header: %.*s: %.*s", + stream->id, (int)name_len, name, + (int)value_len, value); + result = write_resp_raw(x->cf, x->data, name, name_len); + if(!result) + result = write_resp_raw(x->cf, x->data, ": ", 2); + if(!result) + result = write_resp_raw(x->cf, x->data, value, value_len); + if(!result) + result = write_resp_raw(x->cf, x->data, "\r\n", 2); + } + if(result) { + CURL_TRC_CF(x->data, x->cf, "[%"CURL_PRIu64"] on header error %d", + stream->id, result); + } + return result; +} + +static ssize_t stream_resp_read(void *reader_ctx, + unsigned char *buf, size_t len, + CURLcode *err) +{ + struct cb_ctx *x = reader_ctx; + struct cf_quiche_ctx *ctx = x->cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); + ssize_t nread; + + if(!stream) { + *err = CURLE_RECV_ERROR; + return -1; + } + + nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->id, + buf, len); + if(nread >= 0) { + *err = CURLE_OK; + return nread; + } + else { + *err = CURLE_AGAIN; + return -1; + } +} + +static CURLcode cf_recv_body(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + ssize_t nwritten; + struct cb_ctx cb_ctx; + CURLcode result = CURLE_OK; + + if(!stream) + return CURLE_RECV_ERROR; + + if(!stream->resp_hds_complete) { + result = write_resp_raw(cf, data, "\r\n", 2); + if(result) + return result; + stream->resp_hds_complete = TRUE; + } + + cb_ctx.cf = cf; + cb_ctx.data = data; + nwritten = Curl_bufq_slurp(&stream->recvbuf, + stream_resp_read, &cb_ctx, &result); + + if(nwritten < 0 && result != CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv_body error %zd", + stream->id, nwritten); + failf(data, "Error %d in HTTP/3 response body for stream[%"CURL_PRIu64"]", + result, stream->id); + stream->closed = TRUE; + stream->reset = TRUE; + stream->send_closed = TRUE; + streamclose(cf->conn, "Reset of stream"); + return result; + } + return CURLE_OK; +} + +#ifdef DEBUGBUILD +static const char *cf_ev_name(quiche_h3_event *ev) +{ + switch(quiche_h3_event_type(ev)) { + case QUICHE_H3_EVENT_HEADERS: + return "HEADERS"; + case QUICHE_H3_EVENT_DATA: + return "DATA"; + case QUICHE_H3_EVENT_RESET: + return "RESET"; + case QUICHE_H3_EVENT_FINISHED: + return "FINISHED"; + case QUICHE_H3_EVENT_GOAWAY: + return "GOAWAY"; + default: + return "Unknown"; + } +} +#else +#define cf_ev_name(x) "" +#endif + +static CURLcode h3_process_event(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct stream_ctx *stream, + quiche_h3_event *ev) +{ + struct cb_ctx cb_ctx; + CURLcode result = CURLE_OK; + int rc; + + if(!stream) + return CURLE_OK; + switch(quiche_h3_event_type(ev)) { + case QUICHE_H3_EVENT_HEADERS: + stream->resp_got_header = TRUE; + cb_ctx.cf = cf; + cb_ctx.data = data; + rc = quiche_h3_event_for_each_header(ev, cb_each_header, &cb_ctx); + if(rc) { + failf(data, "Error %d in HTTP/3 response header for stream[%" + CURL_PRIu64"]", rc, stream->id); + return CURLE_RECV_ERROR; + } + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [HEADERS]", stream->id); + break; + + case QUICHE_H3_EVENT_DATA: + if(!stream->closed) { + result = cf_recv_body(cf, data); + } + break; + + case QUICHE_H3_EVENT_RESET: + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] RESET", stream->id); + stream->closed = TRUE; + stream->reset = TRUE; + stream->send_closed = TRUE; + streamclose(cf->conn, "Reset of stream"); + break; + + case QUICHE_H3_EVENT_FINISHED: + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] CLOSED", stream->id); + if(!stream->resp_hds_complete) { + result = write_resp_raw(cf, data, "\r\n", 2); + if(result) + return result; + stream->resp_hds_complete = TRUE; + } + stream->closed = TRUE; + streamclose(cf->conn, "End of stream"); + break; + + case QUICHE_H3_EVENT_GOAWAY: + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [GOAWAY]", stream->id); + break; + + default: + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv, unhandled event %d", + stream->id, quiche_h3_event_type(ev)); + break; + } + return result; +} + +static CURLcode cf_poll_events(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = NULL; + struct Curl_easy *sdata; + quiche_h3_event *ev; + CURLcode result; + + /* Take in the events and distribute them to the transfers. */ + while(ctx->h3c) { + curl_int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev); + if(stream3_id == QUICHE_H3_ERR_DONE) { + break; + } + else if(stream3_id < 0) { + CURL_TRC_CF(data, cf, "error poll: %"CURL_PRId64, stream3_id); + return CURLE_HTTP3; + } + + sdata = get_stream_easy(cf, data, stream3_id, &stream); + if(!sdata || !stream) { + CURL_TRC_CF(data, cf, "discard event %s for unknown [%"CURL_PRId64"]", + cf_ev_name(ev), stream3_id); + } + else { + result = h3_process_event(cf, sdata, stream, ev); + drain_stream(cf, sdata); + if(result) { + CURL_TRC_CF(data, cf, "error processing event %s " + "for [%"CURL_PRIu64"] -> %d", cf_ev_name(ev), + stream3_id, result); + if(data == sdata) { + /* Only report this error to the caller if it is about the + * transfer we were called with. Otherwise we fail a transfer + * due to a problem in another one. */ + quiche_h3_event_free(ev); + return result; + } + } + quiche_h3_event_free(ev); + } + } + return CURLE_OK; +} + +struct recv_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; + int pkts; +}; + +static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, + struct sockaddr_storage *remote_addr, + socklen_t remote_addrlen, int ecn, + void *userp) +{ + struct recv_ctx *r = userp; + struct cf_quiche_ctx *ctx = r->cf->ctx; + quiche_recv_info recv_info; + ssize_t nread; + + (void)ecn; + ++r->pkts; + + recv_info.to = (struct sockaddr *)&ctx->q.local_addr; + recv_info.to_len = ctx->q.local_addrlen; + recv_info.from = (struct sockaddr *)remote_addr; + recv_info.from_len = remote_addrlen; + + nread = quiche_conn_recv(ctx->qconn, (unsigned char *)pkt, pktlen, + &recv_info); + if(nread < 0) { + if(QUICHE_ERR_DONE == nread) { + if(quiche_conn_is_draining(ctx->qconn)) { + CURL_TRC_CF(r->data, r->cf, "ingress, connection is draining"); + return CURLE_RECV_ERROR; + } + if(quiche_conn_is_closed(ctx->qconn)) { + CURL_TRC_CF(r->data, r->cf, "ingress, connection is closed"); + return CURLE_RECV_ERROR; + } + CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE"); + return CURLE_OK; + } + else if(QUICHE_ERR_TLS_FAIL == nread) { + long verify_ok = SSL_get_verify_result(ctx->tls.ossl.ssl); + if(verify_ok != X509_V_OK) { + failf(r->data, "SSL certificate problem: %s", + X509_verify_cert_error_string(verify_ok)); + return CURLE_PEER_FAILED_VERIFICATION; + } + } + else { + failf(r->data, "quiche_conn_recv() == %zd", nread); + return CURLE_RECV_ERROR; + } + } + else if((size_t)nread < pktlen) { + CURL_TRC_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes", + nread, pktlen); + } + + return CURLE_OK; +} + +static CURLcode cf_process_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct recv_ctx rctx; + CURLcode result; + + DEBUGASSERT(ctx->qconn); + result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); + if(result) + return result; + + rctx.cf = cf; + rctx.data = data; + rctx.pkts = 0; + + result = vquic_recv_packets(cf, data, &ctx->q, 1000, recv_pkt, &rctx); + if(result) + return result; + + if(rctx.pkts > 0) { + /* quiche digested ingress packets. It might have opened flow control + * windows again. */ + check_resumes(cf, data); + } + return cf_poll_events(cf, data); +} + +struct read_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; + quiche_send_info send_info; +}; + +static ssize_t read_pkt_to_send(void *userp, + unsigned char *buf, size_t buflen, + CURLcode *err) +{ + struct read_ctx *x = userp; + struct cf_quiche_ctx *ctx = x->cf->ctx; + ssize_t nwritten; + + nwritten = quiche_conn_send(ctx->qconn, buf, buflen, &x->send_info); + if(nwritten == QUICHE_ERR_DONE) { + *err = CURLE_AGAIN; + return -1; + } + + if(nwritten < 0) { + failf(x->data, "quiche_conn_send returned %zd", nwritten); + *err = CURLE_SEND_ERROR; + return -1; + } + *err = CURLE_OK; + return nwritten; +} + +/* + * flush_egress drains the buffers and sends off data. + * Calls failf() on errors. + */ +static CURLcode cf_flush_egress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + ssize_t nread; + CURLcode result; + curl_int64_t expiry_ns; + curl_int64_t timeout_ns; + struct read_ctx readx; + size_t pkt_count, gsolen; + + expiry_ns = quiche_conn_timeout_as_nanos(ctx->qconn); + if(!expiry_ns) { + quiche_conn_on_timeout(ctx->qconn); + if(quiche_conn_is_closed(ctx->qconn)) { + if(quiche_conn_is_timed_out(ctx->qconn)) + failf(data, "connection closed by idle timeout"); + else + failf(data, "connection closed by server"); + /* Connection timed out, expire all transfers belonging to it + * as will not get any more POLL events here. */ + cf_quiche_expire_conn_closed(cf, data); + return CURLE_SEND_ERROR; + } + } + + result = vquic_flush(cf, data, &ctx->q); + if(result) { + if(result == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return result; + } + + readx.cf = cf; + readx.data = data; + memset(&readx.send_info, 0, sizeof(readx.send_info)); + pkt_count = 0; + gsolen = quiche_conn_max_send_udp_payload_size(ctx->qconn); + for(;;) { + /* add the next packet to send, if any, to our buffer */ + nread = Curl_bufq_sipn(&ctx->q.sendbuf, 0, + read_pkt_to_send, &readx, &result); + if(nread < 0) { + if(result != CURLE_AGAIN) + return result; + /* Nothing more to add, flush and leave */ + result = vquic_send(cf, data, &ctx->q, gsolen); + if(result) { + if(result == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return result; + } + goto out; + } + + ++pkt_count; + if((size_t)nread < gsolen || pkt_count >= MAX_PKT_BURST) { + result = vquic_send(cf, data, &ctx->q, gsolen); + if(result) { + if(result == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + goto out; + } + pkt_count = 0; + } + } + +out: + timeout_ns = quiche_conn_timeout_as_nanos(ctx->qconn); + if(timeout_ns % 1000000) + timeout_ns += 1000000; + /* expire resolution is milliseconds */ + Curl_expire(data, (timeout_ns / 1000000), EXPIRE_QUIC); + return result; +} + +static ssize_t recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + CURLcode *err) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + ssize_t nread = -1; + + DEBUGASSERT(stream); + if(stream->reset) { + failf(data, + "HTTP/3 stream %" CURL_PRIu64 " reset by server", stream->id); + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, was reset -> %d", + stream->id, *err); + } + else if(!stream->resp_got_header) { + failf(data, + "HTTP/3 stream %" CURL_PRIu64 " was closed cleanly, but before " + "getting all response header fields, treated as error", + stream->id); + /* *err = CURLE_PARTIAL_FILE; */ + *err = CURLE_HTTP3; + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, closed incomplete" + " -> %d", stream->id, *err); + } + else { + *err = CURLE_OK; + nread = 0; + } + return nread; +} + +static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + ssize_t nread = -1; + CURLcode result; + + vquic_ctx_update_time(&ctx->q); + + if(!stream) { + *err = CURLE_RECV_ERROR; + return -1; + } + + if(!Curl_bufq_is_empty(&stream->recvbuf)) { + nread = Curl_bufq_read(&stream->recvbuf, + (unsigned char *)buf, len, err); + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->id, len, nread, *err); + if(nread < 0) + goto out; + } + + if(cf_process_ingress(cf, data)) { + CURL_TRC_CF(data, cf, "cf_recv, error on ingress"); + *err = CURLE_RECV_ERROR; + nread = -1; + goto out; + } + + /* recvbuf had nothing before, maybe after progressing ingress? */ + if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { + nread = Curl_bufq_read(&stream->recvbuf, + (unsigned char *)buf, len, err); + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->id, len, nread, *err); + if(nread < 0) + goto out; + } + + if(nread > 0) { + if(stream->closed) + drain_stream(cf, data); + } + else { + if(stream->closed) { + nread = recv_closed_stream(cf, data, err); + goto out; + } + else if(quiche_conn_is_draining(ctx->qconn)) { + failf(data, "QUIC connection is draining"); + *err = CURLE_HTTP3; + nread = -1; + goto out; + } + *err = CURLE_AGAIN; + nread = -1; + } + +out: + result = cf_flush_egress(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "cf_recv, flush egress failed"); + *err = result; + nread = -1; + } + if(nread > 0) + ctx->data_recvd += nread; + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] cf_recv(total=%" + CURL_FORMAT_CURL_OFF_T ") -> %zd, %d", + stream->id, ctx->data_recvd, nread, *err); + return nread; +} + +/* Index where :authority header field will appear in request header + field list. */ +#define AUTHORITY_DST_IDX 3 + +static ssize_t h3_open_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t len, + CURLcode *err) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + size_t nheader, i; + curl_int64_t stream3_id; + struct dynhds h2_headers; + quiche_h3_header *nva = NULL; + ssize_t nwritten; + + if(!stream) { + *err = h3_data_setup(cf, data); + if(*err) { + return -1; + } + stream = H3_STREAM_CTX(ctx, data); + DEBUGASSERT(stream); + } + + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + + DEBUGASSERT(stream); + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); + if(nwritten < 0) + goto out; + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); + + *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); + if(*err) { + nwritten = -1; + goto out; + } + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); + + nheader = Curl_dynhds_count(&h2_headers); + nva = malloc(sizeof(quiche_h3_header) * nheader); + if(!nva) { + *err = CURLE_OUT_OF_MEMORY; + nwritten = -1; + goto out; + } + + for(i = 0; i < nheader; ++i) { + struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); + nva[i].name = (unsigned char *)e->name; + nva[i].name_len = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].value_len = e->valuelen; + } + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: + if(data->state.infilesize != -1) + stream->upload_left = data->state.infilesize; + else + /* data sending without specifying the data amount up front */ + stream->upload_left = -1; /* unknown */ + break; + default: + stream->upload_left = 0; /* no request body */ + break; + } + + if(stream->upload_left == 0) + stream->send_closed = TRUE; + + stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader, + stream->send_closed); + if(stream3_id < 0) { + if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { + /* quiche seems to report this error if the connection window is + * exhausted. Which happens frequently and intermittent. */ + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] blocked", stream->id); + stream->quic_flow_blocked = TRUE; + *err = CURLE_AGAIN; + nwritten = -1; + goto out; + } + else { + CURL_TRC_CF(data, cf, "send_request(%s) -> %" CURL_PRIu64, + data->state.url, stream3_id); + } + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + + DEBUGASSERT(!stream->opened); + *err = CURLE_OK; + stream->id = stream3_id; + stream->opened = TRUE; + stream->closed = FALSE; + stream->reset = FALSE; + + if(Curl_trc_is_verbose(data)) { + infof(data, "[HTTP/3] [%" CURL_PRIu64 "] OPENED stream for %s", + stream->id, data->state.url); + for(i = 0; i < nheader; ++i) { + infof(data, "[HTTP/3] [%" CURL_PRIu64 "] [%.*s: %.*s]", stream->id, + (int)nva[i].name_len, nva[i].name, + (int)nva[i].value_len, nva[i].value); + } + } + +out: + free(nva); + Curl_dynhds_free(&h2_headers); + return nwritten; +} + +static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result; + ssize_t nwritten; + + vquic_ctx_update_time(&ctx->q); + + *err = cf_process_ingress(cf, data); + if(*err) { + nwritten = -1; + goto out; + } + + if(!stream || !stream->opened) { + nwritten = h3_open_stream(cf, data, buf, len, err); + if(nwritten < 0) + goto out; + stream = H3_STREAM_CTX(ctx, data); + } + else if(stream->closed) { + if(stream->resp_hds_complete) { + /* sending request body on a stream that has been closed by the + * server. If the server has send us a final response, we should + * silently discard the send data. + * This happens for example on redirects where the server, instead + * of reading the full request body just closed the stream after + * sending the 30x response. + * This is sort of a race: had the transfer loop called recv first, + * it would see the response and stop/discard sending on its own- */ + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] discarding data" + "on closed stream with response", stream->id); + *err = CURLE_OK; + nwritten = (ssize_t)len; + goto out; + } + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " + "-> stream closed", stream->id, len); + *err = CURLE_HTTP3; + nwritten = -1; + goto out; + } + else { + bool eof = (stream->upload_left >= 0 && + (curl_off_t)len >= stream->upload_left); + nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, + (uint8_t *)buf, len, eof); + if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) { + /* TODO: we seem to be blocked on flow control and should HOLD + * sending. But when do we open again? */ + if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " + "-> window exhausted", stream->id, len); + stream->quic_flow_blocked = TRUE; + } + *err = CURLE_AGAIN; + nwritten = -1; + goto out; + } + else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " + "-> invalid stream state", stream->id, len); + *err = CURLE_HTTP3; + nwritten = -1; + goto out; + } + else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " + "-> exceeds size", stream->id, len); + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + else if(nwritten < 0) { + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " + "-> quiche err %zd", stream->id, len, nwritten); + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + else { + /* quiche accepted all or at least a part of the buf */ + if(stream->upload_left > 0) { + stream->upload_left = (nwritten < stream->upload_left)? + (stream->upload_left - nwritten) : 0; + } + if(stream->upload_left == 0) + stream->send_closed = TRUE; + + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send body(len=%zu, " + "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd", + stream->id, len, stream->upload_left, nwritten); + *err = CURLE_OK; + } + } + +out: + result = cf_flush_egress(cf, data); + if(result) { + *err = result; + nwritten = -1; + } + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_send(len=%zu) -> %zd, %d", + stream? stream->id : (uint64_t)~0, len, nwritten, *err); + return nwritten; +} + +static bool stream_is_writeable(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + return stream && (quiche_conn_stream_writable( + ctx->qconn, (curl_uint64_t)stream->id, 1) > 0); +} + +static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + bool want_recv, want_send; + + if(!ctx->qconn) + return; + + Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); + if(want_recv || want_send) { + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + bool c_exhaust, s_exhaust; + + c_exhaust = FALSE; /* Have not found any call in quiche that tells + us if the connection itself is blocked */ + s_exhaust = want_send && stream && stream->opened && + (stream->quic_flow_blocked || !stream_is_writeable(cf, data)); + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); + + Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); + } +} + +/* + * Called from transfer.c:data_pending to know if we should keep looping + * to receive more data from the connection. + */ +static bool cf_quiche_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + const struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)cf; + return stream && !Curl_bufq_is_empty(&stream->recvbuf); +} + +static CURLcode h3_data_pause(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool pause) +{ + /* TODO: there seems right now no API in quiche to shrink/enlarge + * the streams windows. As we do in HTTP/2. */ + if(!pause) { + drain_stream(cf, data); + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + return CURLE_OK; +} + +static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_SETUP: + break; + case CF_CTRL_DATA_PAUSE: + result = h3_data_pause(cf, data, (arg1 != 0)); + break; + case CF_CTRL_DATA_DETACH: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE_SEND: { + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + if(stream && !stream->send_closed) { + unsigned char body[1]; + ssize_t sent; + + stream->send_closed = TRUE; + stream->upload_left = 0; + body[0] = 'X'; + sent = cf_quiche_send(cf, data, body, 0, &result); + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] DONE_SEND -> %zd, %d", + stream->id, sent, result); + } + break; + } + case CF_CTRL_DATA_IDLE: { + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + if(stream && !stream->closed) { + result = cf_flush_egress(cf, data); + if(result) + CURL_TRC_CF(data, cf, "data idle, flush egress -> %d", result); + } + break; + } + default: + break; + } + return result; +} + +static CURLcode cf_connect_start(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + int rv; + CURLcode result; + const struct Curl_sockaddr_ex *sockaddr; + + DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD); + +#ifdef DEBUG_QUICHE + /* initialize debug log callback only once */ + static int debug_log_init = 0; + if(!debug_log_init) { + quiche_enable_debug_logging(quiche_debug_log, NULL); + debug_log_init = 1; + } +#endif + Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, + H3_STREAM_POOL_SPARES); + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + ctx->data_recvd = 0; + + result = vquic_ctx_init(&ctx->q); + if(result) + return result; + + result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); + if(result) + return result; + + ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION); + if(!ctx->cfg) { + failf(data, "cannot create quiche config"); + return CURLE_FAILED_INIT; + } + quiche_config_enable_pacing(ctx->cfg, false); + quiche_config_set_max_idle_timeout(ctx->cfg, CURL_QUIC_MAX_IDLE_MS); + quiche_config_set_initial_max_data(ctx->cfg, (1 * 1024 * 1024) + /* (QUIC_MAX_STREAMS/2) * H3_STREAM_WINDOW_SIZE */); + quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS); + quiche_config_set_initial_max_streams_uni(ctx->cfg, QUIC_MAX_STREAMS); + quiche_config_set_initial_max_stream_data_bidi_local(ctx->cfg, + H3_STREAM_WINDOW_SIZE); + quiche_config_set_initial_max_stream_data_bidi_remote(ctx->cfg, + H3_STREAM_WINDOW_SIZE); + quiche_config_set_initial_max_stream_data_uni(ctx->cfg, + H3_STREAM_WINDOW_SIZE); + quiche_config_set_disable_active_migration(ctx->cfg, TRUE); + + quiche_config_set_max_connection_window(ctx->cfg, + 10 * QUIC_MAX_STREAMS * H3_STREAM_WINDOW_SIZE); + quiche_config_set_max_stream_window(ctx->cfg, 10 * H3_STREAM_WINDOW_SIZE); + quiche_config_set_application_protos(ctx->cfg, + (uint8_t *) + QUICHE_H3_APPLICATION_PROTOCOL, + sizeof(QUICHE_H3_APPLICATION_PROTOCOL) + - 1); + + result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, + QUICHE_H3_APPLICATION_PROTOCOL, + sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1, + NULL, NULL, cf); + if(result) + return result; + + result = Curl_rand(data, ctx->scid, sizeof(ctx->scid)); + if(result) + return result; + + Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL); + ctx->q.local_addrlen = sizeof(ctx->q.local_addr); + rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, + &ctx->q.local_addrlen); + if(rv == -1) + return CURLE_QUIC_CONNECT_ERROR; + + ctx->qconn = quiche_conn_new_with_tls((const uint8_t *)ctx->scid, + sizeof(ctx->scid), NULL, 0, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen, + &sockaddr->sa_addr, sockaddr->addrlen, + ctx->cfg, ctx->tls.ossl.ssl, false); + if(!ctx->qconn) { + failf(data, "cannot create quiche connection"); + return CURLE_OUT_OF_MEMORY; + } + + /* Known to not work on Windows */ +#if !defined(_WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) + { + int qfd; + (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd); + if(qfd != -1) + quiche_conn_set_qlog_fd(ctx->qconn, qfd, + "qlog title", "curl qlog"); + } +#endif + + result = cf_flush_egress(cf, data); + if(result) + return result; + + { + unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL; + unsigned alpn_len, offset = 0; + + /* Replace each ALPN length prefix by a comma. */ + while(offset < sizeof(alpn_protocols) - 1) { + alpn_len = alpn_protocols[offset]; + alpn_protocols[offset] = ','; + offset += 1 + alpn_len; + } + + CURL_TRC_CF(data, cf, "Sent QUIC client Initial, ALPN: %s", + alpn_protocols + 1); + } + + return CURLE_OK; +} + +static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + + cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + cf->conn->httpversion = 30; + cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; + + return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); +} + +static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* Connect the UDP filter first */ + if(!cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + } + + *done = FALSE; + vquic_ctx_update_time(&ctx->q); + + if(ctx->reconnect_at.tv_sec && + Curl_timediff(ctx->q.last_op, ctx->reconnect_at) < 0) { + /* Not time yet to attempt the next connect */ + CURL_TRC_CF(data, cf, "waiting for reconnect time"); + goto out; + } + + if(!ctx->qconn) { + result = cf_connect_start(cf, data); + if(result) + goto out; + ctx->started_at = ctx->q.last_op; + result = cf_flush_egress(cf, data); + /* we do not expect to be able to recv anything yet */ + goto out; + } + + result = cf_process_ingress(cf, data); + if(result) + goto out; + + result = cf_flush_egress(cf, data); + if(result) + goto out; + + if(quiche_conn_is_established(ctx->qconn)) { + ctx->handshake_at = ctx->q.last_op; + CURL_TRC_CF(data, cf, "handshake complete after %dms", + (int)Curl_timediff(ctx->handshake_at, ctx->started_at)); + result = cf_quiche_verify_peer(cf, data); + if(!result) { + CURL_TRC_CF(data, cf, "peer verified"); + ctx->h3config = quiche_h3_config_new(); + if(!ctx->h3config) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + /* Create a new HTTP/3 connection on the QUIC connection. */ + ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config); + if(!ctx->h3c) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf->connected = TRUE; + cf->conn->alpn = CURL_HTTP_VERSION_3; + *done = TRUE; + connkeep(cf->conn, "HTTP/3 default"); + } + } + else if(quiche_conn_is_draining(ctx->qconn)) { + /* When a QUIC server instance is shutting down, it may send us a + * CONNECTION_CLOSE right away. Our connection then enters the DRAINING + * state. The CONNECT may work in the near future again. Indicate + * that as a "weird" reply. */ + result = CURLE_WEIRD_SERVER_REPLY; + } + +out: +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(result && result != CURLE_AGAIN) { + struct ip_quadruple ip; + + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); + infof(data, "connect to %s port %u failed: %s", + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); + } +#endif + return result; +} + +static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(cf->shutdown || !ctx || !ctx->qconn) { + *done = TRUE; + return CURLE_OK; + } + + *done = FALSE; + if(!ctx->shutdown_started) { + int err; + + ctx->shutdown_started = TRUE; + vquic_ctx_update_time(&ctx->q); + err = quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0); + if(err) { + CURL_TRC_CF(data, cf, "error %d adding shutdown packet, " + "aborting shutdown", err); + result = CURLE_SEND_ERROR; + goto out; + } + } + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); + result = cf_flush_egress(cf, data); + if(result) + goto out; + } + + if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* sent everything, quiche does not seem to support a graceful + * shutdown waiting for a reply, so ware done. */ + CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); + *done = TRUE; + } + else { + CURL_TRC_CF(data, cf, "shutdown sending blocked"); + } + +out: + return result; +} + +static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + + if(ctx) { + bool done; + (void)cf_quiche_shutdown(cf, data, &done); + cf_quiche_ctx_clear(ctx); + } +} + +static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + + (void)data; + cf_quiche_ctx_clear(ctx); + free(ctx); + cf->ctx = NULL; +} + +static CURLcode cf_quiche_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: { + curl_uint64_t max_streams = CONN_INUSE(cf->conn); + if(!ctx->goaway) { + max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn); + } + *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams; + CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: " + "MAX_CONCURRENT -> %d (%zu in use)", + cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn)); + return CURLE_OK; + } + case CF_QUERY_CONNECT_REPLY_MS: + if(ctx->q.got_first_byte) { + timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at); + *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; + } + else + *pres1 = -1; + return CURLE_OK; + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + if(ctx->q.got_first_byte) + *when = ctx->q.first_byte_at; + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + bool alive = TRUE; + + *input_pending = FALSE; + if(!ctx->qconn) + return FALSE; + + if(quiche_conn_is_closed(ctx->qconn)) { + if(quiche_conn_is_timed_out(ctx->qconn)) + CURL_TRC_CF(data, cf, "connection was closed due to idle timeout"); + else + CURL_TRC_CF(data, cf, "connection is closed"); + return FALSE; + } + + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + return FALSE; + + if(*input_pending) { + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, + only "protocol frames" */ + *input_pending = FALSE; + if(cf_process_ingress(cf, data)) + alive = FALSE; + else { + alive = TRUE; + } + } + + return alive; +} + +struct Curl_cftype Curl_cft_http3 = { + "HTTP/3", + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + 0, + cf_quiche_destroy, + cf_quiche_connect, + cf_quiche_close, + cf_quiche_shutdown, + Curl_cf_def_get_host, + cf_quiche_adjust_pollset, + cf_quiche_data_pending, + cf_quiche_send, + cf_quiche_recv, + cf_quiche_data_event, + cf_quiche_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_quiche_query, +}; + +CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai) +{ + struct cf_quiche_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL, *udp_cf = NULL; + CURLcode result; + + (void)data; + (void)conn; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + + result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); + if(result) + goto out; + + udp_cf->conn = cf->conn; + udp_cf->sockindex = cf->sockindex; + cf->next = udp_cf; + +out: + *pcf = (!result)? cf : NULL; + if(result) { + if(udp_cf) + Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); + Curl_safefree(cf); + Curl_safefree(ctx); + } + + return result; +} + +bool Curl_conn_is_quiche(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; + + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_http3) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + } + return FALSE; +} + +#endif diff --git a/third_party/curl/lib/vquic/curl_quiche.h b/third_party/curl/lib/vquic/curl_quiche.h new file mode 100644 index 0000000000..bce781c1bc --- /dev/null +++ b/third_party/curl/lib/vquic/curl_quiche.h @@ -0,0 +1,50 @@ +#ifndef HEADER_CURL_VQUIC_CURL_QUICHE_H +#define HEADER_CURL_VQUIC_CURL_QUICHE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_QUICHE + +#include +#include + +struct Curl_cfilter; +struct Curl_easy; + +void Curl_quiche_ver(char *p, size_t len); + +CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai); + +bool Curl_conn_is_quiche(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); + +#endif + +#endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */ diff --git a/third_party/curl/lib/vquic/vquic-tls.c b/third_party/curl/lib/vquic/vquic-tls.c new file mode 100644 index 0000000000..6aa6613aff --- /dev/null +++ b/third_party/curl/lib/vquic/vquic-tls.c @@ -0,0 +1,347 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_HTTP3) && \ + (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) + +#ifdef USE_OPENSSL +#include +#include "vtls/openssl.h" +#elif defined(USE_GNUTLS) +#include +#include +#include +#include +#include +#include "vtls/gtls.h" +#elif defined(USE_WOLFSSL) +#include +#include +#include +#include "vtls/wolfssl.h" +#endif + +#include "urldata.h" +#include "curl_trc.h" +#include "cfilters.h" +#include "multiif.h" +#include "vtls/keylog.h" +#include "vtls/vtls.h" +#include "vquic-tls.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#if defined(USE_WOLFSSL) + +#define QUIC_CIPHERS \ + "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ + "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" +#define QUIC_GROUPS "P-256:P-384:P-521" + +#if defined(HAVE_SECRET_CALLBACK) +static void keylog_callback(const WOLFSSL *ssl, const char *line) +{ + (void)ssl; + Curl_tls_keylog_write_line(line); +} +#endif + +static CURLcode Curl_wssl_init_ctx(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data) +{ + struct ssl_primary_config *conn_config; + CURLcode result = CURLE_FAILED_INIT; + + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) { + result = CURLE_FAILED_INIT; + goto out; + } + + ctx->wssl.ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + if(!ctx->wssl.ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(cb_setup) { + result = cb_setup(cf, data, cb_user_data); + if(result) + goto out; + } + + wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx); + + if(wolfSSL_CTX_set_cipher_list(ctx->wssl.ctx, conn_config->cipher_list13 ? + conn_config->cipher_list13 : + QUIC_CIPHERS) != 1) { + char error_buffer[256]; + ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); + failf(data, "wolfSSL failed to set ciphers: %s", error_buffer); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + if(wolfSSL_CTX_set1_groups_list(ctx->wssl.ctx, conn_config->curves ? + conn_config->curves : + (char *)QUIC_GROUPS) != 1) { + failf(data, "wolfSSL failed to set curves"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { +#if defined(HAVE_SECRET_CALLBACK) + wolfSSL_CTX_set_keylog_callback(ctx->wssl.ctx, keylog_callback); +#else + failf(data, "wolfSSL was built without keylog callback"); + result = CURLE_NOT_BUILT_IN; + goto out; +#endif + } + + if(conn_config->verifypeer) { + const char * const ssl_cafile = conn_config->CAfile; + const char * const ssl_capath = conn_config->CApath; + + wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_PEER, NULL); + if(ssl_cafile || ssl_capath) { + /* tell wolfSSL where to find CA certificates that are used to verify + the server's certificate. */ + int rc = + wolfSSL_CTX_load_verify_locations_ex(ctx->wssl.ctx, ssl_cafile, + ssl_capath, + WOLFSSL_LOAD_FLAG_IGNORE_ERR); + if(SSL_SUCCESS != rc) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + result = CURLE_SSL_CACERT_BADFILE; + goto out; + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } +#ifdef CURL_CA_FALLBACK + else { + /* verifying the peer without any CA certificates will not work so + use wolfssl's built-in default as fallback */ + wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx); + } +#endif + } + else { + wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_NONE, NULL); + } + + /* give application a chance to interfere with SSL set up. */ + if(data->set.ssl.fsslctx) { + Curl_set_in_callback(data, true); + result = (*data->set.ssl.fsslctx)(data, ctx->wssl.ctx, + data->set.ssl.fsslctxp); + Curl_set_in_callback(data, false); + if(result) { + failf(data, "error signaled by ssl ctx callback"); + goto out; + } + } + result = CURLE_OK; + +out: + if(result && ctx->wssl.ctx) { + SSL_CTX_free(ctx->wssl.ctx); + ctx->wssl.ctx = NULL; + } + return result; +} + +/** SSL callbacks ***/ + +static CURLcode Curl_wssl_init_ssl(struct curl_tls_ctx *ctx, + struct Curl_easy *data, + struct ssl_peer *peer, + const char *alpn, size_t alpn_len, + void *user_data) +{ + (void)data; + DEBUGASSERT(!ctx->wssl.handle); + DEBUGASSERT(ctx->wssl.ctx); + ctx->wssl.handle = wolfSSL_new(ctx->wssl.ctx); + + wolfSSL_set_app_data(ctx->wssl.handle, user_data); + wolfSSL_set_connect_state(ctx->wssl.handle); + wolfSSL_set_quic_use_legacy_codepoint(ctx->wssl.handle, 0); + + if(alpn) + wolfSSL_set_alpn_protos(ctx->wssl.handle, (const unsigned char *)alpn, + (unsigned int)alpn_len); + + if(peer->sni) { + wolfSSL_UseSNI(ctx->wssl.handle, WOLFSSL_SNI_HOST_NAME, + peer->sni, (unsigned short)strlen(peer->sni)); + } + + return CURLE_OK; +} +#endif /* defined(USE_WOLFSSL) */ + +CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const char *alpn, size_t alpn_len, + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data, void *ssl_user_data) +{ + CURLcode result; + +#ifdef USE_OPENSSL + (void)result; + return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, TRNSPRT_QUIC, + (const unsigned char *)alpn, alpn_len, + cb_setup, cb_user_data, NULL, ssl_user_data); +#elif defined(USE_GNUTLS) + (void)result; + return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, + (const unsigned char *)alpn, alpn_len, + cb_setup, cb_user_data, ssl_user_data); +#elif defined(USE_WOLFSSL) + result = Curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data); + if(result) + return result; + + return Curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data); +#else +#error "no TLS lib in used, should not happen" + return CURLE_FAILED_INIT; +#endif +} + +void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx) +{ +#ifdef USE_OPENSSL + if(ctx->ossl.ssl) + SSL_free(ctx->ossl.ssl); + if(ctx->ossl.ssl_ctx) + SSL_CTX_free(ctx->ossl.ssl_ctx); +#elif defined(USE_GNUTLS) + if(ctx->gtls.session) + gnutls_deinit(ctx->gtls.session); + Curl_gtls_shared_creds_free(&ctx->gtls.shared_creds); +#elif defined(USE_WOLFSSL) + if(ctx->wssl.handle) + wolfSSL_free(ctx->wssl.handle); + if(ctx->wssl.ctx) + wolfSSL_CTX_free(ctx->wssl.ctx); +#endif + memset(ctx, 0, sizeof(*ctx)); +} + +CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ +#ifdef USE_OPENSSL + if(!ctx->ossl.x509_store_setup) { + CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ossl.ssl_ctx); + if(result) + return result; + ctx->ossl.x509_store_setup = TRUE; + } +#elif defined(USE_WOLFSSL) + if(!ctx->wssl.x509_store_setup) { + CURLcode result = Curl_wssl_setup_x509_store(cf, data, &ctx->wssl); + if(result) + return result; + } +#elif defined(USE_GNUTLS) + if(!ctx->gtls.shared_creds->trust_setup) { + CURLcode result = Curl_gtls_client_trust_setup(cf, data, &ctx->gtls); + if(result) + return result; + } +#else + (void)ctx; (void)cf; (void)data; +#endif + return CURLE_OK; +} + +CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer) +{ + struct ssl_primary_config *conn_config; + CURLcode result = CURLE_OK; + + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; + +#ifdef USE_OPENSSL + (void)conn_config; + result = Curl_oss_check_peer_cert(cf, data, &ctx->ossl, peer); +#elif defined(USE_GNUTLS) + if(conn_config->verifyhost) { + result = Curl_gtls_verifyserver(data, ctx->gtls.session, + conn_config, &data->set.ssl, peer, + data->set.str[STRING_SSL_PINNEDPUBLICKEY]); + if(result) + return result; + } +#elif defined(USE_WOLFSSL) + (void)data; + if(conn_config->verifyhost) { + if(peer->sni) { + WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.handle); + if(wolfSSL_X509_check_host(cert, peer->sni, strlen(peer->sni), 0, NULL) + == WOLFSSL_FAILURE) { + result = CURLE_PEER_FAILED_VERIFICATION; + } + wolfSSL_X509_free(cert); + } + + } +#endif + return result; +} + + +#endif /* !USE_HTTP3 && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */ diff --git a/third_party/curl/lib/vquic/vquic-tls.h b/third_party/curl/lib/vquic/vquic-tls.h new file mode 100644 index 0000000000..0ec74bfbae --- /dev/null +++ b/third_party/curl/lib/vquic/vquic-tls.h @@ -0,0 +1,100 @@ +#ifndef HEADER_CURL_VQUIC_TLS_H +#define HEADER_CURL_VQUIC_TLS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "bufq.h" +#include "vtls/openssl.h" + +#if defined(USE_HTTP3) && \ + (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) + +#include "vtls/wolfssl.h" + +struct curl_tls_ctx { +#ifdef USE_OPENSSL + struct ossl_ctx ossl; +#elif defined(USE_GNUTLS) + struct gtls_ctx gtls; +#elif defined(USE_WOLFSSL) + struct wolfssl_ctx wssl; +#endif +}; + +/** + * Callback passed to `Curl_vquic_tls_init()` that can + * do early initializations on the not otherwise configured TLS + * instances created. This varies by TLS backend: + * - openssl/wolfssl: SSL_CTX* has just been created + * - gnutls: gtls_client_init() has run + */ +typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *cb_user_data); + +/** + * Initialize the QUIC TLS instances based of the SSL configurations + * for the connection filter, transfer and peer. + * @param ctx the TLS context to initialize + * @param cf the connection filter involved + * @param data the transfer involved + * @param peer the peer that will be connected to + * @param alpn the ALPN string in protocol format ((len+bytes+)+), + * may be NULL + * @param alpn_len the overall number of bytes in `alpn` + * @param cb_setup optional callback for very early TLS config + ± @param cb_user_data user_data param for callback + * @param ssl_user_data optional pointer to set in TLS application context + */ +CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const char *alpn, size_t alpn_len, + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data, + void *ssl_user_data); + +/** + * Cleanup all data that has been initialized. + */ +void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx); + +CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data); + +/** + * After the QUIC basic handshake has been, verify that the peer + * (and its certificate) fulfill our requirements. + */ +CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer); + +#endif /* !USE_HTTP3 && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */ + +#endif /* HEADER_CURL_VQUIC_TLS_H */ diff --git a/third_party/curl/lib/vquic/vquic.c b/third_party/curl/lib/vquic/vquic.c new file mode 100644 index 0000000000..2bb06d463b --- /dev/null +++ b/third_party/curl/lib/vquic/vquic.c @@ -0,0 +1,755 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* WIP, experimental: use recvmmsg() on linux + * we have no configure check, yet + * and also it is only available for _GNU_SOURCE, which + * we do not use otherwise. +#define HAVE_SENDMMSG + */ +#if defined(HAVE_SENDMMSG) +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#endif + +#include "curl_setup.h" + +#ifdef HAVE_NETINET_UDP_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#include "urldata.h" +#include "bufq.h" +#include "dynbuf.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "curl_msh3.h" +#include "curl_ngtcp2.h" +#include "curl_osslq.h" +#include "curl_quiche.h" +#include "rand.h" +#include "vquic.h" +#include "vquic_int.h" +#include "strerror.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +#ifdef USE_HTTP3 + +#ifdef O_BINARY +#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY +#else +#define QLOGMODE O_WRONLY|O_CREAT +#endif + +#define NW_CHUNK_SIZE (64 * 1024) +#define NW_SEND_CHUNKS 2 + + +void Curl_quic_ver(char *p, size_t len) +{ +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + Curl_ngtcp2_ver(p, len); +#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) + Curl_osslq_ver(p, len); +#elif defined(USE_QUICHE) + Curl_quiche_ver(p, len); +#elif defined(USE_MSH3) + Curl_msh3_ver(p, len); +#endif +} + +CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) +{ + Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); +#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG) + qctx->no_gso = FALSE; +#else + qctx->no_gso = TRUE; +#endif +#ifdef DEBUGBUILD + { + char *p = getenv("CURL_DBG_QUIC_WBLOCK"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0 && l <= 100) + qctx->wblock_percent = (int)l; + } + } +#endif + vquic_ctx_update_time(qctx); + + return CURLE_OK; +} + +void vquic_ctx_free(struct cf_quic_ctx *qctx) +{ + Curl_bufq_free(&qctx->sendbuf); +} + +void vquic_ctx_update_time(struct cf_quic_ctx *qctx) +{ + qctx->last_op = Curl_now(); +} + +static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, + size_t gsolen, size_t *psent); + +static CURLcode do_sendmsg(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, size_t gsolen, + size_t *psent) +{ +#ifdef HAVE_SENDMSG + struct iovec msg_iov; + struct msghdr msg = {0}; + ssize_t sent; +#if defined(__linux__) && defined(UDP_SEGMENT) + uint8_t msg_ctrl[32]; + struct cmsghdr *cm; +#endif + + *psent = 0; + msg_iov.iov_base = (uint8_t *)pkt; + msg_iov.iov_len = pktlen; + msg.msg_iov = &msg_iov; + msg.msg_iovlen = 1; + +#if defined(__linux__) && defined(UDP_SEGMENT) + if(pktlen > gsolen) { + /* Only set this, when we need it. macOS, for example, + * does not seem to like a msg_control of length 0. */ + msg.msg_control = msg_ctrl; + assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t))); + msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); + cm = CMSG_FIRSTHDR(&msg); + cm->cmsg_level = SOL_UDP; + cm->cmsg_type = UDP_SEGMENT; + cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); + *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff; + } +#endif + + + while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR) + ; + + if(sent == -1) { + switch(SOCKERRNO) { + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + return CURLE_AGAIN; + case EMSGSIZE: + /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */ + break; + case EIO: + if(pktlen > gsolen) { + /* GSO failure */ + failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent, + SOCKERRNO); + qctx->no_gso = TRUE; + return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); + } + FALLTHROUGH(); + default: + failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO); + return CURLE_SEND_ERROR; + } + } + else { + assert(pktlen == (size_t)sent); + } +#else + ssize_t sent; + (void)gsolen; + + *psent = 0; + + while((sent = send(qctx->sockfd, + (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 && + SOCKERRNO == EINTR) + ; + + if(sent == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + return CURLE_AGAIN; + } + else { + failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO); + if(SOCKERRNO != EMSGSIZE) { + return CURLE_SEND_ERROR; + } + /* UDP datagram is too large; caused by PMTUD. Just let it be + lost. */ + } + } +#endif + (void)cf; + *psent = pktlen; + + return CURLE_OK; +} + +static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, + size_t gsolen, size_t *psent) +{ + const uint8_t *p, *end = pkt + pktlen; + size_t sent; + + *psent = 0; + + for(p = pkt; p < end; p += gsolen) { + size_t len = CURLMIN(gsolen, (size_t)(end - p)); + CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent); + if(curlcode != CURLE_OK) { + return curlcode; + } + *psent += sent; + } + + return CURLE_OK; +} + +static CURLcode vquic_send_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, + size_t gsolen, size_t *psent) +{ + CURLcode result; +#ifdef DEBUGBUILD + /* simulate network blocking/partial writes */ + if(qctx->wblock_percent > 0) { + unsigned char c; + Curl_rand(data, &c, 1); + if(c >= ((100-qctx->wblock_percent)*256/100)) { + CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK"); + return CURLE_AGAIN; + } + } +#endif + if(qctx->no_gso && pktlen > gsolen) { + result = send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); + } + else { + result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); + } + if(!result) + qctx->last_io = qctx->last_op; + return result; +} + +CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx) +{ + const unsigned char *buf; + size_t blen, sent; + CURLcode result; + size_t gsolen; + + while(Curl_bufq_peek(&qctx->sendbuf, &buf, &blen)) { + gsolen = qctx->gsolen; + if(qctx->split_len) { + gsolen = qctx->split_gsolen; + if(blen > qctx->split_len) + blen = qctx->split_len; + } + + result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); + CURL_TRC_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu", + blen, gsolen, result, sent); + if(result) { + if(result == CURLE_AGAIN) { + Curl_bufq_skip(&qctx->sendbuf, sent); + if(qctx->split_len) + qctx->split_len -= sent; + } + return result; + } + Curl_bufq_skip(&qctx->sendbuf, sent); + if(qctx->split_len) + qctx->split_len -= sent; + } + return CURLE_OK; +} + +CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx, size_t gsolen) +{ + qctx->gsolen = gsolen; + return vquic_flush(cf, data, qctx); +} + +CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx, size_t gsolen, + size_t tail_len, size_t tail_gsolen) +{ + DEBUGASSERT(Curl_bufq_len(&qctx->sendbuf) > tail_len); + qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len; + qctx->split_gsolen = gsolen; + qctx->gsolen = tail_gsolen; + CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", + qctx->split_len, qctx->split_gsolen, + tail_len, qctx->gsolen); + return vquic_flush(cf, data, qctx); +} + +#if defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG) +static size_t msghdr_get_udp_gro(struct msghdr *msg) +{ + int gso_size = 0; +#if defined(__linux__) && defined(UDP_GRO) + struct cmsghdr *cmsg; + + /* Workaround musl CMSG_NXTHDR issue */ +#ifndef __GLIBC__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wcast-align" +#endif + for(cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { +#ifndef __GLIBC__ +#pragma clang diagnostic pop +#endif + if(cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) { + memcpy(&gso_size, CMSG_DATA(cmsg), sizeof(gso_size)); + + break; + } + } +#endif + (void)msg; + + return (size_t)gso_size; +} +#endif + +#ifdef HAVE_SENDMMSG +static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + vquic_recv_pkt_cb *recv_cb, void *userp) +{ +#define MMSG_NUM 64 + struct iovec msg_iov[MMSG_NUM]; + struct mmsghdr mmsg[MMSG_NUM]; + uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(uint16_t))]; + uint8_t bufs[MMSG_NUM][2*1024]; + struct sockaddr_storage remote_addr[MMSG_NUM]; + size_t total_nread, pkts; + int mcount, i, n; + char errstr[STRERROR_LEN]; + CURLcode result = CURLE_OK; + size_t gso_size; + size_t pktlen; + size_t offset, to; + + DEBUGASSERT(max_pkts > 0); + pkts = 0; + total_nread = 0; + while(pkts < max_pkts) { + n = (int)CURLMIN(MMSG_NUM, max_pkts); + memset(&mmsg, 0, sizeof(mmsg)); + for(i = 0; i < n; ++i) { + msg_iov[i].iov_base = bufs[i]; + msg_iov[i].iov_len = (int)sizeof(bufs[i]); + mmsg[i].msg_hdr.msg_iov = &msg_iov[i]; + mmsg[i].msg_hdr.msg_iovlen = 1; + mmsg[i].msg_hdr.msg_name = &remote_addr[i]; + mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]); + mmsg[i].msg_hdr.msg_control = &msg_ctrl[i]; + mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); + } + + while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 && + SOCKERRNO == EINTR) + ; + if(mcount == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN"); + goto out; + } + if(!cf->connected && SOCKERRNO == ECONNREFUSED) { + struct ip_quadruple ip; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); + failf(data, "QUIC: connection to %s port %u refused", + ip.remote_ip, ip.remote_port); + result = CURLE_COULDNT_CONNECT; + goto out; + } + Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d; %s)", + mcount, SOCKERRNO, errstr); + result = CURLE_RECV_ERROR; + goto out; + } + + CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount); + for(i = 0; i < mcount; ++i) { + total_nread += mmsg[i].msg_len; + + gso_size = msghdr_get_udp_gro(&mmsg[i].msg_hdr); + if(gso_size == 0) { + gso_size = mmsg[i].msg_len; + } + + for(offset = 0; offset < mmsg[i].msg_len; offset = to) { + ++pkts; + + to = offset + gso_size; + if(to > mmsg[i].msg_len) { + pktlen = mmsg[i].msg_len - offset; + } + else { + pktlen = gso_size; + } + + result = recv_cb(bufs[i] + offset, pktlen, mmsg[i].msg_hdr.msg_name, + mmsg[i].msg_hdr.msg_namelen, 0, userp); + if(result) + goto out; + } + } + } + +out: + if(total_nread || result) + CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", + pkts, total_nread, result); + return result; +} + +#elif defined(HAVE_SENDMSG) +static CURLcode recvmsg_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + vquic_recv_pkt_cb *recv_cb, void *userp) +{ + struct iovec msg_iov; + struct msghdr msg; + uint8_t buf[64*1024]; + struct sockaddr_storage remote_addr; + size_t total_nread, pkts; + ssize_t nread; + char errstr[STRERROR_LEN]; + CURLcode result = CURLE_OK; + uint8_t msg_ctrl[CMSG_SPACE(sizeof(uint16_t))]; + size_t gso_size; + size_t pktlen; + size_t offset, to; + + msg_iov.iov_base = buf; + msg_iov.iov_len = (int)sizeof(buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &msg_iov; + msg.msg_iovlen = 1; + msg.msg_control = msg_ctrl; + + DEBUGASSERT(max_pkts > 0); + for(pkts = 0, total_nread = 0; pkts < max_pkts;) { + msg.msg_name = &remote_addr; + msg.msg_namelen = sizeof(remote_addr); + msg.msg_controllen = sizeof(msg_ctrl); + while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 && + SOCKERRNO == EINTR) + ; + if(nread == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + goto out; + } + if(!cf->connected && SOCKERRNO == ECONNREFUSED) { + struct ip_quadruple ip; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); + failf(data, "QUIC: connection to %s port %u refused", + ip.remote_ip, ip.remote_port); + result = CURLE_COULDNT_CONNECT; + goto out; + } + Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)", + nread, SOCKERRNO, errstr); + result = CURLE_RECV_ERROR; + goto out; + } + + total_nread += (size_t)nread; + + gso_size = msghdr_get_udp_gro(&msg); + if(gso_size == 0) { + gso_size = (size_t)nread; + } + + for(offset = 0; offset < (size_t)nread; offset = to) { + ++pkts; + + to = offset + gso_size; + if(to > (size_t)nread) { + pktlen = (size_t)nread - offset; + } + else { + pktlen = gso_size; + } + + result = + recv_cb(buf + offset, pktlen, msg.msg_name, msg.msg_namelen, 0, userp); + if(result) + goto out; + } + } + +out: + if(total_nread || result) + CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", + pkts, total_nread, result); + return result; +} + +#else /* HAVE_SENDMMSG || HAVE_SENDMSG */ +static CURLcode recvfrom_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + vquic_recv_pkt_cb *recv_cb, void *userp) +{ + uint8_t buf[64*1024]; + int bufsize = (int)sizeof(buf); + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen = sizeof(remote_addr); + size_t total_nread, pkts; + ssize_t nread; + char errstr[STRERROR_LEN]; + CURLcode result = CURLE_OK; + + DEBUGASSERT(max_pkts > 0); + for(pkts = 0, total_nread = 0; pkts < max_pkts;) { + while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0, + (struct sockaddr *)&remote_addr, + &remote_addrlen)) == -1 && + SOCKERRNO == EINTR) + ; + if(nread == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN"); + goto out; + } + if(!cf->connected && SOCKERRNO == ECONNREFUSED) { + struct ip_quadruple ip; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); + failf(data, "QUIC: connection to %s port %u refused", + ip.remote_ip, ip.remote_port); + result = CURLE_COULDNT_CONNECT; + goto out; + } + Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)", + nread, SOCKERRNO, errstr); + result = CURLE_RECV_ERROR; + goto out; + } + + ++pkts; + total_nread += (size_t)nread; + result = recv_cb(buf, (size_t)nread, &remote_addr, remote_addrlen, + 0, userp); + if(result) + goto out; + } + +out: + if(total_nread || result) + CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", + pkts, total_nread, result); + return result; +} +#endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ + +CURLcode vquic_recv_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + vquic_recv_pkt_cb *recv_cb, void *userp) +{ + CURLcode result; +#if defined(HAVE_SENDMMSG) + result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); +#elif defined(HAVE_SENDMSG) + result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); +#else + result = recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp); +#endif + if(!result) { + if(!qctx->got_first_byte) { + qctx->got_first_byte = TRUE; + qctx->first_byte_at = qctx->last_op; + } + qctx->last_io = qctx->last_op; + } + return result; +} + +/* + * If the QLOGDIR environment variable is set, open and return a file + * descriptor to write the log to. + * + * This function returns error if something failed outside of failing to + * create the file. Open file success is deemed by seeing if the returned fd + * is != -1. + */ +CURLcode Curl_qlogdir(struct Curl_easy *data, + unsigned char *scid, + size_t scidlen, + int *qlogfdp) +{ + const char *qlog_dir = getenv("QLOGDIR"); + *qlogfdp = -1; + if(qlog_dir) { + struct dynbuf fname; + CURLcode result; + unsigned int i; + Curl_dyn_init(&fname, DYN_QLOG_NAME); + result = Curl_dyn_add(&fname, qlog_dir); + if(!result) + result = Curl_dyn_add(&fname, "/"); + for(i = 0; (i < scidlen) && !result; i++) { + char hex[3]; + msnprintf(hex, 3, "%02x", scid[i]); + result = Curl_dyn_add(&fname, hex); + } + if(!result) + result = Curl_dyn_add(&fname, ".sqlog"); + + if(!result) { + int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE, + data->set.new_file_perms); + if(qlogfd != -1) + *qlogfdp = qlogfd; + } + Curl_dyn_free(&fname); + if(result) + return result; + } + + return CURLE_OK; +} + +CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport) +{ + (void)transport; + DEBUGASSERT(transport == TRNSPRT_QUIC); +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_cf_ngtcp2_create(pcf, data, conn, ai); +#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) + return Curl_cf_osslq_create(pcf, data, conn, ai); +#elif defined(USE_QUICHE) + return Curl_cf_quiche_create(pcf, data, conn, ai); +#elif defined(USE_MSH3) + return Curl_cf_msh3_create(pcf, data, conn, ai); +#else + *pcf = NULL; + (void)data; + (void)conn; + (void)ai; + return CURLE_NOT_BUILT_IN; +#endif +} + +bool Curl_conn_is_http3(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_conn_is_ngtcp2(data, conn, sockindex); +#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) + return Curl_conn_is_osslq(data, conn, sockindex); +#elif defined(USE_QUICHE) + return Curl_conn_is_quiche(data, conn, sockindex); +#elif defined(USE_MSH3) + return Curl_conn_is_msh3(data, conn, sockindex); +#else + return ((conn->handler->protocol & PROTO_FAMILY_HTTP) && + (conn->httpversion == 30)); +#endif +} + +CURLcode Curl_conn_may_http3(struct Curl_easy *data, + const struct connectdata *conn) +{ + if(conn->transport == TRNSPRT_UNIX) { + /* cannot do QUIC over a unix domain socket */ + return CURLE_QUIC_CONNECT_ERROR; + } + if(!(conn->handler->flags & PROTOPT_SSL)) { + failf(data, "HTTP/3 requested for non-HTTPS URL"); + return CURLE_URL_MALFORMAT; + } +#ifndef CURL_DISABLE_PROXY + if(conn->bits.socksproxy) { + failf(data, "HTTP/3 is not supported over a SOCKS proxy"); + return CURLE_URL_MALFORMAT; + } + if(conn->bits.httpproxy && conn->bits.tunnel_proxy) { + failf(data, "HTTP/3 is not supported over an HTTP proxy"); + return CURLE_URL_MALFORMAT; + } +#endif + + return CURLE_OK; +} + +#else /* USE_HTTP3 */ + +CURLcode Curl_conn_may_http3(struct Curl_easy *data, + const struct connectdata *conn) +{ + (void)conn; + (void)data; + DEBUGF(infof(data, "QUIC is not supported in this build")); + return CURLE_NOT_BUILT_IN; +} + +#endif /* !USE_HTTP3 */ diff --git a/third_party/curl/lib/vquic/vquic.h b/third_party/curl/lib/vquic/vquic.h new file mode 100644 index 0000000000..c1ca1df6aa --- /dev/null +++ b/third_party/curl/lib/vquic/vquic.h @@ -0,0 +1,64 @@ +#ifndef HEADER_CURL_VQUIC_QUIC_H +#define HEADER_CURL_VQUIC_QUIC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_HTTP3 +struct Curl_cfilter; +struct Curl_easy; +struct connectdata; +struct Curl_addrinfo; + +void Curl_quic_ver(char *p, size_t len); + +CURLcode Curl_qlogdir(struct Curl_easy *data, + unsigned char *scid, + size_t scidlen, + int *qlogfdp); + + +CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport); + +bool Curl_conn_is_http3(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); + +extern struct Curl_cftype Curl_cft_http3; + +#else /* USE_HTTP3 */ + +#define Curl_conn_is_http3(a,b,c) FALSE + +#endif /* !USE_HTTP3 */ + +CURLcode Curl_conn_may_http3(struct Curl_easy *data, + const struct connectdata *conn); + +#endif /* HEADER_CURL_VQUIC_QUIC_H */ diff --git a/third_party/curl/lib/vquic/vquic_int.h b/third_party/curl/lib/vquic/vquic_int.h new file mode 100644 index 0000000000..754e1f5910 --- /dev/null +++ b/third_party/curl/lib/vquic/vquic_int.h @@ -0,0 +1,93 @@ +#ifndef HEADER_CURL_VQUIC_QUIC_INT_H +#define HEADER_CURL_VQUIC_QUIC_INT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "bufq.h" + +#ifdef USE_HTTP3 + +#define MAX_PKT_BURST 10 +#define MAX_UDP_PAYLOAD_SIZE 1452 +/* Default QUIC connection timeout we announce from our side */ +#define CURL_QUIC_MAX_IDLE_MS (120 * 1000) + +struct cf_quic_ctx { + curl_socket_t sockfd; /* connected UDP socket */ + struct sockaddr_storage local_addr; /* address socket is bound to */ + socklen_t local_addrlen; /* length of local address */ + + struct bufq sendbuf; /* buffer for sending one or more packets */ + struct curltime first_byte_at; /* when first byte was recvd */ + struct curltime last_op; /* last (attempted) send/recv operation */ + struct curltime last_io; /* last successful socket IO */ + size_t gsolen; /* length of individual packets in send buf */ + size_t split_len; /* if != 0, buffer length after which GSO differs */ + size_t split_gsolen; /* length of individual packets after split_len */ +#ifdef DEBUGBUILD + int wblock_percent; /* percent of writes doing EAGAIN */ +#endif + BIT(got_first_byte); /* if first byte was received */ + BIT(no_gso); /* do not use gso on sending */ +}; + +CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx); +void vquic_ctx_free(struct cf_quic_ctx *qctx); + +void vquic_ctx_update_time(struct cf_quic_ctx *qctx); + +void vquic_push_blocked_pkt(struct Curl_cfilter *cf, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, size_t gsolen); + +CURLcode vquic_send_blocked_pkts(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx); + +CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx, size_t gsolen); + +CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx, size_t gsolen, + size_t tail_len, size_t tail_gsolen); + +CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx); + + +typedef CURLcode vquic_recv_pkt_cb(const unsigned char *pkt, size_t pktlen, + struct sockaddr_storage *remote_addr, + socklen_t remote_addrlen, int ecn, + void *userp); + +CURLcode vquic_recv_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + vquic_recv_pkt_cb *recv_cb, void *userp); + +#endif /* !USE_HTTP3 */ + +#endif /* HEADER_CURL_VQUIC_QUIC_INT_H */ diff --git a/third_party/curl/lib/vssh/libssh.c b/third_party/curl/lib/vssh/libssh.c new file mode 100644 index 0000000000..20a05636d3 --- /dev/null +++ b/third_party/curl/lib/vssh/libssh.c @@ -0,0 +1,2953 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Red Hat, Inc. + * + * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek, + * Robert Kolcun, Andreas Schneider + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_LIBSSH + +#include + +/* in 0.10.0 or later, ignore deprecated warnings */ +#define SSH_SUPPRESS_DEPRECATED +#include +#include + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "escape.h" +#include "http.h" /* for HTTP proxy tunnel stuff */ +#include "ssh.h" +#include "url.h" +#include "speedcheck.h" +#include "getinfo.h" +#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 */ +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "strtoofft.h" +#include "multiif.h" +#include "select.h" +#include "warnless.h" +#include "curl_path.h" + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* A recent macro provided by libssh. Or make our own. */ +#ifndef SSH_STRING_FREE_CHAR +#define SSH_STRING_FREE_CHAR(x) \ + do { \ + if(x) { \ + ssh_string_free_char(x); \ + x = NULL; \ + } \ + } while(0) +#endif + +/* These stat values may not be the same as the user's S_IFMT / S_IFLNK */ +#ifndef SSH_S_IFMT +#define SSH_S_IFMT 00170000 +#endif +#ifndef SSH_S_IFLNK +#define SSH_S_IFLNK 0120000 +#endif + +/* Local functions: */ +static CURLcode myssh_connect(struct Curl_easy *data, bool *done); +static CURLcode myssh_multi_statemach(struct Curl_easy *data, + bool *done); +static CURLcode myssh_do_it(struct Curl_easy *data, bool *done); + +static CURLcode scp_done(struct Curl_easy *data, + CURLcode, bool premature); +static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done); +static CURLcode scp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection); + +static CURLcode sftp_done(struct Curl_easy *data, + CURLcode, bool premature); +static CURLcode sftp_doing(struct Curl_easy *data, + bool *dophase_done); +static CURLcode sftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead); +static +CURLcode sftp_perform(struct Curl_easy *data, + bool *connected, + bool *dophase_done); + +static void sftp_quote(struct Curl_easy *data); +static void sftp_quote_stat(struct Curl_easy *data); +static int myssh_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *sock); + +static CURLcode myssh_setup_connection(struct Curl_easy *data, + struct connectdata *conn); + +/* + * SCP protocol handler. + */ + +const struct Curl_handler Curl_handler_scp = { + "SCP", /* scheme */ + myssh_setup_connection, /* setup_connection */ + myssh_do_it, /* do_it */ + scp_done, /* done */ + ZERO_NULL, /* do_more */ + myssh_connect, /* connect_it */ + myssh_multi_statemach, /* connecting */ + scp_doing, /* doing */ + myssh_getsock, /* proto_getsock */ + myssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + myssh_getsock, /* perform_getsock */ + scp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_SSH, /* defport */ + CURLPROTO_SCP, /* protocol */ + CURLPROTO_SCP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */ +}; + +/* + * SFTP protocol handler. + */ + +const struct Curl_handler Curl_handler_sftp = { + "SFTP", /* scheme */ + myssh_setup_connection, /* setup_connection */ + myssh_do_it, /* do_it */ + sftp_done, /* done */ + ZERO_NULL, /* do_more */ + myssh_connect, /* connect_it */ + myssh_multi_statemach, /* connecting */ + sftp_doing, /* doing */ + myssh_getsock, /* proto_getsock */ + myssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + myssh_getsock, /* perform_getsock */ + sftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_SSH, /* defport */ + CURLPROTO_SFTP, /* protocol */ + CURLPROTO_SFTP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION + | PROTOPT_NOURLQUERY /* flags */ +}; + +static CURLcode sftp_error_to_CURLE(int err) +{ + switch(err) { + case SSH_FX_OK: + return CURLE_OK; + + case SSH_FX_NO_SUCH_FILE: + case SSH_FX_NO_SUCH_PATH: + return CURLE_REMOTE_FILE_NOT_FOUND; + + case SSH_FX_PERMISSION_DENIED: + case SSH_FX_WRITE_PROTECT: + return CURLE_REMOTE_ACCESS_DENIED; + + case SSH_FX_FILE_ALREADY_EXISTS: + return CURLE_REMOTE_FILE_EXISTS; + + default: + break; + } + + return CURLE_SSH; +} + +#ifndef DEBUGBUILD +#define state(x,y) mystate(x,y) +#else +#define state(x,y) mystate(x,y, __LINE__) +#endif + +/* + * SSH State machine related code + */ +/* This is the ONLY way to change SSH state! */ +static void mystate(struct Curl_easy *data, sshstate nowstate +#ifdef DEBUGBUILD + , int lineno +#endif + ) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char *const names[] = { + "SSH_STOP", + "SSH_INIT", + "SSH_S_STARTUP", + "SSH_HOSTKEY", + "SSH_AUTHLIST", + "SSH_AUTH_PKEY_INIT", + "SSH_AUTH_PKEY", + "SSH_AUTH_PASS_INIT", + "SSH_AUTH_PASS", + "SSH_AUTH_AGENT_INIT", + "SSH_AUTH_AGENT_LIST", + "SSH_AUTH_AGENT", + "SSH_AUTH_HOST_INIT", + "SSH_AUTH_HOST", + "SSH_AUTH_KEY_INIT", + "SSH_AUTH_KEY", + "SSH_AUTH_GSSAPI", + "SSH_AUTH_DONE", + "SSH_SFTP_INIT", + "SSH_SFTP_REALPATH", + "SSH_SFTP_QUOTE_INIT", + "SSH_SFTP_POSTQUOTE_INIT", + "SSH_SFTP_QUOTE", + "SSH_SFTP_NEXT_QUOTE", + "SSH_SFTP_QUOTE_STAT", + "SSH_SFTP_QUOTE_SETSTAT", + "SSH_SFTP_QUOTE_SYMLINK", + "SSH_SFTP_QUOTE_MKDIR", + "SSH_SFTP_QUOTE_RENAME", + "SSH_SFTP_QUOTE_RMDIR", + "SSH_SFTP_QUOTE_UNLINK", + "SSH_SFTP_QUOTE_STATVFS", + "SSH_SFTP_GETINFO", + "SSH_SFTP_FILETIME", + "SSH_SFTP_TRANS_INIT", + "SSH_SFTP_UPLOAD_INIT", + "SSH_SFTP_CREATE_DIRS_INIT", + "SSH_SFTP_CREATE_DIRS", + "SSH_SFTP_CREATE_DIRS_MKDIR", + "SSH_SFTP_READDIR_INIT", + "SSH_SFTP_READDIR", + "SSH_SFTP_READDIR_LINK", + "SSH_SFTP_READDIR_BOTTOM", + "SSH_SFTP_READDIR_DONE", + "SSH_SFTP_DOWNLOAD_INIT", + "SSH_SFTP_DOWNLOAD_STAT", + "SSH_SFTP_CLOSE", + "SSH_SFTP_SHUTDOWN", + "SSH_SCP_TRANS_INIT", + "SSH_SCP_UPLOAD_INIT", + "SSH_SCP_DOWNLOAD_INIT", + "SSH_SCP_DOWNLOAD", + "SSH_SCP_DONE", + "SSH_SCP_SEND_EOF", + "SSH_SCP_WAIT_EOF", + "SSH_SCP_WAIT_CLOSE", + "SSH_SCP_CHANNEL_FREE", + "SSH_SESSION_DISCONNECT", + "SSH_SESSION_FREE", + "QUIT" + }; + + + if(sshc->state != nowstate) { + infof(data, "SSH %p state change from %s to %s (line %d)", + (void *) sshc, names[sshc->state], names[nowstate], + lineno); + } +#endif + + sshc->state = nowstate; +} + +/* Multiple options: + * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 + * hash (90s style auth, not sure we should have it here) + * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first + * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE + * is returned by it. + * 3. none of the above. We only accept if it is present on known hosts. + * + * Returns SSH_OK or SSH_ERROR. + */ +static int myssh_is_known(struct Curl_easy *data) +{ + int rc; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + ssh_key pubkey; + size_t hlen; + unsigned char *hash = NULL; + char *found_base64 = NULL; + char *known_base64 = NULL; + int vstate; + enum curl_khmatch keymatch; + struct curl_khkey foundkey; + struct curl_khkey *knownkeyp = NULL; + curl_sshkeycallback func = + data->set.ssh_keyfunc; + +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) + struct ssh_knownhosts_entry *knownhostsentry = NULL; + struct curl_khkey knownkey; +#endif + +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0) + rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey); +#else + rc = ssh_get_publickey(sshc->ssh_session, &pubkey); +#endif + if(rc != SSH_OK) + return rc; + + if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { + int i; + char md5buffer[33]; + const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; + + rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, + &hash, &hlen); + if(rc != SSH_OK || hlen != 16) { + failf(data, + "Denied establishing ssh session: md5 fingerprint not available"); + goto cleanup; + } + + for(i = 0; i < 16; i++) + msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]); + + infof(data, "SSH MD5 fingerprint: %s", md5buffer); + + if(!strcasecompare(md5buffer, pubkey_md5)) { + failf(data, + "Denied establishing ssh session: mismatch md5 fingerprint. " + "Remote %s is not equal to %s", md5buffer, pubkey_md5); + rc = SSH_ERROR; + goto cleanup; + } + + rc = SSH_OK; + goto cleanup; + } + + if(data->set.str[STRING_SSH_KNOWNHOSTS]) { + +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) + /* Get the known_key from the known hosts file */ + vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session, + &knownhostsentry); + + /* Case an entry was found in a known hosts file */ + if(knownhostsentry) { + if(knownhostsentry->publickey) { + rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey, + &known_base64); + if(rc != SSH_OK) { + goto cleanup; + } + knownkey.key = known_base64; + knownkey.len = strlen(known_base64); + + switch(ssh_key_type(knownhostsentry->publickey)) { + case SSH_KEYTYPE_RSA: + knownkey.keytype = CURLKHTYPE_RSA; + break; + case SSH_KEYTYPE_RSA1: + knownkey.keytype = CURLKHTYPE_RSA1; + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_ECDSA_P256: + case SSH_KEYTYPE_ECDSA_P384: + case SSH_KEYTYPE_ECDSA_P521: + knownkey.keytype = CURLKHTYPE_ECDSA; + break; + case SSH_KEYTYPE_ED25519: + knownkey.keytype = CURLKHTYPE_ED25519; + break; + case SSH_KEYTYPE_DSS: + knownkey.keytype = CURLKHTYPE_DSS; + break; + default: + rc = SSH_ERROR; + goto cleanup; + } + knownkeyp = &knownkey; + } + } + + switch(vstate) { + case SSH_KNOWN_HOSTS_OK: + keymatch = CURLKHMATCH_OK; + break; + case SSH_KNOWN_HOSTS_OTHER: + case SSH_KNOWN_HOSTS_NOT_FOUND: + case SSH_KNOWN_HOSTS_UNKNOWN: + case SSH_KNOWN_HOSTS_ERROR: + keymatch = CURLKHMATCH_MISSING; + break; + default: + keymatch = CURLKHMATCH_MISMATCH; + break; + } + +#else + vstate = ssh_is_server_known(sshc->ssh_session); + switch(vstate) { + case SSH_SERVER_KNOWN_OK: + keymatch = CURLKHMATCH_OK; + break; + case SSH_SERVER_FILE_NOT_FOUND: + case SSH_SERVER_NOT_KNOWN: + keymatch = CURLKHMATCH_MISSING; + break; + default: + keymatch = CURLKHMATCH_MISMATCH; + break; + } +#endif + + if(func) { /* use callback to determine action */ + rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64); + if(rc != SSH_OK) + goto cleanup; + + foundkey.key = found_base64; + foundkey.len = strlen(found_base64); + + switch(ssh_key_type(pubkey)) { + case SSH_KEYTYPE_RSA: + foundkey.keytype = CURLKHTYPE_RSA; + break; + case SSH_KEYTYPE_RSA1: + foundkey.keytype = CURLKHTYPE_RSA1; + break; + case SSH_KEYTYPE_ECDSA: +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) + case SSH_KEYTYPE_ECDSA_P256: + case SSH_KEYTYPE_ECDSA_P384: + case SSH_KEYTYPE_ECDSA_P521: +#endif + foundkey.keytype = CURLKHTYPE_ECDSA; + break; +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0) + case SSH_KEYTYPE_ED25519: + foundkey.keytype = CURLKHTYPE_ED25519; + break; +#endif + case SSH_KEYTYPE_DSS: + foundkey.keytype = CURLKHTYPE_DSS; + break; + default: + rc = SSH_ERROR; + goto cleanup; + } + + Curl_set_in_callback(data, true); + rc = func(data, knownkeyp, /* from the knownhosts file */ + &foundkey, /* from the remote host */ + keymatch, data->set.ssh_keyfunc_userp); + Curl_set_in_callback(data, false); + + switch(rc) { + case CURLKHSTAT_FINE_ADD_TO_FILE: +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0) + rc = ssh_session_update_known_hosts(sshc->ssh_session); +#else + rc = ssh_write_knownhost(sshc->ssh_session); +#endif + if(rc != SSH_OK) { + goto cleanup; + } + break; + case CURLKHSTAT_FINE: + break; + default: /* REJECT/DEFER */ + rc = SSH_ERROR; + goto cleanup; + } + } + else { + if(keymatch != CURLKHMATCH_OK) { + rc = SSH_ERROR; + goto cleanup; + } + } + } + rc = SSH_OK; + +cleanup: + if(found_base64) { + (free)(found_base64); + } + if(known_base64) { + (free)(known_base64); + } + if(hash) + ssh_clean_pubkey_hash(&hash); + ssh_key_free(pubkey); +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) + if(knownhostsentry) { + ssh_knownhosts_entry_free(knownhostsentry); + } +#endif + return rc; +} + +#define MOVE_TO_ERROR_STATE(_r) do { \ + state(data, SSH_SESSION_DISCONNECT); \ + sshc->actualcode = _r; \ + rc = SSH_ERROR; \ + } while(0) + +#define MOVE_TO_SFTP_CLOSE_STATE() do { \ + state(data, SSH_SFTP_CLOSE); \ + sshc->actualcode = \ + sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \ + rc = SSH_ERROR; \ + } while(0) + +#define MOVE_TO_PASSWD_AUTH do { \ + if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \ + rc = SSH_OK; \ + state(data, SSH_AUTH_PASS_INIT); \ + } \ + else { \ + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \ + } \ + } while(0) + +#define MOVE_TO_KEY_AUTH do { \ + if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \ + rc = SSH_OK; \ + state(data, SSH_AUTH_KEY_INIT); \ + } \ + else { \ + MOVE_TO_PASSWD_AUTH; \ + } \ + } while(0) + +#define MOVE_TO_GSSAPI_AUTH do { \ + if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \ + rc = SSH_OK; \ + state(data, SSH_AUTH_GSSAPI); \ + } \ + else { \ + MOVE_TO_KEY_AUTH; \ + } \ + } while(0) + +static +int myssh_auth_interactive(struct connectdata *conn) +{ + int rc; + struct ssh_conn *sshc = &conn->proto.sshc; + int nprompts; + +restart: + switch(sshc->kbd_state) { + case 0: + rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + + if(rc != SSH_AUTH_INFO) + return SSH_ERROR; + + nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); + if(nprompts != 1) + return SSH_ERROR; + + rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); + if(rc < 0) + return SSH_ERROR; + + FALLTHROUGH(); + case 1: + sshc->kbd_state = 1; + + rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + else if(rc == SSH_AUTH_SUCCESS) + rc = SSH_OK; + else if(rc == SSH_AUTH_INFO) { + nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); + if(nprompts) + return SSH_ERROR; + + sshc->kbd_state = 2; + goto restart; + } + else + rc = SSH_ERROR; + break; + case 2: + sshc->kbd_state = 2; + + rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + else if(rc == SSH_AUTH_SUCCESS) + rc = SSH_OK; + else + rc = SSH_ERROR; + + break; + default: + return SSH_ERROR; + } + + sshc->kbd_state = 0; + return rc; +} + +/* + * ssh_statemach_act() runs the SSH state machine as far as it can without + * blocking and without reaching the end. The data the pointer 'block' points + * to will be set to TRUE if the libssh function returns SSH_AGAIN + * meaning it wants to be called again when the socket is ready + */ +static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct SSHPROTO *protop = data->req.p.ssh; + struct ssh_conn *sshc = &conn->proto.sshc; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int rc = SSH_NO_ERROR, err; + int seekerr = CURL_SEEKFUNC_OK; + const char *err_msg; + *block = 0; /* we are not blocking by default */ + + do { + + switch(sshc->state) { + case SSH_INIT: + sshc->secondCreateDirs = 0; + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_OK; + +#if 0 + ssh_set_log_level(SSH_LOG_PROTOCOL); +#endif + + /* Set libssh to non-blocking, since everything internally is + non-blocking */ + ssh_set_blocking(sshc->ssh_session, 0); + + state(data, SSH_S_STARTUP); + FALLTHROUGH(); + + case SSH_S_STARTUP: + rc = ssh_connect(sshc->ssh_session); + if(rc == SSH_AGAIN) + break; + + if(rc != SSH_OK) { + failf(data, "Failure establishing ssh session"); + MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT); + break; + } + + state(data, SSH_HOSTKEY); + + FALLTHROUGH(); + case SSH_HOSTKEY: + + rc = myssh_is_known(data); + if(rc != SSH_OK) { + MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION); + break; + } + + state(data, SSH_AUTHLIST); + FALLTHROUGH(); + case SSH_AUTHLIST:{ + sshc->authed = FALSE; + + rc = ssh_userauth_none(sshc->ssh_session, NULL); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + + if(rc == SSH_AUTH_SUCCESS) { + sshc->authed = TRUE; + infof(data, "Authenticated with none"); + state(data, SSH_AUTH_DONE); + break; + } + else if(rc == SSH_AUTH_ERROR) { + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + break; + } + + sshc->auth_methods = + (unsigned int)ssh_userauth_list(sshc->ssh_session, NULL); + if(sshc->auth_methods) + infof(data, "SSH authentication methods available: %s%s%s%s", + sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ? + "public key, ": "", + sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ? + "GSSAPI, " : "", + sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ? + "keyboard-interactive, " : "", + sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ? + "password": ""); + if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { + state(data, SSH_AUTH_PKEY_INIT); + infof(data, "Authentication using SSH public key file"); + } + else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { + state(data, SSH_AUTH_GSSAPI); + } + else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { + state(data, SSH_AUTH_KEY_INIT); + } + else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { + state(data, SSH_AUTH_PASS_INIT); + } + else { /* unsupported authentication method */ + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + break; + } + + break; + } + case SSH_AUTH_PKEY_INIT: + if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) { + MOVE_TO_GSSAPI_AUTH; + break; + } + + /* Two choices, (1) private key was given on CMD, + * (2) use the "default" keys. */ + if(data->set.str[STRING_SSH_PRIVATE_KEY]) { + if(sshc->pubkey && !data->set.ssl.key_passwd) { + rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, + sshc->pubkey); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + + if(rc != SSH_OK) { + MOVE_TO_GSSAPI_AUTH; + break; + } + } + + rc = ssh_pki_import_privkey_file(data-> + set.str[STRING_SSH_PRIVATE_KEY], + data->set.ssl.key_passwd, NULL, + NULL, &sshc->privkey); + if(rc != SSH_OK) { + failf(data, "Could not load private key file %s", + data->set.str[STRING_SSH_PRIVATE_KEY]); + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + break; + } + + state(data, SSH_AUTH_PKEY); + break; + + } + else { + rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL, + data->set.ssl.key_passwd); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + if(rc == SSH_AUTH_SUCCESS) { + rc = SSH_OK; + sshc->authed = TRUE; + infof(data, "Completed public key authentication"); + state(data, SSH_AUTH_DONE); + break; + } + + MOVE_TO_GSSAPI_AUTH; + } + break; + case SSH_AUTH_PKEY: + rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + + if(rc == SSH_AUTH_SUCCESS) { + sshc->authed = TRUE; + infof(data, "Completed public key authentication"); + state(data, SSH_AUTH_DONE); + break; + } + else { + infof(data, "Failed public key authentication (rc: %d)", rc); + MOVE_TO_GSSAPI_AUTH; + } + break; + + case SSH_AUTH_GSSAPI: + if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) { + MOVE_TO_KEY_AUTH; + break; + } + + rc = ssh_userauth_gssapi(sshc->ssh_session); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + + if(rc == SSH_AUTH_SUCCESS) { + rc = SSH_OK; + sshc->authed = TRUE; + infof(data, "Completed gssapi authentication"); + state(data, SSH_AUTH_DONE); + break; + } + + MOVE_TO_KEY_AUTH; + break; + + case SSH_AUTH_KEY_INIT: + if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) { + state(data, SSH_AUTH_KEY); + } + else { + MOVE_TO_PASSWD_AUTH; + } + break; + + case SSH_AUTH_KEY: + /* keyboard-interactive authentication */ + rc = myssh_auth_interactive(conn); + if(rc == SSH_AGAIN) { + break; + } + if(rc == SSH_OK) { + sshc->authed = TRUE; + infof(data, "completed keyboard interactive authentication"); + state(data, SSH_AUTH_DONE); + } + else { + MOVE_TO_PASSWD_AUTH; + } + break; + + case SSH_AUTH_PASS_INIT: + if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) { + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + break; + } + state(data, SSH_AUTH_PASS); + FALLTHROUGH(); + + case SSH_AUTH_PASS: + rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + + if(rc == SSH_AUTH_SUCCESS) { + sshc->authed = TRUE; + infof(data, "Completed password authentication"); + state(data, SSH_AUTH_DONE); + } + else { + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + } + break; + + case SSH_AUTH_DONE: + if(!sshc->authed) { + failf(data, "Authentication failure"); + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + break; + } + + /* + * At this point we have an authenticated ssh session. + */ + infof(data, "Authentication complete"); + + Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ + + conn->sockfd = sock; + conn->writesockfd = CURL_SOCKET_BAD; + + if(conn->handler->protocol == CURLPROTO_SFTP) { + state(data, SSH_SFTP_INIT); + break; + } + infof(data, "SSH CONNECT phase done"); + state(data, SSH_STOP); + break; + + case SSH_SFTP_INIT: + ssh_set_blocking(sshc->ssh_session, 1); + + sshc->sftp_session = sftp_new(sshc->ssh_session); + if(!sshc->sftp_session) { + failf(data, "Failure initializing sftp session: %s", + ssh_get_error(sshc->ssh_session)); + MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); + break; + } + + rc = sftp_init(sshc->sftp_session); + if(rc != SSH_OK) { + failf(data, "Failure initializing sftp session: %s", + ssh_get_error(sshc->ssh_session)); + MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE)); + break; + } + state(data, SSH_SFTP_REALPATH); + FALLTHROUGH(); + case SSH_SFTP_REALPATH: + /* + * Get the "home" directory + */ + sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, "."); + if(!sshc->homedir) { + MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); + break; + } + data->state.most_recent_ftp_entrypath = sshc->homedir; + + /* This is the last step in the SFTP connect phase. Do note that while + we get the homedir here, we get the "workingpath" in the DO action + since the homedir will remain the same between request but the + working path will not. */ + DEBUGF(infof(data, "SSH CONNECT phase done")); + state(data, SSH_STOP); + break; + + case SSH_SFTP_QUOTE_INIT: + result = Curl_getworkingpath(data, sshc->homedir, &protop->path); + if(result) { + sshc->actualcode = result; + state(data, SSH_STOP); + break; + } + + if(data->set.quote) { + infof(data, "Sending quote commands"); + sshc->quote_item = data->set.quote; + state(data, SSH_SFTP_QUOTE); + } + else { + state(data, SSH_SFTP_GETINFO); + } + break; + + case SSH_SFTP_POSTQUOTE_INIT: + if(data->set.postquote) { + infof(data, "Sending quote commands"); + sshc->quote_item = data->set.postquote; + state(data, SSH_SFTP_QUOTE); + } + else { + state(data, SSH_STOP); + } + break; + + case SSH_SFTP_QUOTE: + /* Send any quote commands */ + sftp_quote(data); + break; + + case SSH_SFTP_NEXT_QUOTE: + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + + sshc->quote_item = sshc->quote_item->next; + + if(sshc->quote_item) { + state(data, SSH_SFTP_QUOTE); + } + else { + if(sshc->nextstate != SSH_NO_STATE) { + state(data, sshc->nextstate); + sshc->nextstate = SSH_NO_STATE; + } + else { + state(data, SSH_SFTP_GETINFO); + } + } + break; + + case SSH_SFTP_QUOTE_STAT: + sftp_quote_stat(data); + break; + + case SSH_SFTP_QUOTE_SETSTAT: + rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, + sshc->quote_attrs); + if(rc && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Attempt to set SFTP stats failed: %s", + ssh_get_error(sshc->ssh_session)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + /* sshc->actualcode = sftp_error_to_CURLE(err); + * we do not send the actual error; we return + * the error the libssh2 backend is returning */ + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_SYMLINK: + rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2, + sshc->quote_path1); + if(rc && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "symlink command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_MKDIR: + rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1, + (mode_t)data->set.new_directory_perms); + if(rc && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + failf(data, "mkdir command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_RENAME: + rc = sftp_rename(sshc->sftp_session, sshc->quote_path1, + sshc->quote_path2); + if(rc && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "rename command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_RMDIR: + rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1); + if(rc && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + failf(data, "rmdir command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_UNLINK: + rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1); + if(rc && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + failf(data, "rm command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_STATVFS: + { + sftp_statvfs_t statvfs; + + statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1); + if(!statvfs && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + failf(data, "statvfs command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + else if(statvfs) { + #ifdef _MSC_VER + #define CURL_LIBSSH_VFS_SIZE_MASK "I64u" + #else + #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64 + #endif + char *tmp = aprintf("statvfs:\n" + "f_bsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_frsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_blocks: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_bfree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_bavail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_files: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_ffree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_favail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_fsid: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_flag: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_namemax: %" CURL_LIBSSH_VFS_SIZE_MASK "\n", + statvfs->f_bsize, statvfs->f_frsize, + statvfs->f_blocks, statvfs->f_bfree, + statvfs->f_bavail, statvfs->f_files, + statvfs->f_ffree, statvfs->f_favail, + statvfs->f_fsid, statvfs->f_flag, + statvfs->f_namemax); + sftp_statvfs_free(statvfs); + + if(!tmp) { + result = CURLE_OUT_OF_MEMORY; + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + break; + } + + result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + free(tmp); + if(result) { + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + } + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + } + + case SSH_SFTP_GETINFO: + if(data->set.get_filetime) { + state(data, SSH_SFTP_FILETIME); + } + else { + state(data, SSH_SFTP_TRANS_INIT); + } + break; + + case SSH_SFTP_FILETIME: + { + sftp_attributes attrs; + + attrs = sftp_stat(sshc->sftp_session, protop->path); + if(attrs) { + data->info.filetime = attrs->mtime; + sftp_attributes_free(attrs); + } + + state(data, SSH_SFTP_TRANS_INIT); + break; + } + + case SSH_SFTP_TRANS_INIT: + if(data->state.upload) + state(data, SSH_SFTP_UPLOAD_INIT); + else { + if(protop->path[strlen(protop->path)-1] == '/') + state(data, SSH_SFTP_READDIR_INIT); + else + state(data, SSH_SFTP_DOWNLOAD_INIT); + } + break; + + case SSH_SFTP_UPLOAD_INIT: + { + int flags; + + if(data->state.resume_from) { + sftp_attributes attrs; + + if(data->state.resume_from < 0) { + attrs = sftp_stat(sshc->sftp_session, protop->path); + if(attrs) { + curl_off_t size = attrs->size; + if(size < 0) { + failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME); + break; + } + data->state.resume_from = attrs->size; + + sftp_attributes_free(attrs); + } + else { + data->state.resume_from = 0; + } + } + } + + if(data->set.remote_append) + /* Try to open for append, but create if nonexisting */ + flags = O_WRONLY|O_CREAT|O_APPEND; + else if(data->state.resume_from > 0) + /* If we have restart position then open for append */ + flags = O_WRONLY|O_APPEND; + else + /* Clear file before writing (normal behavior) */ + flags = O_WRONLY|O_CREAT|O_TRUNC; + + if(sshc->sftp_file) + sftp_close(sshc->sftp_file); + sshc->sftp_file = + sftp_open(sshc->sftp_session, protop->path, + flags, (mode_t)data->set.new_file_perms); + if(!sshc->sftp_file) { + err = sftp_get_error(sshc->sftp_session); + + if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE || + err == SSH_FX_NO_SUCH_PATH)) && + (data->set.ftp_create_missing_dirs && + (strlen(protop->path) > 1))) { + /* try to create the path remotely */ + rc = 0; + sshc->secondCreateDirs = 1; + state(data, SSH_SFTP_CREATE_DIRS_INIT); + break; + } + else { + MOVE_TO_SFTP_CLOSE_STATE(); + break; + } + } + + /* If we have a restart point then we need to seek to the correct + position. */ + if(data->state.resume_from > 0) { + /* Let's read off the proper amount of bytes from the input. */ + if(data->set.seek_func) { + Curl_set_in_callback(data, true); + seekerr = data->set.seek_func(data->set.seek_client, + data->state.resume_from, SEEK_SET); + Curl_set_in_callback(data, false); + } + + if(seekerr != CURL_SEEKFUNC_OK) { + curl_off_t passed = 0; + + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_FTP_COULDNT_USE_REST; + } + /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ + do { + char scratch[4*1024]; + size_t readthisamountnow = + (data->state.resume_from - passed > + (curl_off_t)sizeof(scratch)) ? + sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed); + + size_t actuallyread = + data->state.fread_func(scratch, 1, + readthisamountnow, data->state.in); + + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST); + break; + } + } while(passed < data->state.resume_from); + if(rc) + break; + } + + /* now, decrease the size of the read */ + if(data->state.infilesize > 0) { + data->state.infilesize -= data->state.resume_from; + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + + rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); + if(rc) { + MOVE_TO_SFTP_CLOSE_STATE(); + break; + } + } + if(data->state.infilesize > 0) { + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + /* upload data */ + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + conn->sockfd = conn->writesockfd; + + /* store this original bitmask setup to use later on if we cannot + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh sftp send function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_OUT; + + /* since we do not really wait for anything at this point, we want the + state machine to move on as soon as possible so we set a very short + timeout here */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + + state(data, SSH_STOP); + break; + } + + case SSH_SFTP_CREATE_DIRS_INIT: + if(strlen(protop->path) > 1) { + sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */ + state(data, SSH_SFTP_CREATE_DIRS); + } + else { + state(data, SSH_SFTP_UPLOAD_INIT); + } + break; + + case SSH_SFTP_CREATE_DIRS: + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { + *sshc->slash_pos = 0; + + infof(data, "Creating directory '%s'", protop->path); + state(data, SSH_SFTP_CREATE_DIRS_MKDIR); + break; + } + state(data, SSH_SFTP_UPLOAD_INIT); + break; + + case SSH_SFTP_CREATE_DIRS_MKDIR: + /* 'mode' - parameter is preliminary - default to 0644 */ + rc = sftp_mkdir(sshc->sftp_session, protop->path, + (mode_t)data->set.new_directory_perms); + *sshc->slash_pos = '/'; + ++sshc->slash_pos; + if(rc < 0) { + /* + * Abort if failure was not that the dir already exists or the + * permission was denied (creation might succeed further down the + * path) - retry on unspecific FAILURE also + */ + err = sftp_get_error(sshc->sftp_session); + if((err != SSH_FX_FILE_ALREADY_EXISTS) && + (err != SSH_FX_FAILURE) && + (err != SSH_FX_PERMISSION_DENIED)) { + MOVE_TO_SFTP_CLOSE_STATE(); + break; + } + rc = 0; /* clear rc and continue */ + } + state(data, SSH_SFTP_CREATE_DIRS); + break; + + case SSH_SFTP_READDIR_INIT: + Curl_pgrsSetDownloadSize(data, -1); + if(data->req.no_body) { + state(data, SSH_STOP); + break; + } + + /* + * This is a directory that we are trying to get, so produce a directory + * listing + */ + sshc->sftp_dir = sftp_opendir(sshc->sftp_session, + protop->path); + if(!sshc->sftp_dir) { + failf(data, "Could not open directory for reading: %s", + ssh_get_error(sshc->ssh_session)); + MOVE_TO_SFTP_CLOSE_STATE(); + break; + } + state(data, SSH_SFTP_READDIR); + break; + + case SSH_SFTP_READDIR: + Curl_dyn_reset(&sshc->readdir_buf); + if(sshc->readdir_attrs) + sftp_attributes_free(sshc->readdir_attrs); + + sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir); + if(sshc->readdir_attrs) { + sshc->readdir_filename = sshc->readdir_attrs->name; + sshc->readdir_longentry = sshc->readdir_attrs->longname; + sshc->readdir_len = strlen(sshc->readdir_filename); + + if(data->set.list_only) { + char *tmpLine; + + tmpLine = aprintf("%s\n", sshc->readdir_filename); + if(!tmpLine) { + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + result = Curl_client_write(data, CLIENTWRITE_BODY, + tmpLine, sshc->readdir_len + 1); + free(tmpLine); + + if(result) { + state(data, SSH_STOP); + break; + } + + } + else { + if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) { + sshc->actualcode = CURLE_OUT_OF_MEMORY; + state(data, SSH_STOP); + break; + } + + if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) && + ((sshc->readdir_attrs->permissions & SSH_S_IFMT) == + SSH_S_IFLNK)) { + sshc->readdir_linkPath = aprintf("%s%s", protop->path, + sshc->readdir_filename); + + if(!sshc->readdir_linkPath) { + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + + state(data, SSH_SFTP_READDIR_LINK); + break; + } + state(data, SSH_SFTP_READDIR_BOTTOM); + break; + } + } + else if(sftp_dir_eof(sshc->sftp_dir)) { + state(data, SSH_SFTP_READDIR_DONE); + break; + } + else { + failf(data, "Could not open remote file for reading: %s", + ssh_get_error(sshc->ssh_session)); + MOVE_TO_SFTP_CLOSE_STATE(); + break; + } + break; + + case SSH_SFTP_READDIR_LINK: + if(sshc->readdir_link_attrs) + sftp_attributes_free(sshc->readdir_link_attrs); + + sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session, + sshc->readdir_linkPath); + if(sshc->readdir_link_attrs == 0) { + failf(data, "Could not read symlink for reading: %s", + ssh_get_error(sshc->ssh_session)); + MOVE_TO_SFTP_CLOSE_STATE(); + break; + } + + if(!sshc->readdir_link_attrs->name) { + sshc->readdir_tmp = sftp_readlink(sshc->sftp_session, + sshc->readdir_linkPath); + if(!sshc->readdir_filename) + sshc->readdir_len = 0; + else + sshc->readdir_len = strlen(sshc->readdir_tmp); + sshc->readdir_longentry = NULL; + sshc->readdir_filename = sshc->readdir_tmp; + } + else { + sshc->readdir_len = strlen(sshc->readdir_link_attrs->name); + sshc->readdir_filename = sshc->readdir_link_attrs->name; + sshc->readdir_longentry = sshc->readdir_link_attrs->longname; + } + + Curl_safefree(sshc->readdir_linkPath); + + if(Curl_dyn_addf(&sshc->readdir_buf, " -> %s", + sshc->readdir_filename)) { + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + + sftp_attributes_free(sshc->readdir_link_attrs); + sshc->readdir_link_attrs = NULL; + sshc->readdir_filename = NULL; + sshc->readdir_longentry = NULL; + + state(data, SSH_SFTP_READDIR_BOTTOM); + FALLTHROUGH(); + case SSH_SFTP_READDIR_BOTTOM: + if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1)) + result = CURLE_OUT_OF_MEMORY; + else + result = Curl_client_write(data, CLIENTWRITE_BODY, + Curl_dyn_ptr(&sshc->readdir_buf), + Curl_dyn_len(&sshc->readdir_buf)); + + ssh_string_free_char(sshc->readdir_tmp); + sshc->readdir_tmp = NULL; + + if(result) { + state(data, SSH_STOP); + } + else + state(data, SSH_SFTP_READDIR); + break; + + case SSH_SFTP_READDIR_DONE: + sftp_closedir(sshc->sftp_dir); + sshc->sftp_dir = NULL; + + /* no data to transfer */ + Curl_xfer_setup_nop(data); + state(data, SSH_STOP); + break; + + case SSH_SFTP_DOWNLOAD_INIT: + /* + * Work on getting the specified file + */ + if(sshc->sftp_file) + sftp_close(sshc->sftp_file); + + sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path, + O_RDONLY, (mode_t)data->set.new_file_perms); + if(!sshc->sftp_file) { + failf(data, "Could not open remote file for reading: %s", + ssh_get_error(sshc->ssh_session)); + + MOVE_TO_SFTP_CLOSE_STATE(); + break; + } + sftp_file_set_nonblocking(sshc->sftp_file); + state(data, SSH_SFTP_DOWNLOAD_STAT); + break; + + case SSH_SFTP_DOWNLOAD_STAT: + { + sftp_attributes attrs; + curl_off_t size; + + attrs = sftp_fstat(sshc->sftp_file); + if(!attrs || + !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) || + (attrs->size == 0)) { + /* + * sftp_fstat did not return an error, so maybe the server + * just does not support stat() + * OR the server does not return a file size with a stat() + * OR file size is 0 + */ + data->req.size = -1; + data->req.maxdownload = -1; + Curl_pgrsSetDownloadSize(data, -1); + size = 0; + } + else { + size = attrs->size; + + sftp_attributes_free(attrs); + + if(size < 0) { + failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + if(data->state.use_range) { + curl_off_t from, to; + char *ptr; + char *ptr2; + CURLofft to_t; + CURLofft from_t; + + from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from); + if(from_t == CURL_OFFT_FLOW) { + return CURLE_RANGE_ERROR; + } + while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) + ptr++; + to_t = curlx_strtoofft(ptr, &ptr2, 10, &to); + if(to_t == CURL_OFFT_FLOW) { + return CURLE_RANGE_ERROR; + } + if((to_t == CURL_OFFT_INVAL) /* no "to" value given */ + || (to >= size)) { + to = size - 1; + } + if(from_t) { + /* from is relative to end of file */ + from = size - to; + to = size - 1; + } + if(from > size) { + failf(data, "Offset (%" + CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" + CURL_FORMAT_CURL_OFF_T ")", from, size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + if(from > to) { + from = to; + size = 0; + } + else { + if((to - from) == CURL_OFF_T_MAX) + return CURLE_RANGE_ERROR; + size = to - from + 1; + } + + rc = sftp_seek64(sshc->sftp_file, from); + if(rc) { + MOVE_TO_SFTP_CLOSE_STATE(); + break; + } + } + data->req.size = size; + data->req.maxdownload = size; + Curl_pgrsSetDownloadSize(data, size); + } + + /* We can resume if we can seek to the resume position */ + if(data->state.resume_from) { + if(data->state.resume_from < 0) { + /* We are supposed to download the last abs(from) bytes */ + if((curl_off_t)size < -data->state.resume_from) { + failf(data, "Offset (%" + CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" + CURL_FORMAT_CURL_OFF_T ")", + data->state.resume_from, size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* download from where? */ + data->state.resume_from += size; + } + else { + if((curl_off_t)size < data->state.resume_from) { + failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T + ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", + data->state.resume_from, size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + } + /* Now store the number of bytes we are expected to download */ + data->req.size = size - data->state.resume_from; + data->req.maxdownload = size - data->state.resume_from; + Curl_pgrsSetDownloadSize(data, + size - data->state.resume_from); + + rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); + if(rc) { + MOVE_TO_SFTP_CLOSE_STATE(); + break; + } + } + } + + /* Setup the actual download */ + if(data->req.size == 0) { + /* no data to transfer */ + Curl_xfer_setup_nop(data); + infof(data, "File already completely downloaded"); + state(data, SSH_STOP); + break; + } + Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + conn->writesockfd = conn->sockfd; + + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh recv function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_IN; + + if(result) { + /* this should never occur; the close state should be entered + at the time the error occurs */ + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = result; + } + else { + sshc->sftp_recv_state = 0; + state(data, SSH_STOP); + } + break; + + case SSH_SFTP_CLOSE: + if(sshc->sftp_file) { + sftp_close(sshc->sftp_file); + sshc->sftp_file = NULL; + } + Curl_safefree(protop->path); + + DEBUGF(infof(data, "SFTP DONE done")); + + /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT + After nextstate is executed, the control should come back to + SSH_SFTP_CLOSE to pass the correct result back */ + if(sshc->nextstate != SSH_NO_STATE && + sshc->nextstate != SSH_SFTP_CLOSE) { + state(data, sshc->nextstate); + sshc->nextstate = SSH_SFTP_CLOSE; + } + else { + state(data, SSH_STOP); + result = sshc->actualcode; + } + break; + + case SSH_SFTP_SHUTDOWN: + /* during times we get here due to a broken transfer and then the + sftp_handle might not have been taken down so make sure that is done + before we proceed */ + + if(sshc->sftp_file) { + sftp_close(sshc->sftp_file); + sshc->sftp_file = NULL; + } + + if(sshc->sftp_session) { + sftp_free(sshc->sftp_session); + sshc->sftp_session = NULL; + } + + SSH_STRING_FREE_CHAR(sshc->homedir); + data->state.most_recent_ftp_entrypath = NULL; + + state(data, SSH_SESSION_DISCONNECT); + break; + + case SSH_SCP_TRANS_INIT: + result = Curl_getworkingpath(data, sshc->homedir, &protop->path); + if(result) { + sshc->actualcode = result; + state(data, SSH_STOP); + break; + } + + /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ + ssh_set_blocking(sshc->ssh_session, 1); + + if(data->state.upload) { + if(data->state.infilesize < 0) { + failf(data, "SCP requires a known file size for upload"); + sshc->actualcode = CURLE_UPLOAD_FAILED; + MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + break; + } + + sshc->scp_session = + ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path); + state(data, SSH_SCP_UPLOAD_INIT); + } + else { + sshc->scp_session = + ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path); + state(data, SSH_SCP_DOWNLOAD_INIT); + } + + if(!sshc->scp_session) { + err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + } + + break; + + case SSH_SCP_UPLOAD_INIT: + + rc = ssh_scp_init(sshc->scp_session); + if(rc != SSH_OK) { + err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + break; + } + + rc = ssh_scp_push_file(sshc->scp_session, protop->path, + data->state.infilesize, + (int)data->set.new_file_perms); + if(rc != SSH_OK) { + err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + break; + } + + /* upload data */ + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + conn->sockfd = conn->writesockfd; + + /* store this original bitmask setup to use later on if we cannot + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh scp send function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_OUT; + + state(data, SSH_STOP); + + break; + + case SSH_SCP_DOWNLOAD_INIT: + + rc = ssh_scp_init(sshc->scp_session); + if(rc != SSH_OK) { + err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); + break; + } + state(data, SSH_SCP_DOWNLOAD); + FALLTHROUGH(); + + case SSH_SCP_DOWNLOAD:{ + curl_off_t bytecount; + + rc = ssh_scp_pull_request(sshc->scp_session); + if(rc != SSH_SCP_REQUEST_NEWFILE) { + err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + MOVE_TO_ERROR_STATE(CURLE_REMOTE_FILE_NOT_FOUND); + break; + } + + /* download data */ + bytecount = ssh_scp_request_get_size(sshc->scp_session); + data->req.maxdownload = (curl_off_t) bytecount; + Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + conn->writesockfd = conn->sockfd; + + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh recv function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_IN; + + state(data, SSH_STOP); + break; + } + case SSH_SCP_DONE: + if(data->state.upload) + state(data, SSH_SCP_SEND_EOF); + else + state(data, SSH_SCP_CHANNEL_FREE); + break; + + case SSH_SCP_SEND_EOF: + if(sshc->scp_session) { + rc = ssh_scp_close(sshc->scp_session); + if(rc == SSH_AGAIN) { + /* Currently the ssh_scp_close handles waiting for EOF in + * blocking way. + */ + break; + } + if(rc != SSH_OK) { + infof(data, "Failed to close libssh scp channel: %s", + ssh_get_error(sshc->ssh_session)); + } + } + + state(data, SSH_SCP_CHANNEL_FREE); + break; + + case SSH_SCP_CHANNEL_FREE: + if(sshc->scp_session) { + ssh_scp_free(sshc->scp_session); + sshc->scp_session = NULL; + } + DEBUGF(infof(data, "SCP DONE phase complete")); + + ssh_set_blocking(sshc->ssh_session, 0); + + state(data, SSH_SESSION_DISCONNECT); + FALLTHROUGH(); + + case SSH_SESSION_DISCONNECT: + /* during weird times when we have been prematurely aborted, the channel + is still alive when we reach this state and we MUST kill the channel + properly first */ + if(sshc->scp_session) { + ssh_scp_free(sshc->scp_session); + sshc->scp_session = NULL; + } + + ssh_disconnect(sshc->ssh_session); + if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) { + /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back, + tell the connection to forget about it. This libssh + bug is fixed in 0.10.0. */ + Curl_conn_forget_socket(data, FIRSTSOCKET); + } + + SSH_STRING_FREE_CHAR(sshc->homedir); + data->state.most_recent_ftp_entrypath = NULL; + + state(data, SSH_SESSION_FREE); + FALLTHROUGH(); + case SSH_SESSION_FREE: + if(sshc->ssh_session) { + ssh_free(sshc->ssh_session); + sshc->ssh_session = NULL; + } + + /* worst-case scenario cleanup */ + + DEBUGASSERT(sshc->ssh_session == NULL); + DEBUGASSERT(sshc->scp_session == NULL); + + if(sshc->readdir_tmp) { + ssh_string_free_char(sshc->readdir_tmp); + sshc->readdir_tmp = NULL; + } + + if(sshc->quote_attrs) + sftp_attributes_free(sshc->quote_attrs); + + if(sshc->readdir_attrs) + sftp_attributes_free(sshc->readdir_attrs); + + if(sshc->readdir_link_attrs) + sftp_attributes_free(sshc->readdir_link_attrs); + + if(sshc->privkey) + ssh_key_free(sshc->privkey); + if(sshc->pubkey) + ssh_key_free(sshc->pubkey); + + Curl_safefree(sshc->rsa_pub); + Curl_safefree(sshc->rsa); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + Curl_dyn_free(&sshc->readdir_buf); + Curl_safefree(sshc->readdir_linkPath); + SSH_STRING_FREE_CHAR(sshc->homedir); + + /* the code we are about to return */ + result = sshc->actualcode; + + memset(sshc, 0, sizeof(struct ssh_conn)); + + connclose(conn, "SSH session free"); + sshc->state = SSH_SESSION_FREE; /* current */ + sshc->nextstate = SSH_NO_STATE; + state(data, SSH_STOP); + break; + + case SSH_QUIT: + default: + /* internal error */ + sshc->nextstate = SSH_NO_STATE; + state(data, SSH_STOP); + break; + + } + } while(!rc && (sshc->state != SSH_STOP)); + + + if(rc == SSH_AGAIN) { + /* we would block, we need to wait for the socket to be ready (in the + right direction too)! */ + *block = TRUE; + } + + return result; +} + + +/* called by the multi interface to figure out what socket(s) to wait for and + for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ +static int myssh_getsock(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *sock) +{ + int bitmap = GETSOCK_BLANK; + (void)data; + sock[0] = conn->sock[FIRSTSOCKET]; + + if(conn->waitfor & KEEP_RECV) + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + + if(conn->waitfor & KEEP_SEND) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + if(!conn->waitfor) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + return bitmap; +} + +static void myssh_block2waitfor(struct connectdata *conn, bool block) +{ + struct ssh_conn *sshc = &conn->proto.sshc; + + /* If it did not block, or nothing was returned by ssh_get_poll_flags + * have the original set */ + conn->waitfor = sshc->orig_waitfor; + + if(block) { + int dir = ssh_get_poll_flags(sshc->ssh_session); + if(dir & SSH_READ_PENDING) { + /* translate the libssh define bits into our own bit defines */ + conn->waitfor = KEEP_RECV; + } + else if(dir & SSH_WRITE_PENDING) { + conn->waitfor = KEEP_SEND; + } + } +} + +/* called repeatedly until done from multi.c */ +static CURLcode myssh_multi_statemach(struct Curl_easy *data, + bool *done) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + bool block; /* we store the status and use that to provide a ssh_getsock() + implementation */ + CURLcode result = myssh_statemach_act(data, &block); + + *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; + myssh_block2waitfor(conn, block); + + return result; +} + +static CURLcode myssh_block_statemach(struct Curl_easy *data, + bool disconnect) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result = CURLE_OK; + + while((sshc->state != SSH_STOP) && !result) { + bool block; + timediff_t left = 1000; + struct curltime now = Curl_now(); + + result = myssh_statemach_act(data, &block); + if(result) + break; + + if(!disconnect) { + if(Curl_pgrsUpdate(data)) + return CURLE_ABORTED_BY_CALLBACK; + + result = Curl_speedcheck(data, now); + if(result) + break; + + left = Curl_timeleft(data, NULL, FALSE); + if(left < 0) { + failf(data, "Operation timed out"); + return CURLE_OPERATION_TIMEDOUT; + } + } + + if(block) { + curl_socket_t fd_read = conn->sock[FIRSTSOCKET]; + /* wait for the socket to become ready */ + (void) Curl_socket_check(fd_read, CURL_SOCKET_BAD, + CURL_SOCKET_BAD, left > 1000 ? 1000 : left); + } + + } + + return result; +} + +/* + * SSH setup connection + */ +static CURLcode myssh_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + struct SSHPROTO *ssh; + struct ssh_conn *sshc = &conn->proto.sshc; + + data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO)); + if(!ssh) + return CURLE_OUT_OF_MEMORY; + Curl_dyn_init(&sshc->readdir_buf, PATH_MAX * 2); + + return CURLE_OK; +} + +static Curl_recv scp_recv, sftp_recv; +static Curl_send scp_send, sftp_send; + +/* + * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to + * do protocol-specific actions at connect-time. + */ +static CURLcode myssh_connect(struct Curl_easy *data, bool *done) +{ + struct ssh_conn *ssh; + CURLcode result; + struct connectdata *conn = data->conn; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int rc; + + /* initialize per-handle data if not already */ + if(!data->req.p.ssh) + myssh_setup_connection(data, conn); + + /* We default to persistent connections. We set this already in this connect + function to make the reuse checks properly be able to check this bit. */ + connkeep(conn, "SSH default"); + + if(conn->handler->protocol & CURLPROTO_SCP) { + conn->recv[FIRSTSOCKET] = scp_recv; + conn->send[FIRSTSOCKET] = scp_send; + } + else { + conn->recv[FIRSTSOCKET] = sftp_recv; + conn->send[FIRSTSOCKET] = sftp_send; + } + + ssh = &conn->proto.sshc; + + ssh->ssh_session = ssh_new(); + if(!ssh->ssh_session) { + failf(data, "Failure initialising ssh session"); + return CURLE_FAILED_INIT; + } + + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name); + if(rc != SSH_OK) { + failf(data, "Could not set remote host"); + return CURLE_FAILED_INIT; + } + + rc = ssh_options_parse_config(ssh->ssh_session, NULL); + if(rc != SSH_OK) { + infof(data, "Could not parse SSH configuration files"); + /* ignore */ + } + + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock); + if(rc != SSH_OK) { + failf(data, "Could not set socket"); + return CURLE_FAILED_INIT; + } + + if(conn->user && conn->user[0] != '\0') { + infof(data, "User: %s", conn->user); + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user); + if(rc != SSH_OK) { + failf(data, "Could not set user"); + return CURLE_FAILED_INIT; + } + } + + if(data->set.str[STRING_SSH_KNOWNHOSTS]) { + infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]); + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS, + data->set.str[STRING_SSH_KNOWNHOSTS]); + if(rc != SSH_OK) { + failf(data, "Could not set known hosts file path"); + return CURLE_FAILED_INIT; + } + } + + if(conn->remote_port) { + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT, + &conn->remote_port); + if(rc != SSH_OK) { + failf(data, "Could not set remote port"); + return CURLE_FAILED_INIT; + } + } + + if(data->set.ssh_compression) { + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION, + "zlib,zlib@openssh.com,none"); + if(rc != SSH_OK) { + failf(data, "Could not set compression"); + return CURLE_FAILED_INIT; + } + } + + ssh->privkey = NULL; + ssh->pubkey = NULL; + + if(data->set.str[STRING_SSH_PUBLIC_KEY]) { + rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY], + &ssh->pubkey); + if(rc != SSH_OK) { + failf(data, "Could not load public key file"); + return CURLE_FAILED_INIT; + } + } + + /* we do not verify here, we do it at the state machine, + * after connection */ + + state(data, SSH_INIT); + + result = myssh_multi_statemach(data, done); + + return result; +} + +/* called from multi.c while DOing */ +static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done) +{ + CURLcode result; + + result = myssh_multi_statemach(data, dophase_done); + + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + return result; +} + +/* + *********************************************************************** + * + * scp_perform() + * + * This is the actual DO function for SCP. Get a file according to + * the options previously setup. + */ + +static +CURLcode scp_perform(struct Curl_easy *data, + bool *connected, bool *dophase_done) +{ + CURLcode result = CURLE_OK; + + DEBUGF(infof(data, "DO phase starts")); + + *dophase_done = FALSE; /* not done yet */ + + /* start the first command in the DO phase */ + state(data, SSH_SCP_TRANS_INIT); + + result = myssh_multi_statemach(data, dophase_done); + + *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +static CURLcode myssh_do_it(struct Curl_easy *data, bool *done) +{ + CURLcode result; + bool connected = 0; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + + *done = FALSE; /* default to false */ + + data->req.size = -1; /* make sure this is unknown at this point */ + + sshc->actualcode = CURLE_OK; /* reset error code */ + sshc->secondCreateDirs = 0; /* reset the create dir attempt state + variable */ + + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); + + if(conn->handler->protocol & CURLPROTO_SCP) + result = scp_perform(data, &connected, done); + else + result = sftp_perform(data, &connected, done); + + return result; +} + +/* BLOCKING, but the function is using the state machine so the only reason + this is still blocking is that the multi interface code has no support for + disconnecting operations that takes a while */ +static CURLcode scp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + CURLcode result = CURLE_OK; + struct ssh_conn *ssh = &conn->proto.sshc; + (void) dead_connection; + + if(ssh->ssh_session) { + /* only if there is a session still around to use! */ + + state(data, SSH_SESSION_DISCONNECT); + + result = myssh_block_statemach(data, TRUE); + } + + return result; +} + +/* generic done function for both SCP and SFTP called from their specific + done functions */ +static CURLcode myssh_done(struct Curl_easy *data, CURLcode status) +{ + CURLcode result = CURLE_OK; + struct SSHPROTO *protop = data->req.p.ssh; + + if(!status) { + /* run the state-machine */ + result = myssh_block_statemach(data, FALSE); + } + else + result = status; + + if(protop) + Curl_safefree(protop->path); + if(Curl_pgrsDone(data)) + return CURLE_ABORTED_BY_CALLBACK; + + data->req.keepon = 0; /* clear all bits */ + return result; +} + + +static CURLcode scp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + (void) premature; /* not used */ + + if(!status) + state(data, SSH_SCP_DONE); + + return myssh_done(data, status); + +} + +static ssize_t scp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, CURLcode *err) +{ + int rc; + struct connectdata *conn = data->conn; + (void) sockindex; /* we only support SCP on the fixed known primary socket */ + (void) err; + + rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len); + +#if 0 + /* The following code is misleading, mostly added as wishful thinking + * that libssh at some point will implement non-blocking ssh_scp_write/read. + * Currently rc can only be number of bytes read or SSH_ERROR. */ + myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE); + + if(rc == SSH_AGAIN) { + *err = CURLE_AGAIN; + return 0; + } + else +#endif + if(rc != SSH_OK) { + *err = CURLE_SSH; + return -1; + } + + return len; +} + +static ssize_t scp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, CURLcode *err) +{ + ssize_t nread; + struct connectdata *conn = data->conn; + (void) err; + (void) sockindex; /* we only support SCP on the fixed known primary socket */ + + /* libssh returns int */ + nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len); + +#if 0 + /* The following code is misleading, mostly added as wishful thinking + * that libssh at some point will implement non-blocking ssh_scp_write/read. + * Currently rc can only be SSH_OK or SSH_ERROR. */ + + myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE); + if(nread == SSH_AGAIN) { + *err = CURLE_AGAIN; + nread = -1; + } +#endif + + return nread; +} + +/* + * =============== SFTP =============== + */ + +/* + *********************************************************************** + * + * sftp_perform() + * + * This is the actual DO function for SFTP. Get a file/directory according to + * the options previously setup. + */ + +static +CURLcode sftp_perform(struct Curl_easy *data, + bool *connected, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + + DEBUGF(infof(data, "DO phase starts")); + + *dophase_done = FALSE; /* not done yet */ + + /* start the first command in the DO phase */ + state(data, SSH_SFTP_QUOTE_INIT); + + /* run the state-machine */ + result = myssh_multi_statemach(data, dophase_done); + + *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +/* called from multi.c while DOing */ +static CURLcode sftp_doing(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = myssh_multi_statemach(data, dophase_done); + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + return result; +} + +/* BLOCKING, but the function is using the state machine so the only reason + this is still blocking is that the multi interface code has no support for + disconnecting operations that takes a while */ +static CURLcode sftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + CURLcode result = CURLE_OK; + (void) dead_connection; + + DEBUGF(infof(data, "SSH DISCONNECT starts now")); + + if(conn->proto.sshc.ssh_session) { + /* only if there is a session still around to use! */ + state(data, SSH_SFTP_SHUTDOWN); + result = myssh_block_statemach(data, TRUE); + } + + DEBUGF(infof(data, "SSH DISCONNECT is done")); + + return result; + +} + +static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + + if(!status) { + /* Post quote commands are executed after the SFTP_CLOSE state to avoid + errors that could happen due to open file handles during POSTQUOTE + operation */ + if(!premature && data->set.postquote && !conn->bits.retry) + sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT; + state(data, SSH_SFTP_CLOSE); + } + return myssh_done(data, status); +} + +/* return number of sent bytes */ +static ssize_t sftp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, CURLcode *err) +{ + ssize_t nwrite; + struct connectdata *conn = data->conn; + (void)sockindex; + + /* limit the writes to the maximum specified in Section 3 of + * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02 + */ + if(len > 32768) + len = 32768; + + nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len); + + myssh_block2waitfor(conn, FALSE); + +#if 0 /* not returned by libssh on write */ + if(nwrite == SSH_AGAIN) { + *err = CURLE_AGAIN; + nwrite = 0; + } + else +#endif + if(nwrite < 0) { + *err = CURLE_SSH; + nwrite = -1; + } + + return nwrite; +} + +/* + * Return number of received (decrypted) bytes + * or <0 on error + */ +static ssize_t sftp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, CURLcode *err) +{ + ssize_t nread; + struct connectdata *conn = data->conn; + (void)sockindex; + + DEBUGASSERT(len < CURL_MAX_READ_SIZE); + + switch(conn->proto.sshc.sftp_recv_state) { + case 0: + conn->proto.sshc.sftp_file_index = + sftp_async_read_begin(conn->proto.sshc.sftp_file, + (uint32_t)len); + if(conn->proto.sshc.sftp_file_index < 0) { + *err = CURLE_RECV_ERROR; + return -1; + } + + FALLTHROUGH(); + case 1: + conn->proto.sshc.sftp_recv_state = 1; + + nread = sftp_async_read(conn->proto.sshc.sftp_file, + mem, (uint32_t)len, + (uint32_t)conn->proto.sshc.sftp_file_index); + + myssh_block2waitfor(conn, (nread == SSH_AGAIN)?TRUE:FALSE); + + if(nread == SSH_AGAIN) { + *err = CURLE_AGAIN; + return -1; + } + else if(nread < 0) { + *err = CURLE_RECV_ERROR; + return -1; + } + + conn->proto.sshc.sftp_recv_state = 0; + return nread; + + default: + /* we never reach here */ + return -1; + } +} + +static void sftp_quote(struct Curl_easy *data) +{ + const char *cp; + struct connectdata *conn = data->conn; + struct SSHPROTO *protop = data->req.p.ssh; + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result; + + /* + * Support some of the "FTP" commands + */ + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server responds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + if(strcasecompare("pwd", cmd)) { + /* output debug output if that is requested */ + char *tmp = aprintf("257 \"%s\" is current directory.\n", + protop->path); + if(!tmp) { + sshc->actualcode = CURLE_OUT_OF_MEMORY; + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + return; + } + Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4); + Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); + + /* this sends an FTP-like "header" to the header callback so that the + current directory can be read very similar to how it is read when + using ordinary FTP. */ + result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + free(tmp); + if(result) { + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + } + else + state(data, SSH_SFTP_NEXT_QUOTE); + return; + } + + /* + * the arguments following the command must be separated from the + * command with a space so we can check for it unconditionally + */ + cp = strchr(cmd, ' '); + if(!cp) { + failf(data, "Syntax error in SFTP command. Supply parameter(s)"); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + + /* + * also, every command takes at least one argument so we get that + * first argument right now + */ + result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error: Bad first parameter"); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return; + } + + /* + * SFTP is a binary protocol, so we do not send text commands + * to the server. Instead, we scan for commands used by + * OpenSSH's sftp program and call the appropriate libssh + * functions. + */ + if(strncasecompare(cmd, "chgrp ", 6) || + strncasecompare(cmd, "chmod ", 6) || + strncasecompare(cmd, "chown ", 6) || + strncasecompare(cmd, "atime ", 6) || + strncasecompare(cmd, "mtime ", 6)) { + /* attribute change */ + + /* sshc->quote_path1 contains the mode to set */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: " + "Bad second parameter"); + Curl_safefree(sshc->quote_path1); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return; + } + sshc->quote_attrs = NULL; + state(data, SSH_SFTP_QUOTE_STAT); + return; + } + if(strncasecompare(cmd, "ln ", 3) || + strncasecompare(cmd, "symlink ", 8)) { + /* symbolic linking */ + /* sshc->quote_path1 is the source */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in ln/symlink: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return; + } + state(data, SSH_SFTP_QUOTE_SYMLINK); + return; + } + else if(strncasecompare(cmd, "mkdir ", 6)) { + /* create dir */ + state(data, SSH_SFTP_QUOTE_MKDIR); + return; + } + else if(strncasecompare(cmd, "rename ", 7)) { + /* rename file */ + /* first param is the source path */ + /* second param is the dest. path */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in rename: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return; + } + state(data, SSH_SFTP_QUOTE_RENAME); + return; + } + else if(strncasecompare(cmd, "rmdir ", 6)) { + /* delete dir */ + state(data, SSH_SFTP_QUOTE_RMDIR); + return; + } + else if(strncasecompare(cmd, "rm ", 3)) { + state(data, SSH_SFTP_QUOTE_UNLINK); + return; + } +#ifdef HAS_STATVFS_SUPPORT + else if(strncasecompare(cmd, "statvfs ", 8)) { + state(data, SSH_SFTP_QUOTE_STATVFS); + return; + } +#endif + + failf(data, "Unknown SFTP command"); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; +} + +static void sftp_quote_stat(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server responds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + /* We read the file attributes, store them in sshc->quote_attrs + * and modify them accordingly to command. Then we switch to + * QUOTE_SETSTAT state to write new ones. + */ + + if(sshc->quote_attrs) + sftp_attributes_free(sshc->quote_attrs); + sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2); + if(!sshc->quote_attrs) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Attempt to get SFTP stats failed: %d", + sftp_get_error(sshc->sftp_session)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + + /* Now set the new attributes... */ + if(strncasecompare(cmd, "chgrp", 5)) { + sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10); + if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chgrp gid not a number"); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; + } + else if(strncasecompare(cmd, "chmod", 5)) { + mode_t perms; + perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8); + /* permissions are octal */ + if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chmod permissions not a number"); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + sshc->quote_attrs->permissions = perms; + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS; + } + else if(strncasecompare(cmd, "chown", 5)) { + sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10); + if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chown uid not a number"); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; + } + else if(strncasecompare(cmd, "atime", 5) || + strncasecompare(cmd, "mtime", 5)) { + time_t date = Curl_getdate_capped(sshc->quote_path1); + bool fail = FALSE; + if(date == -1) { + failf(data, "incorrect date format for %.*s", 5, cmd); + fail = TRUE; + } +#if SIZEOF_TIME_T > 4 + else if(date > 0xffffffff) { + failf(data, "date overflow"); + fail = TRUE; /* avoid setting a capped time */ + } +#endif + if(fail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + if(strncasecompare(cmd, "atime", 5)) + sshc->quote_attrs->atime = (uint32_t)date; + else /* mtime */ + sshc->quote_attrs->mtime = (uint32_t)date; + + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME; + } + + /* Now send the completed structure... */ + state(data, SSH_SFTP_QUOTE_SETSTAT); + return; +} + +CURLcode Curl_ssh_init(void) +{ + if(ssh_init()) { + DEBUGF(fprintf(stderr, "Error: libssh_init failed\n")); + return CURLE_FAILED_INIT; + } + return CURLE_OK; +} + +void Curl_ssh_cleanup(void) +{ + (void)ssh_finalize(); +} + +void Curl_ssh_version(char *buffer, size_t buflen) +{ + (void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0)); +} + +#endif /* USE_LIBSSH */ diff --git a/third_party/curl/lib/vssh/libssh2.c b/third_party/curl/lib/vssh/libssh2.c new file mode 100644 index 0000000000..1b566d1ab2 --- /dev/null +++ b/third_party/curl/lib/vssh/libssh2.c @@ -0,0 +1,3837 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* #define CURL_LIBSSH2_DEBUG */ + +#include "curl_setup.h" + +#ifdef USE_LIBSSH2 + +#include + +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "escape.h" +#include "http.h" /* for HTTP proxy tunnel stuff */ +#include "ssh.h" +#include "url.h" +#include "speedcheck.h" +#include "getinfo.h" +#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 */ +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "strtoofft.h" +#include "multiif.h" +#include "select.h" +#include "warnless.h" +#include "curl_path.h" + +#include /* for base64 encoding/decoding */ +#include + + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if LIBSSH2_VERSION_NUM >= 0x010206 +/* libssh2_sftp_statvfs and friends were added in 1.2.6 */ +#define HAS_STATVFS_SUPPORT 1 +#endif + +#define sftp_libssh2_realpath(s,p,t,m) \ + libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \ + (t), (m), LIBSSH2_SFTP_REALPATH) + +/* Local functions: */ +static const char *sftp_libssh2_strerror(unsigned long err); +static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); +static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); +static LIBSSH2_FREE_FUNC(my_libssh2_free); +static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data); +static CURLcode ssh_connect(struct Curl_easy *data, bool *done); +static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done); +static CURLcode ssh_do(struct Curl_easy *data, bool *done); +static CURLcode scp_done(struct Curl_easy *data, CURLcode c, bool premature); +static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done); +static CURLcode scp_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection); +static CURLcode sftp_done(struct Curl_easy *data, CURLcode, bool premature); +static CURLcode sftp_doing(struct Curl_easy *data, bool *dophase_done); +static CURLcode sftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead); +static CURLcode sftp_perform(struct Curl_easy *data, bool *connected, + bool *dophase_done); +static int ssh_getsock(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t *sock); +static CURLcode ssh_setup_connection(struct Curl_easy *data, + struct connectdata *conn); +static void ssh_attach(struct Curl_easy *data, struct connectdata *conn); + +/* + * SCP protocol handler. + */ + +const struct Curl_handler Curl_handler_scp = { + "SCP", /* scheme */ + ssh_setup_connection, /* setup_connection */ + ssh_do, /* do_it */ + scp_done, /* done */ + ZERO_NULL, /* do_more */ + ssh_connect, /* connect_it */ + ssh_multi_statemach, /* connecting */ + scp_doing, /* doing */ + ssh_getsock, /* proto_getsock */ + ssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ssh_getsock, /* perform_getsock */ + scp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ssh_attach, /* attach */ + PORT_SSH, /* defport */ + CURLPROTO_SCP, /* protocol */ + CURLPROTO_SCP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION + | PROTOPT_NOURLQUERY /* flags */ +}; + + +/* + * SFTP protocol handler. + */ + +const struct Curl_handler Curl_handler_sftp = { + "SFTP", /* scheme */ + ssh_setup_connection, /* setup_connection */ + ssh_do, /* do_it */ + sftp_done, /* done */ + ZERO_NULL, /* do_more */ + ssh_connect, /* connect_it */ + ssh_multi_statemach, /* connecting */ + sftp_doing, /* doing */ + ssh_getsock, /* proto_getsock */ + ssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ssh_getsock, /* perform_getsock */ + sftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ssh_attach, /* attach */ + PORT_SSH, /* defport */ + CURLPROTO_SFTP, /* protocol */ + CURLPROTO_SFTP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION + | PROTOPT_NOURLQUERY /* flags */ +}; + +static void +kbd_callback(const char *name, int name_len, const char *instruction, + int instruction_len, int num_prompts, + const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, + LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, + void **abstract) +{ + struct Curl_easy *data = (struct Curl_easy *)*abstract; + +#ifdef CURL_LIBSSH2_DEBUG + fprintf(stderr, "name=%s\n", name); + fprintf(stderr, "name_len=%d\n", name_len); + fprintf(stderr, "instruction=%s\n", instruction); + fprintf(stderr, "instruction_len=%d\n", instruction_len); + fprintf(stderr, "num_prompts=%d\n", num_prompts); +#else + (void)name; + (void)name_len; + (void)instruction; + (void)instruction_len; +#endif /* CURL_LIBSSH2_DEBUG */ + if(num_prompts == 1) { + struct connectdata *conn = data->conn; + responses[0].text = strdup(conn->passwd); + responses[0].length = + responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd)); + } + (void)prompts; +} /* kbd_callback */ + +static CURLcode sftp_libssh2_error_to_CURLE(unsigned long err) +{ + switch(err) { + case LIBSSH2_FX_OK: + return CURLE_OK; + + case LIBSSH2_FX_NO_SUCH_FILE: + case LIBSSH2_FX_NO_SUCH_PATH: + return CURLE_REMOTE_FILE_NOT_FOUND; + + case LIBSSH2_FX_PERMISSION_DENIED: + case LIBSSH2_FX_WRITE_PROTECT: + case LIBSSH2_FX_LOCK_CONFlICT: + return CURLE_REMOTE_ACCESS_DENIED; + + case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: + case LIBSSH2_FX_QUOTA_EXCEEDED: + return CURLE_REMOTE_DISK_FULL; + + case LIBSSH2_FX_FILE_ALREADY_EXISTS: + return CURLE_REMOTE_FILE_EXISTS; + + case LIBSSH2_FX_DIR_NOT_EMPTY: + return CURLE_QUOTE_ERROR; + + default: + break; + } + + return CURLE_SSH; +} + +static CURLcode libssh2_session_error_to_CURLE(int err) +{ + switch(err) { + /* Ordered by order of appearance in libssh2.h */ + case LIBSSH2_ERROR_NONE: + return CURLE_OK; + + /* This is the error returned by libssh2_scp_recv2 + * on unknown file */ + case LIBSSH2_ERROR_SCP_PROTOCOL: + return CURLE_REMOTE_FILE_NOT_FOUND; + + case LIBSSH2_ERROR_SOCKET_NONE: + return CURLE_COULDNT_CONNECT; + + case LIBSSH2_ERROR_ALLOC: + return CURLE_OUT_OF_MEMORY; + + case LIBSSH2_ERROR_SOCKET_SEND: + return CURLE_SEND_ERROR; + + case LIBSSH2_ERROR_HOSTKEY_INIT: + case LIBSSH2_ERROR_HOSTKEY_SIGN: + case LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED: + case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED: + return CURLE_PEER_FAILED_VERIFICATION; + + case LIBSSH2_ERROR_PASSWORD_EXPIRED: + return CURLE_LOGIN_DENIED; + + case LIBSSH2_ERROR_SOCKET_TIMEOUT: + case LIBSSH2_ERROR_TIMEOUT: + return CURLE_OPERATION_TIMEDOUT; + + case LIBSSH2_ERROR_EAGAIN: + return CURLE_AGAIN; + } + + return CURLE_SSH; +} + +static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc) +{ + (void)abstract; /* arg not used */ + return malloc(count); +} + +static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc) +{ + (void)abstract; /* arg not used */ + return realloc(ptr, count); +} + +static LIBSSH2_FREE_FUNC(my_libssh2_free) +{ + (void)abstract; /* arg not used */ + if(ptr) /* ssh2 agent sometimes call free with null ptr */ + free(ptr); +} + +/* + * SSH State machine related code + */ +/* This is the ONLY way to change SSH state! */ +static void state(struct Curl_easy *data, sshstate nowstate) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char * const names[] = { + "SSH_STOP", + "SSH_INIT", + "SSH_S_STARTUP", + "SSH_HOSTKEY", + "SSH_AUTHLIST", + "SSH_AUTH_PKEY_INIT", + "SSH_AUTH_PKEY", + "SSH_AUTH_PASS_INIT", + "SSH_AUTH_PASS", + "SSH_AUTH_AGENT_INIT", + "SSH_AUTH_AGENT_LIST", + "SSH_AUTH_AGENT", + "SSH_AUTH_HOST_INIT", + "SSH_AUTH_HOST", + "SSH_AUTH_KEY_INIT", + "SSH_AUTH_KEY", + "SSH_AUTH_GSSAPI", + "SSH_AUTH_DONE", + "SSH_SFTP_INIT", + "SSH_SFTP_REALPATH", + "SSH_SFTP_QUOTE_INIT", + "SSH_SFTP_POSTQUOTE_INIT", + "SSH_SFTP_QUOTE", + "SSH_SFTP_NEXT_QUOTE", + "SSH_SFTP_QUOTE_STAT", + "SSH_SFTP_QUOTE_SETSTAT", + "SSH_SFTP_QUOTE_SYMLINK", + "SSH_SFTP_QUOTE_MKDIR", + "SSH_SFTP_QUOTE_RENAME", + "SSH_SFTP_QUOTE_RMDIR", + "SSH_SFTP_QUOTE_UNLINK", + "SSH_SFTP_QUOTE_STATVFS", + "SSH_SFTP_GETINFO", + "SSH_SFTP_FILETIME", + "SSH_SFTP_TRANS_INIT", + "SSH_SFTP_UPLOAD_INIT", + "SSH_SFTP_CREATE_DIRS_INIT", + "SSH_SFTP_CREATE_DIRS", + "SSH_SFTP_CREATE_DIRS_MKDIR", + "SSH_SFTP_READDIR_INIT", + "SSH_SFTP_READDIR", + "SSH_SFTP_READDIR_LINK", + "SSH_SFTP_READDIR_BOTTOM", + "SSH_SFTP_READDIR_DONE", + "SSH_SFTP_DOWNLOAD_INIT", + "SSH_SFTP_DOWNLOAD_STAT", + "SSH_SFTP_CLOSE", + "SSH_SFTP_SHUTDOWN", + "SSH_SCP_TRANS_INIT", + "SSH_SCP_UPLOAD_INIT", + "SSH_SCP_DOWNLOAD_INIT", + "SSH_SCP_DOWNLOAD", + "SSH_SCP_DONE", + "SSH_SCP_SEND_EOF", + "SSH_SCP_WAIT_EOF", + "SSH_SCP_WAIT_CLOSE", + "SSH_SCP_CHANNEL_FREE", + "SSH_SESSION_DISCONNECT", + "SSH_SESSION_FREE", + "QUIT" + }; + + /* a precaution to make sure the lists are in sync */ + DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST); + + if(sshc->state != nowstate) { + infof(data, "SFTP %p state change from %s to %s", + (void *)sshc, names[sshc->state], names[nowstate]); + } +#endif + + sshc->state = nowstate; +} + + +#ifdef HAVE_LIBSSH2_KNOWNHOST_API +static int sshkeycallback(struct Curl_easy *easy, + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch match, + void *clientp) +{ + (void)easy; + (void)knownkey; + (void)foundkey; + (void)clientp; + + /* we only allow perfect matches, and we reject everything else */ + return (match != CURLKHMATCH_OK)?CURLKHSTAT_REJECT:CURLKHSTAT_FINE; +} +#endif + +/* + * Earlier libssh2 versions did not have the ability to seek to 64-bit + * positions with 32-bit size_t. + */ +#ifdef HAVE_LIBSSH2_SFTP_SEEK64 +#define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y) +#else +#define SFTP_SEEK(x,y) libssh2_sftp_seek(x, (size_t)y) +#endif + +/* + * Earlier libssh2 versions did not do SCP properly beyond 32-bit sizes on + * 32-bit architectures so we check of the necessary function is present. + */ +#ifndef HAVE_LIBSSH2_SCP_SEND64 +#define SCP_SEND(a,b,c,d) libssh2_scp_send_ex(a, b, (int)(c), (size_t)d, 0, 0) +#else +#define SCP_SEND(a,b,c,d) libssh2_scp_send64(a, b, (int)(c), \ + (libssh2_int64_t)d, 0, 0) +#endif + +/* + * libssh2 1.2.8 fixed the problem with 32-bit ints used for sockets on win64. + */ +#ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE +#define session_startup(x,y) libssh2_session_handshake(x, y) +#else +#define session_startup(x,y) libssh2_session_startup(x, (int)y) +#endif +static enum curl_khtype convert_ssh2_keytype(int sshkeytype) +{ + enum curl_khtype keytype = CURLKHTYPE_UNKNOWN; + switch(sshkeytype) { + case LIBSSH2_HOSTKEY_TYPE_RSA: + keytype = CURLKHTYPE_RSA; + break; + case LIBSSH2_HOSTKEY_TYPE_DSS: + keytype = CURLKHTYPE_DSS; + break; +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: + keytype = CURLKHTYPE_ECDSA; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: + keytype = CURLKHTYPE_ECDSA; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: + keytype = CURLKHTYPE_ECDSA; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519 + case LIBSSH2_HOSTKEY_TYPE_ED25519: + keytype = CURLKHTYPE_ED25519; + break; +#endif + } + return keytype; +} + +static CURLcode ssh_knownhost(struct Curl_easy *data) +{ + int sshkeytype = 0; + size_t keylen = 0; + int rc = 0; + CURLcode result = CURLE_OK; + +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + if(data->set.str[STRING_SSH_KNOWNHOSTS]) { + /* we are asked to verify the host against a file */ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + struct libssh2_knownhost *host = NULL; + const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, + &keylen, &sshkeytype); + int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE; + int keybit = 0; + + if(remotekey) { + /* + * A subject to figure out is what hostname we need to pass in here. + * What hostname does OpenSSH store in its file if an IDN name is + * used? + */ + enum curl_khmatch keymatch; + curl_sshkeycallback func = + data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback; + struct curl_khkey knownkey; + struct curl_khkey *knownkeyp = NULL; + struct curl_khkey foundkey; + + switch(sshkeytype) { + case LIBSSH2_HOSTKEY_TYPE_RSA: + keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; + break; + case LIBSSH2_HOSTKEY_TYPE_DSS: + keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; + break; +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519 + case LIBSSH2_HOSTKEY_TYPE_ED25519: + keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; + break; +#endif + default: + infof(data, "unsupported key type, cannot check knownhosts"); + keybit = 0; + break; + } + if(!keybit) + /* no check means failure! */ + rc = CURLKHSTAT_REJECT; + else { +#ifdef HAVE_LIBSSH2_KNOWNHOST_CHECKP + keycheck = libssh2_knownhost_checkp(sshc->kh, + conn->host.name, + (conn->remote_port != PORT_SSH)? + conn->remote_port:-1, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); +#else + keycheck = libssh2_knownhost_check(sshc->kh, + conn->host.name, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); +#endif + + infof(data, "SSH host check: %d, key: %s", keycheck, + (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)? + host->key:""); + + /* setup 'knownkey' */ + if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { + knownkey.key = host->key; + knownkey.len = 0; + knownkey.keytype = convert_ssh2_keytype(sshkeytype); + knownkeyp = &knownkey; + } + + /* setup 'foundkey' */ + foundkey.key = remotekey; + foundkey.len = keylen; + foundkey.keytype = convert_ssh2_keytype(sshkeytype); + + /* + * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the + * curl_khmatch enum are ever modified, we need to introduce a + * translation table here! + */ + keymatch = (enum curl_khmatch)keycheck; + + /* Ask the callback how to behave */ + Curl_set_in_callback(data, true); + rc = func(data, knownkeyp, /* from the knownhosts file */ + &foundkey, /* from the remote host */ + keymatch, data->set.ssh_keyfunc_userp); + Curl_set_in_callback(data, false); + } + } + else + /* no remotekey means failure! */ + rc = CURLKHSTAT_REJECT; + + switch(rc) { + default: /* unknown return codes will equal reject */ + case CURLKHSTAT_REJECT: + state(data, SSH_SESSION_FREE); + FALLTHROUGH(); + case CURLKHSTAT_DEFER: + /* DEFER means bail out but keep the SSH_HOSTKEY state */ + result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + break; + case CURLKHSTAT_FINE_REPLACE: + /* remove old host+key that does not match */ + if(host) + libssh2_knownhost_del(sshc->kh, host); + FALLTHROUGH(); + case CURLKHSTAT_FINE: + case CURLKHSTAT_FINE_ADD_TO_FILE: + /* proceed */ + if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { + /* the found host+key did not match but has been told to be fine + anyway so we add it in memory */ + int addrc = libssh2_knownhost_add(sshc->kh, + conn->host.name, NULL, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, NULL); + if(addrc) + infof(data, "WARNING: adding the known host %s failed", + conn->host.name); + else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || + rc == CURLKHSTAT_FINE_REPLACE) { + /* now we write the entire in-memory list of known hosts to the + known_hosts file */ + int wrc = + libssh2_knownhost_writefile(sshc->kh, + data->set.str[STRING_SSH_KNOWNHOSTS], + LIBSSH2_KNOWNHOST_FILE_OPENSSH); + if(wrc) { + infof(data, "WARNING: writing %s failed", + data->set.str[STRING_SSH_KNOWNHOSTS]); + } + } + } + break; + } + } +#else /* HAVE_LIBSSH2_KNOWNHOST_API */ + (void)data; +#endif + return result; +} + +static CURLcode ssh_check_fingerprint(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; + const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; + + infof(data, "SSH MD5 public key: %s", + pubkey_md5 != NULL ? pubkey_md5 : "NULL"); + infof(data, "SSH SHA256 public key: %s", + pubkey_sha256 != NULL ? pubkey_sha256 : "NULL"); + + if(pubkey_sha256) { + const char *fingerprint = NULL; + char *fingerprint_b64 = NULL; + size_t fingerprint_b64_len; + size_t pub_pos = 0; + size_t b64_pos = 0; + +#ifdef LIBSSH2_HOSTKEY_HASH_SHA256 + /* The fingerprint points to static storage (!), do not free() it. */ + fingerprint = libssh2_hostkey_hash(sshc->ssh_session, + LIBSSH2_HOSTKEY_HASH_SHA256); +#else + const char *hostkey; + size_t len = 0; + unsigned char hash[32]; + + hostkey = libssh2_session_hostkey(sshc->ssh_session, &len, NULL); + if(hostkey) { + if(!Curl_sha256it(hash, (const unsigned char *) hostkey, len)) + fingerprint = (char *) hash; + } +#endif + + if(!fingerprint) { + failf(data, + "Denied establishing ssh session: sha256 fingerprint " + "not available"); + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + + /* The length of fingerprint is 32 bytes for SHA256. + * See libssh2_hostkey_hash documentation. */ + if(Curl_base64_encode(fingerprint, 32, &fingerprint_b64, + &fingerprint_b64_len) != CURLE_OK) { + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + + if(!fingerprint_b64) { + failf(data, "sha256 fingerprint could not be encoded"); + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + + infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64); + + /* Find the position of any = padding characters in the public key */ + while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) { + pub_pos++; + } + + /* Find the position of any = padding characters in the base64 coded + * hostkey fingerprint */ + while((fingerprint_b64[b64_pos] != '=') && fingerprint_b64[b64_pos]) { + b64_pos++; + } + + /* Before we authenticate we check the hostkey's sha256 fingerprint + * against a known fingerprint, if available. + */ + if((pub_pos != b64_pos) || + strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) { + failf(data, + "Denied establishing ssh session: mismatch sha256 fingerprint. " + "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); + free(fingerprint_b64); + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + + free(fingerprint_b64); + + infof(data, "SHA256 checksum match"); + } + + if(pubkey_md5) { + char md5buffer[33]; + const char *fingerprint = NULL; + + fingerprint = libssh2_hostkey_hash(sshc->ssh_session, + LIBSSH2_HOSTKEY_HASH_MD5); + + if(fingerprint) { + /* The fingerprint points to static storage (!), do not free() it. */ + int i; + for(i = 0; i < 16; i++) { + msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]); + } + + infof(data, "SSH MD5 fingerprint: %s", md5buffer); + } + + /* This does NOT verify the length of 'pubkey_md5' separately, which will + make the comparison below fail unless it is exactly 32 characters */ + if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) { + if(fingerprint) { + failf(data, + "Denied establishing ssh session: mismatch md5 fingerprint. " + "Remote %s is not equal to %s", md5buffer, pubkey_md5); + } + else { + failf(data, + "Denied establishing ssh session: md5 fingerprint " + "not available"); + } + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + infof(data, "MD5 checksum match"); + } + + if(!pubkey_md5 && !pubkey_sha256) { + if(data->set.ssh_hostkeyfunc) { + size_t keylen = 0; + int sshkeytype = 0; + int rc = 0; + /* we handle the process to the callback */ + const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, + &keylen, &sshkeytype); + if(remotekey) { + enum curl_khtype keytype = convert_ssh2_keytype(sshkeytype); + Curl_set_in_callback(data, true); + rc = data->set.ssh_hostkeyfunc(data->set.ssh_hostkeyfunc_userp, + (int)keytype, remotekey, keylen); + Curl_set_in_callback(data, false); + if(rc!= CURLKHMATCH_OK) { + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + } + else { + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + return CURLE_OK; + } + else { + return ssh_knownhost(data); + } + } + else { + /* as we already matched, we skip the check for known hosts */ + return CURLE_OK; + } +} + +/* + * ssh_force_knownhost_key_type() will check the known hosts file and try to + * force a specific public key type from the server if an entry is found. + */ +static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + +#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 + static const char * const hostkey_method_ssh_ed25519 + = "ssh-ed25519"; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 + static const char * const hostkey_method_ssh_ecdsa_521 + = "ecdsa-sha2-nistp521"; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 + static const char * const hostkey_method_ssh_ecdsa_384 + = "ecdsa-sha2-nistp384"; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 + static const char * const hostkey_method_ssh_ecdsa_256 + = "ecdsa-sha2-nistp256"; +#endif + static const char * const hostkey_method_ssh_rsa + = "ssh-rsa"; + static const char * const hostkey_method_ssh_rsa_all + = "rsa-sha2-256,rsa-sha2-512,ssh-rsa"; + static const char * const hostkey_method_ssh_dss + = "ssh-dss"; + + const char *hostkey_method = NULL; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + struct libssh2_knownhost* store = NULL; + const char *kh_name_end = NULL; + size_t kh_name_size = 0; + int port = 0; + bool found = false; + + if(sshc->kh && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { + /* lets try to find our host in the known hosts file */ + while(!libssh2_knownhost_get(sshc->kh, &store, store)) { + /* For non-standard ports, the name will be enclosed in */ + /* square brackets, followed by a colon and the port */ + if(store) { + if(store->name) { + if(store->name[0] == '[') { + kh_name_end = strstr(store->name, "]:"); + if(!kh_name_end) { + infof(data, "Invalid host pattern %s in %s", + store->name, data->set.str[STRING_SSH_KNOWNHOSTS]); + continue; + } + port = atoi(kh_name_end + 2); + if(kh_name_end && (port == conn->remote_port)) { + kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end); + if(strncmp(store->name + 1, + conn->host.name, kh_name_size) == 0) { + found = true; + break; + } + } + } + else if(strcmp(store->name, conn->host.name) == 0) { + found = true; + break; + } + } + else { + found = true; + break; + } + } + } + + if(found) { + int rc; + infof(data, "Found host %s in %s", + conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); + + switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { +#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 + case LIBSSH2_KNOWNHOST_KEY_ED25519: + hostkey_method = hostkey_method_ssh_ed25519; + break; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 + case LIBSSH2_KNOWNHOST_KEY_ECDSA_521: + hostkey_method = hostkey_method_ssh_ecdsa_521; + break; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 + case LIBSSH2_KNOWNHOST_KEY_ECDSA_384: + hostkey_method = hostkey_method_ssh_ecdsa_384; + break; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 + case LIBSSH2_KNOWNHOST_KEY_ECDSA_256: + hostkey_method = hostkey_method_ssh_ecdsa_256; + break; +#endif + case LIBSSH2_KNOWNHOST_KEY_SSHRSA: +#ifdef HAVE_LIBSSH2_VERSION + if(libssh2_version(0x010900)) + /* since 1.9.0 libssh2_session_method_pref() works as expected */ + hostkey_method = hostkey_method_ssh_rsa_all; + else +#endif + /* old libssh2 which cannot correctly remove unsupported methods due + * to bug in src/kex.c or does not support the new methods anyways. + */ + hostkey_method = hostkey_method_ssh_rsa; + break; + case LIBSSH2_KNOWNHOST_KEY_SSHDSS: + hostkey_method = hostkey_method_ssh_dss; + break; + case LIBSSH2_KNOWNHOST_KEY_RSA1: + failf(data, "Found host key type RSA1 which is not supported"); + return CURLE_SSH; + default: + failf(data, "Unknown host key type: %i", + (store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK)); + return CURLE_SSH; + } + + infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method); + rc = libssh2_session_method_pref(sshc->ssh_session, + LIBSSH2_METHOD_HOSTKEY, hostkey_method); + if(rc) { + char *errmsg = NULL; + int errlen; + libssh2_session_last_error(sshc->ssh_session, &errmsg, &errlen, 0); + failf(data, "libssh2: %s", errmsg); + result = libssh2_session_error_to_CURLE(rc); + } + } + else { + infof(data, "Did not find host %s in %s", + conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); + } + } + +#endif /* HAVE_LIBSSH2_KNOWNHOST_API */ + + return result; +} + +/* + * ssh_statemach_act() runs the SSH state machine as far as it can without + * blocking and without reaching the end. The data the pointer 'block' points + * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN + * meaning it wants to be called again when the socket is ready + */ + +static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct SSHPROTO *sshp = data->req.p.ssh; + struct ssh_conn *sshc = &conn->proto.sshc; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int rc = LIBSSH2_ERROR_NONE; + int ssherr; + unsigned long sftperr; + int seekerr = CURL_SEEKFUNC_OK; + size_t readdir_len; + *block = 0; /* we are not blocking by default */ + + do { + switch(sshc->state) { + case SSH_INIT: + sshc->secondCreateDirs = 0; + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_OK; + + /* Set libssh2 to non-blocking, since everything internally is + non-blocking */ + libssh2_session_set_blocking(sshc->ssh_session, 0); + + result = ssh_force_knownhost_key_type(data); + if(result) { + state(data, SSH_SESSION_FREE); + sshc->actualcode = result; + break; + } + + state(data, SSH_S_STARTUP); + FALLTHROUGH(); + + case SSH_S_STARTUP: + rc = session_startup(sshc->ssh_session, sock); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg); + + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_FAILED_INIT; + break; + } + + state(data, SSH_HOSTKEY); + + FALLTHROUGH(); + case SSH_HOSTKEY: + /* + * Before we authenticate we should check the hostkey's fingerprint + * against our known hosts. How that is handled (reading from file, + * whatever) is up to us. + */ + result = ssh_check_fingerprint(data); + if(!result) + state(data, SSH_AUTHLIST); + /* ssh_check_fingerprint sets state appropriately on error */ + break; + + case SSH_AUTHLIST: + /* + * Figure out authentication methods + * NB: As soon as we have provided a username to an openssh server we + * must never change it later. Thus, always specify the correct username + * here, even though the libssh2 docs kind of indicate that it should be + * possible to get a 'generic' list (not user-specific) of authentication + * methods, presumably with a blank username. That will not work in my + * experience. + * So always specify it here. + */ + sshc->authlist = libssh2_userauth_list(sshc->ssh_session, + conn->user, + curlx_uztoui(strlen(conn->user))); + + if(!sshc->authlist) { + if(libssh2_userauth_authenticated(sshc->ssh_session)) { + sshc->authed = TRUE; + infof(data, "SSH user accepted with no authentication"); + state(data, SSH_AUTH_DONE); + break; + } + ssherr = libssh2_session_last_errno(sshc->ssh_session); + if(ssherr == LIBSSH2_ERROR_EAGAIN) + rc = LIBSSH2_ERROR_EAGAIN; + else { + state(data, SSH_SESSION_FREE); + sshc->actualcode = libssh2_session_error_to_CURLE(ssherr); + } + break; + } + infof(data, "SSH authentication methods available: %s", + sshc->authlist); + + state(data, SSH_AUTH_PKEY_INIT); + break; + + case SSH_AUTH_PKEY_INIT: + /* + * Check the supported auth types in the order I feel is most secure + * with the requested type of authentication + */ + sshc->authed = FALSE; + + if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && + (strstr(sshc->authlist, "publickey") != NULL)) { + bool out_of_memory = FALSE; + + sshc->rsa_pub = sshc->rsa = NULL; + + if(data->set.str[STRING_SSH_PRIVATE_KEY]) + sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); + else { + /* To ponder about: should really the lib be messing about with the + HOME environment variable etc? */ + char *home = curl_getenv("HOME"); + struct_stat sbuf; + + /* If no private key file is specified, try some common paths. */ + if(home) { + /* Try ~/.ssh first. */ + sshc->rsa = aprintf("%s/.ssh/id_rsa", home); + if(!sshc->rsa) + out_of_memory = TRUE; + else if(stat(sshc->rsa, &sbuf)) { + Curl_safefree(sshc->rsa); + sshc->rsa = aprintf("%s/.ssh/id_dsa", home); + if(!sshc->rsa) + out_of_memory = TRUE; + else if(stat(sshc->rsa, &sbuf)) { + Curl_safefree(sshc->rsa); + } + } + free(home); + } + if(!out_of_memory && !sshc->rsa) { + /* Nothing found; try the current dir. */ + sshc->rsa = strdup("id_rsa"); + if(sshc->rsa && stat(sshc->rsa, &sbuf)) { + Curl_safefree(sshc->rsa); + sshc->rsa = strdup("id_dsa"); + if(sshc->rsa && stat(sshc->rsa, &sbuf)) { + Curl_safefree(sshc->rsa); + /* Out of guesses. Set to the empty string to avoid + * surprising info messages. */ + sshc->rsa = strdup(""); + } + } + } + } + + /* + * Unless the user explicitly specifies a public key file, let + * libssh2 extract the public key from the private key file. + * This is done by simply passing sshc->rsa_pub = NULL. + */ + if(data->set.str[STRING_SSH_PUBLIC_KEY] + /* treat empty string the same way as NULL */ + && data->set.str[STRING_SSH_PUBLIC_KEY][0]) { + sshc->rsa_pub = strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); + if(!sshc->rsa_pub) + out_of_memory = TRUE; + } + + if(out_of_memory || !sshc->rsa) { + Curl_safefree(sshc->rsa); + Curl_safefree(sshc->rsa_pub); + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + + sshc->passphrase = data->set.ssl.key_passwd; + if(!sshc->passphrase) + sshc->passphrase = ""; + + if(sshc->rsa_pub) + infof(data, "Using SSH public key file '%s'", sshc->rsa_pub); + infof(data, "Using SSH private key file '%s'", sshc->rsa); + + state(data, SSH_AUTH_PKEY); + } + else { + state(data, SSH_AUTH_PASS_INIT); + } + break; + + case SSH_AUTH_PKEY: + /* The function below checks if the files exists, no need to stat() here. + */ + rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session, + conn->user, + curlx_uztoui( + strlen(conn->user)), + sshc->rsa_pub, + sshc->rsa, sshc->passphrase); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + + Curl_safefree(sshc->rsa_pub); + Curl_safefree(sshc->rsa); + + if(rc == 0) { + sshc->authed = TRUE; + infof(data, "Initialized SSH public key authentication"); + state(data, SSH_AUTH_DONE); + } + else { + char *err_msg = NULL; + char unknown[] = "Reason unknown (-1)"; + if(rc == -1) { + /* No error message has been set and the last set error message, if + any, is from a previous error so ignore it. #11837 */ + err_msg = unknown; + } + else { + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + } + infof(data, "SSH public key authentication failed: %s", err_msg); + state(data, SSH_AUTH_PASS_INIT); + rc = 0; /* clear rc and continue */ + } + break; + + case SSH_AUTH_PASS_INIT: + if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) && + (strstr(sshc->authlist, "password") != NULL)) { + state(data, SSH_AUTH_PASS); + } + else { + state(data, SSH_AUTH_HOST_INIT); + rc = 0; /* clear rc and continue */ + } + break; + + case SSH_AUTH_PASS: + rc = libssh2_userauth_password_ex(sshc->ssh_session, conn->user, + curlx_uztoui(strlen(conn->user)), + conn->passwd, + curlx_uztoui(strlen(conn->passwd)), + NULL); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc == 0) { + sshc->authed = TRUE; + infof(data, "Initialized password authentication"); + state(data, SSH_AUTH_DONE); + } + else { + state(data, SSH_AUTH_HOST_INIT); + rc = 0; /* clear rc and continue */ + } + break; + + case SSH_AUTH_HOST_INIT: + if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) && + (strstr(sshc->authlist, "hostbased") != NULL)) { + state(data, SSH_AUTH_HOST); + } + else { + state(data, SSH_AUTH_AGENT_INIT); + } + break; + + case SSH_AUTH_HOST: + state(data, SSH_AUTH_AGENT_INIT); + break; + + case SSH_AUTH_AGENT_INIT: +#ifdef HAVE_LIBSSH2_AGENT_API + if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) + && (strstr(sshc->authlist, "publickey") != NULL)) { + + /* Connect to the ssh-agent */ + /* The agent could be shared by a curl thread i believe + but nothing obvious as keys can be added/removed at any time */ + if(!sshc->ssh_agent) { + sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session); + if(!sshc->ssh_agent) { + infof(data, "Could not create agent object"); + + state(data, SSH_AUTH_KEY_INIT); + break; + } + } + + rc = libssh2_agent_connect(sshc->ssh_agent); + if(rc == LIBSSH2_ERROR_EAGAIN) + break; + if(rc < 0) { + infof(data, "Failure connecting to agent"); + state(data, SSH_AUTH_KEY_INIT); + rc = 0; /* clear rc and continue */ + } + else { + state(data, SSH_AUTH_AGENT_LIST); + } + } + else +#endif /* HAVE_LIBSSH2_AGENT_API */ + state(data, SSH_AUTH_KEY_INIT); + break; + + case SSH_AUTH_AGENT_LIST: +#ifdef HAVE_LIBSSH2_AGENT_API + rc = libssh2_agent_list_identities(sshc->ssh_agent); + + if(rc == LIBSSH2_ERROR_EAGAIN) + break; + if(rc < 0) { + infof(data, "Failure requesting identities to agent"); + state(data, SSH_AUTH_KEY_INIT); + rc = 0; /* clear rc and continue */ + } + else { + state(data, SSH_AUTH_AGENT); + sshc->sshagent_prev_identity = NULL; + } +#endif + break; + + case SSH_AUTH_AGENT: +#ifdef HAVE_LIBSSH2_AGENT_API + /* as prev_identity evolves only after an identity user auth finished we + can safely request it again as long as EAGAIN is returned here or by + libssh2_agent_userauth */ + rc = libssh2_agent_get_identity(sshc->ssh_agent, + &sshc->sshagent_identity, + sshc->sshagent_prev_identity); + if(rc == LIBSSH2_ERROR_EAGAIN) + break; + + if(rc == 0) { + rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user, + sshc->sshagent_identity); + + if(rc < 0) { + if(rc != LIBSSH2_ERROR_EAGAIN) { + /* tried and failed? go to next identity */ + sshc->sshagent_prev_identity = sshc->sshagent_identity; + } + break; + } + } + + if(rc < 0) + infof(data, "Failure requesting identities to agent"); + else if(rc == 1) + infof(data, "No identity would match"); + + if(rc == LIBSSH2_ERROR_NONE) { + sshc->authed = TRUE; + infof(data, "Agent based authentication successful"); + state(data, SSH_AUTH_DONE); + } + else { + state(data, SSH_AUTH_KEY_INIT); + rc = 0; /* clear rc and continue */ + } +#endif + break; + + case SSH_AUTH_KEY_INIT: + if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) + && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) { + state(data, SSH_AUTH_KEY); + } + else { + state(data, SSH_AUTH_DONE); + } + break; + + case SSH_AUTH_KEY: + /* Authentication failed. Continue with keyboard-interactive now. */ + rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session, + conn->user, + curlx_uztoui( + strlen(conn->user)), + &kbd_callback); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc == 0) { + sshc->authed = TRUE; + infof(data, "Initialized keyboard interactive authentication"); + } + state(data, SSH_AUTH_DONE); + break; + + case SSH_AUTH_DONE: + if(!sshc->authed) { + failf(data, "Authentication failure"); + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_LOGIN_DENIED; + break; + } + + /* + * At this point we have an authenticated ssh session. + */ + infof(data, "Authentication complete"); + + Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ + + conn->sockfd = sock; + conn->writesockfd = CURL_SOCKET_BAD; + + if(conn->handler->protocol == CURLPROTO_SFTP) { + state(data, SSH_SFTP_INIT); + break; + } + infof(data, "SSH CONNECT phase done"); + state(data, SSH_STOP); + break; + + case SSH_SFTP_INIT: + /* + * Start the libssh2 sftp session + */ + sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session); + if(!sshc->sftp_session) { + char *err_msg = NULL; + if(libssh2_session_last_errno(sshc->ssh_session) == + LIBSSH2_ERROR_EAGAIN) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + failf(data, "Failure initializing sftp session: %s", err_msg); + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_FAILED_INIT; + break; + } + state(data, SSH_SFTP_REALPATH); + break; + + case SSH_SFTP_REALPATH: + { + char tempHome[PATH_MAX]; + + /* + * Get the "home" directory + */ + rc = sftp_libssh2_realpath(sshc->sftp_session, ".", + tempHome, PATH_MAX-1); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc > 0) { + /* It seems that this string is not always NULL terminated */ + tempHome[rc] = '\0'; + sshc->homedir = strdup(tempHome); + if(!sshc->homedir) { + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + data->state.most_recent_ftp_entrypath = sshc->homedir; + } + else { + /* Return the error type */ + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + if(sftperr) + result = sftp_libssh2_error_to_CURLE(sftperr); + else + /* in this case, the error was not in the SFTP level but for example + a time-out or similar */ + result = CURLE_SSH; + sshc->actualcode = result; + DEBUGF(infof(data, "error = %lu makes libcurl = %d", + sftperr, (int)result)); + state(data, SSH_STOP); + break; + } + } + /* This is the last step in the SFTP connect phase. Do note that while + we get the homedir here, we get the "workingpath" in the DO action + since the homedir will remain the same between request but the + working path will not. */ + DEBUGF(infof(data, "SSH CONNECT phase done")); + state(data, SSH_STOP); + break; + + case SSH_SFTP_QUOTE_INIT: + + result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + if(result) { + sshc->actualcode = result; + state(data, SSH_STOP); + break; + } + + if(data->set.quote) { + infof(data, "Sending quote commands"); + sshc->quote_item = data->set.quote; + state(data, SSH_SFTP_QUOTE); + } + else { + state(data, SSH_SFTP_GETINFO); + } + break; + + case SSH_SFTP_POSTQUOTE_INIT: + if(data->set.postquote) { + infof(data, "Sending quote commands"); + sshc->quote_item = data->set.postquote; + state(data, SSH_SFTP_QUOTE); + } + else { + state(data, SSH_STOP); + } + break; + + case SSH_SFTP_QUOTE: + /* Send any quote commands */ + { + const char *cp; + + /* + * Support some of the "FTP" commands + * + * 'sshc->quote_item' is already verified to be non-NULL before it + * switched to this state. + */ + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server responds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + if(strcasecompare("pwd", cmd)) { + /* output debug output if that is requested */ + char *tmp = aprintf("257 \"%s\" is current directory.\n", + sshp->path); + if(!tmp) { + result = CURLE_OUT_OF_MEMORY; + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + break; + } + Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4); + Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); + + /* this sends an FTP-like "header" to the header callback so that the + current directory can be read very similar to how it is read when + using ordinary FTP. */ + result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + free(tmp); + if(result) { + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + } + else + state(data, SSH_SFTP_NEXT_QUOTE); + break; + } + + /* + * the arguments following the command must be separated from the + * command with a space so we can check for it unconditionally + */ + cp = strchr(cmd, ' '); + if(!cp) { + failf(data, "Syntax error command '%s', missing parameter", + cmd); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + + /* + * also, every command takes at least one argument so we get that + * first argument right now + */ + result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error: Bad first parameter to '%s'", cmd); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + break; + } + + /* + * SFTP is a binary protocol, so we do not send text commands + * to the server. Instead, we scan for commands used by + * OpenSSH's sftp program and call the appropriate libssh2 + * functions. + */ + if(strncasecompare(cmd, "chgrp ", 6) || + strncasecompare(cmd, "chmod ", 6) || + strncasecompare(cmd, "chown ", 6) || + strncasecompare(cmd, "atime ", 6) || + strncasecompare(cmd, "mtime ", 6)) { + /* attribute change */ + + /* sshc->quote_path1 contains the mode to set */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in %s: Bad second parameter", cmd); + Curl_safefree(sshc->quote_path1); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + break; + } + memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + state(data, SSH_SFTP_QUOTE_STAT); + break; + } + if(strncasecompare(cmd, "ln ", 3) || + strncasecompare(cmd, "symlink ", 8)) { + /* symbolic linking */ + /* sshc->quote_path1 is the source */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, + "Syntax error in ln/symlink: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + break; + } + state(data, SSH_SFTP_QUOTE_SYMLINK); + break; + } + else if(strncasecompare(cmd, "mkdir ", 6)) { + /* create dir */ + state(data, SSH_SFTP_QUOTE_MKDIR); + break; + } + else if(strncasecompare(cmd, "rename ", 7)) { + /* rename file */ + /* first param is the source path */ + /* second param is the dest. path */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in rename: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + break; + } + state(data, SSH_SFTP_QUOTE_RENAME); + break; + } + else if(strncasecompare(cmd, "rmdir ", 6)) { + /* delete dir */ + state(data, SSH_SFTP_QUOTE_RMDIR); + break; + } + else if(strncasecompare(cmd, "rm ", 3)) { + state(data, SSH_SFTP_QUOTE_UNLINK); + break; + } +#ifdef HAS_STATVFS_SUPPORT + else if(strncasecompare(cmd, "statvfs ", 8)) { + state(data, SSH_SFTP_QUOTE_STATVFS); + break; + } +#endif + + failf(data, "Unknown SFTP command"); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + + case SSH_SFTP_NEXT_QUOTE: + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + + sshc->quote_item = sshc->quote_item->next; + + if(sshc->quote_item) { + state(data, SSH_SFTP_QUOTE); + } + else { + if(sshc->nextstate != SSH_NO_STATE) { + state(data, sshc->nextstate); + sshc->nextstate = SSH_NO_STATE; + } + else { + state(data, SSH_SFTP_GETINFO); + } + } + break; + + case SSH_SFTP_QUOTE_STAT: + { + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server responds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + if(!strncasecompare(cmd, "chmod", 5)) { + /* Since chown and chgrp only set owner OR group but libssh2 wants to + * set them both at once, we need to obtain the current ownership + * first. This takes an extra protocol round trip. + */ + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, + curlx_uztoui(strlen(sshc->quote_path2)), + LIBSSH2_SFTP_STAT, + &sshp->quote_attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc && !sshc->acceptfail) { /* get those attributes */ + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Attempt to get SFTP stats failed: %s", + sftp_libssh2_strerror(sftperr)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + } + + /* Now set the new attributes... */ + if(strncasecompare(cmd, "chgrp", 5)) { + sshp->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10); + sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; + if(sshp->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chgrp gid not a number"); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + } + else if(strncasecompare(cmd, "chmod", 5)) { + sshp->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8); + sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS; + /* permissions are octal */ + if(sshp->quote_attrs.permissions == 0 && + !ISDIGIT(sshc->quote_path1[0])) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chmod permissions not a number"); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + } + else if(strncasecompare(cmd, "chown", 5)) { + sshp->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10); + sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; + if(sshp->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chown uid not a number"); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + } + else if(strncasecompare(cmd, "atime", 5) || + strncasecompare(cmd, "mtime", 5)) { + time_t date = Curl_getdate_capped(sshc->quote_path1); + bool fail = FALSE; + + if(date == -1) { + failf(data, "incorrect date format for %.*s", 5, cmd); + fail = TRUE; + } +#if SIZEOF_TIME_T > SIZEOF_LONG + if(date > 0xffffffff) { + /* if 'long' cannot old >32bit, this date cannot be sent */ + failf(data, "date overflow"); + fail = TRUE; + } +#endif + if(fail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + if(strncasecompare(cmd, "atime", 5)) + sshp->quote_attrs.atime = (unsigned long)date; + else /* mtime */ + sshp->quote_attrs.mtime = (unsigned long)date; + + sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME; + } + + /* Now send the completed structure... */ + state(data, SSH_SFTP_QUOTE_SETSTAT); + break; + } + + case SSH_SFTP_QUOTE_SETSTAT: + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, + curlx_uztoui(strlen(sshc->quote_path2)), + LIBSSH2_SFTP_SETSTAT, + &sshp->quote_attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc && !sshc->acceptfail) { + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Attempt to set SFTP stats failed: %s", + sftp_libssh2_strerror(sftperr)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_SYMLINK: + rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1, + curlx_uztoui(strlen(sshc->quote_path1)), + sshc->quote_path2, + curlx_uztoui(strlen(sshc->quote_path2)), + LIBSSH2_SFTP_SYMLINK); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc && !sshc->acceptfail) { + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "symlink command failed: %s", + sftp_libssh2_strerror(sftperr)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_MKDIR: + rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1, + curlx_uztoui(strlen(sshc->quote_path1)), + (long)data->set.new_directory_perms); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc && !sshc->acceptfail) { + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + Curl_safefree(sshc->quote_path1); + failf(data, "mkdir command failed: %s", + sftp_libssh2_strerror(sftperr)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_RENAME: + rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1, + curlx_uztoui(strlen(sshc->quote_path1)), + sshc->quote_path2, + curlx_uztoui(strlen(sshc->quote_path2)), + LIBSSH2_SFTP_RENAME_OVERWRITE | + LIBSSH2_SFTP_RENAME_ATOMIC | + LIBSSH2_SFTP_RENAME_NATIVE); + + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc && !sshc->acceptfail) { + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "rename command failed: %s", + sftp_libssh2_strerror(sftperr)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_RMDIR: + rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1, + curlx_uztoui(strlen(sshc->quote_path1))); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc && !sshc->acceptfail) { + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + Curl_safefree(sshc->quote_path1); + failf(data, "rmdir command failed: %s", + sftp_libssh2_strerror(sftperr)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_UNLINK: + rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1, + curlx_uztoui(strlen(sshc->quote_path1))); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc && !sshc->acceptfail) { + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + Curl_safefree(sshc->quote_path1); + failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + +#ifdef HAS_STATVFS_SUPPORT + case SSH_SFTP_QUOTE_STATVFS: + { + LIBSSH2_SFTP_STATVFS statvfs; + rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1, + curlx_uztoui(strlen(sshc->quote_path1)), + &statvfs); + + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc && !sshc->acceptfail) { + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + Curl_safefree(sshc->quote_path1); + failf(data, "statvfs command failed: %s", + sftp_libssh2_strerror(sftperr)); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + else if(rc == 0) { + #ifdef _MSC_VER + #define CURL_LIBSSH2_VFS_SIZE_MASK "I64u" + #else + #define CURL_LIBSSH2_VFS_SIZE_MASK "llu" + #endif + char *tmp = aprintf("statvfs:\n" + "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n", + statvfs.f_bsize, statvfs.f_frsize, + statvfs.f_blocks, statvfs.f_bfree, + statvfs.f_bavail, statvfs.f_files, + statvfs.f_ffree, statvfs.f_favail, + statvfs.f_fsid, statvfs.f_flag, + statvfs.f_namemax); + if(!tmp) { + result = CURLE_OUT_OF_MEMORY; + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + break; + } + + result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + free(tmp); + if(result) { + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + } + } + state(data, SSH_SFTP_NEXT_QUOTE); + break; + } +#endif + case SSH_SFTP_GETINFO: + { + if(data->set.get_filetime) { + state(data, SSH_SFTP_FILETIME); + } + else { + state(data, SSH_SFTP_TRANS_INIT); + } + break; + } + + case SSH_SFTP_FILETIME: + { + LIBSSH2_SFTP_ATTRIBUTES attrs; + + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + LIBSSH2_SFTP_STAT, &attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc == 0) { + data->info.filetime = (time_t)attrs.mtime; + } + + state(data, SSH_SFTP_TRANS_INIT); + break; + } + + case SSH_SFTP_TRANS_INIT: + if(data->state.upload) + state(data, SSH_SFTP_UPLOAD_INIT); + else { + if(sshp->path[strlen(sshp->path)-1] == '/') + state(data, SSH_SFTP_READDIR_INIT); + else + state(data, SSH_SFTP_DOWNLOAD_INIT); + } + break; + + case SSH_SFTP_UPLOAD_INIT: + { + unsigned long flags; + /* + * NOTE!!! libssh2 requires that the destination path is a full path + * that includes the destination file and name OR ends in a "/" + * If this is not done the destination file will be named the + * same name as the last directory in the path. + */ + + if(data->state.resume_from) { + LIBSSH2_SFTP_ATTRIBUTES attrs; + if(data->state.resume_from < 0) { + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + LIBSSH2_SFTP_STAT, &attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc) { + data->state.resume_from = 0; + } + else { + curl_off_t size = attrs.filesize; + if(size < 0) { + failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + data->state.resume_from = attrs.filesize; + } + } + } + + if(data->set.remote_append) + /* Try to open for append, but create if nonexisting */ + flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND; + else if(data->state.resume_from > 0) + /* If we have restart position then open for append */ + flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND; + else + /* Clear file before writing (normal behavior) */ + flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC; + + sshc->sftp_handle = + libssh2_sftp_open_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + flags, (long)data->set.new_file_perms, + LIBSSH2_SFTP_OPENFILE); + + if(!sshc->sftp_handle) { + rc = libssh2_session_last_errno(sshc->ssh_session); + + if(LIBSSH2_ERROR_EAGAIN == rc) + break; + + if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc) + /* only when there was an SFTP protocol error can we extract + the sftp error! */ + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + else + sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */ + + if(sshc->secondCreateDirs) { + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = sftperr != LIBSSH2_FX_OK ? + sftp_libssh2_error_to_CURLE(sftperr):CURLE_SSH; + failf(data, "Creating the dir/file failed: %s", + sftp_libssh2_strerror(sftperr)); + break; + } + if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) || + (sftperr == LIBSSH2_FX_FAILURE) || + (sftperr == LIBSSH2_FX_NO_SUCH_PATH)) && + (data->set.ftp_create_missing_dirs && + (strlen(sshp->path) > 1))) { + /* try to create the path remotely */ + rc = 0; /* clear rc and continue */ + sshc->secondCreateDirs = 1; + state(data, SSH_SFTP_CREATE_DIRS_INIT); + break; + } + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = sftperr != LIBSSH2_FX_OK ? + sftp_libssh2_error_to_CURLE(sftperr):CURLE_SSH; + if(!sshc->actualcode) { + /* Sometimes, for some reason libssh2_sftp_last_error() returns zero + even though libssh2_sftp_open() failed previously! We need to + work around that! */ + sshc->actualcode = CURLE_SSH; + sftperr = LIBSSH2_FX_OK; + } + failf(data, "Upload failed: %s (%lu/%d)", + sftperr != LIBSSH2_FX_OK ? + sftp_libssh2_strerror(sftperr):"ssh error", + sftperr, rc); + break; + } + + /* If we have a restart point then we need to seek to the correct + position. */ + if(data->state.resume_from > 0) { + /* Let's read off the proper amount of bytes from the input. */ + if(data->set.seek_func) { + Curl_set_in_callback(data, true); + seekerr = data->set.seek_func(data->set.seek_client, + data->state.resume_from, SEEK_SET); + Curl_set_in_callback(data, false); + } + + if(seekerr != CURL_SEEKFUNC_OK) { + curl_off_t passed = 0; + + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_FTP_COULDNT_USE_REST; + } + /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ + do { + char scratch[4*1024]; + size_t readthisamountnow = + (data->state.resume_from - passed > + (curl_off_t)sizeof(scratch)) ? + sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed); + + size_t actuallyread; + Curl_set_in_callback(data, true); + actuallyread = data->state.fread_func(scratch, 1, + readthisamountnow, + data->state.in); + Curl_set_in_callback(data, false); + + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + return CURLE_FTP_COULDNT_USE_REST; + } + } while(passed < data->state.resume_from); + } + + /* now, decrease the size of the read */ + if(data->state.infilesize > 0) { + data->state.infilesize -= data->state.resume_from; + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + + SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); + } + if(data->state.infilesize > 0) { + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + /* upload data */ + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + conn->sockfd = conn->writesockfd; + + if(result) { + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = result; + } + else { + /* store this original bitmask setup to use later on if we cannot + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh2 sftp send function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_OUT; + + /* since we do not really wait for anything at this point, we want the + state machine to move on as soon as possible so we set a very short + timeout here */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + + state(data, SSH_STOP); + } + break; + } + + case SSH_SFTP_CREATE_DIRS_INIT: + if(strlen(sshp->path) > 1) { + sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ + state(data, SSH_SFTP_CREATE_DIRS); + } + else { + state(data, SSH_SFTP_UPLOAD_INIT); + } + break; + + case SSH_SFTP_CREATE_DIRS: + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { + *sshc->slash_pos = 0; + + infof(data, "Creating directory '%s'", sshp->path); + state(data, SSH_SFTP_CREATE_DIRS_MKDIR); + break; + } + state(data, SSH_SFTP_UPLOAD_INIT); + break; + + case SSH_SFTP_CREATE_DIRS_MKDIR: + /* 'mode' - parameter is preliminary - default to 0644 */ + rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + (long)data->set.new_directory_perms); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + *sshc->slash_pos = '/'; + ++sshc->slash_pos; + if(rc < 0) { + /* + * Abort if failure was not that the dir already exists or the + * permission was denied (creation might succeed further down the + * path) - retry on unspecific FAILURE also + */ + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) && + (sftperr != LIBSSH2_FX_FAILURE) && + (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) { + result = sftp_libssh2_error_to_CURLE(sftperr); + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = result?result:CURLE_SSH; + break; + } + rc = 0; /* clear rc and continue */ + } + state(data, SSH_SFTP_CREATE_DIRS); + break; + + case SSH_SFTP_READDIR_INIT: + Curl_pgrsSetDownloadSize(data, -1); + if(data->req.no_body) { + state(data, SSH_STOP); + break; + } + + /* + * This is a directory that we are trying to get, so produce a directory + * listing + */ + sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session, + sshp->path, + curlx_uztoui( + strlen(sshp->path)), + 0, 0, LIBSSH2_SFTP_OPENDIR); + if(!sshc->sftp_handle) { + if(libssh2_session_last_errno(sshc->ssh_session) == + LIBSSH2_ERROR_EAGAIN) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + failf(data, "Could not open directory for reading: %s", + sftp_libssh2_strerror(sftperr)); + state(data, SSH_SFTP_CLOSE); + result = sftp_libssh2_error_to_CURLE(sftperr); + sshc->actualcode = result?result:CURLE_SSH; + break; + } + sshp->readdir_filename = malloc(PATH_MAX + 1); + if(!sshp->readdir_filename) { + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + sshp->readdir_longentry = malloc(PATH_MAX + 1); + if(!sshp->readdir_longentry) { + Curl_safefree(sshp->readdir_filename); + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + Curl_dyn_init(&sshp->readdir, PATH_MAX * 2); + state(data, SSH_SFTP_READDIR); + break; + + case SSH_SFTP_READDIR: + rc = libssh2_sftp_readdir_ex(sshc->sftp_handle, + sshp->readdir_filename, + PATH_MAX, + sshp->readdir_longentry, + PATH_MAX, + &sshp->readdir_attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc > 0) { + readdir_len = (size_t) rc; + sshp->readdir_filename[readdir_len] = '\0'; + + if(data->set.list_only) { + result = Curl_client_write(data, CLIENTWRITE_BODY, + sshp->readdir_filename, + readdir_len); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, + (char *)"\n", 1); + if(result) { + state(data, SSH_STOP); + break; + } + + } + else { + result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry); + + if(!result) { + if((sshp->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) && + ((sshp->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) == + LIBSSH2_SFTP_S_IFLNK)) { + Curl_dyn_init(&sshp->readdir_link, PATH_MAX); + result = Curl_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path, + sshp->readdir_filename); + state(data, SSH_SFTP_READDIR_LINK); + if(!result) + break; + } + else { + state(data, SSH_SFTP_READDIR_BOTTOM); + break; + } + } + sshc->actualcode = result; + state(data, SSH_SFTP_CLOSE); + break; + } + } + else if(rc == 0) { + Curl_safefree(sshp->readdir_filename); + Curl_safefree(sshp->readdir_longentry); + state(data, SSH_SFTP_READDIR_DONE); + break; + } + else if(rc < 0) { + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + result = sftp_libssh2_error_to_CURLE(sftperr); + sshc->actualcode = result?result:CURLE_SSH; + failf(data, "Could not open remote file for reading: %s :: %d", + sftp_libssh2_strerror(sftperr), + libssh2_session_last_errno(sshc->ssh_session)); + Curl_safefree(sshp->readdir_filename); + Curl_safefree(sshp->readdir_longentry); + state(data, SSH_SFTP_CLOSE); + break; + } + break; + + case SSH_SFTP_READDIR_LINK: + rc = + libssh2_sftp_symlink_ex(sshc->sftp_session, + Curl_dyn_ptr(&sshp->readdir_link), + (unsigned int) + Curl_dyn_len(&sshp->readdir_link), + sshp->readdir_filename, + PATH_MAX, LIBSSH2_SFTP_READLINK); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + Curl_dyn_free(&sshp->readdir_link); + + /* append filename and extra output */ + result = Curl_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename); + + if(result) { + Curl_safefree(sshp->readdir_filename); + Curl_safefree(sshp->readdir_longentry); + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = result; + break; + } + + state(data, SSH_SFTP_READDIR_BOTTOM); + break; + + case SSH_SFTP_READDIR_BOTTOM: + result = Curl_dyn_addn(&sshp->readdir, "\n", 1); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, + Curl_dyn_ptr(&sshp->readdir), + Curl_dyn_len(&sshp->readdir)); + + if(result) { + Curl_dyn_free(&sshp->readdir); + state(data, SSH_STOP); + } + else { + Curl_dyn_reset(&sshp->readdir); + state(data, SSH_SFTP_READDIR); + } + break; + + case SSH_SFTP_READDIR_DONE: + if(libssh2_sftp_closedir(sshc->sftp_handle) == + LIBSSH2_ERROR_EAGAIN) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + sshc->sftp_handle = NULL; + Curl_safefree(sshp->readdir_filename); + Curl_safefree(sshp->readdir_longentry); + + /* no data to transfer */ + Curl_xfer_setup_nop(data); + state(data, SSH_STOP); + break; + + case SSH_SFTP_DOWNLOAD_INIT: + /* + * Work on getting the specified file + */ + sshc->sftp_handle = + libssh2_sftp_open_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + LIBSSH2_FXF_READ, (long)data->set.new_file_perms, + LIBSSH2_SFTP_OPENFILE); + if(!sshc->sftp_handle) { + if(libssh2_session_last_errno(sshc->ssh_session) == + LIBSSH2_ERROR_EAGAIN) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + failf(data, "Could not open remote file for reading: %s", + sftp_libssh2_strerror(sftperr)); + state(data, SSH_SFTP_CLOSE); + result = sftp_libssh2_error_to_CURLE(sftperr); + sshc->actualcode = result?result:CURLE_SSH; + break; + } + state(data, SSH_SFTP_DOWNLOAD_STAT); + break; + + case SSH_SFTP_DOWNLOAD_STAT: + { + LIBSSH2_SFTP_ATTRIBUTES attrs; + + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + LIBSSH2_SFTP_STAT, &attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc || + !(attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) || + (attrs.filesize == 0)) { + /* + * libssh2_sftp_open() did not return an error, so maybe the server + * just does not support stat() + * OR the server does not return a file size with a stat() + * OR file size is 0 + */ + data->req.size = -1; + data->req.maxdownload = -1; + Curl_pgrsSetDownloadSize(data, -1); + } + else { + curl_off_t size = attrs.filesize; + + if(size < 0) { + failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + if(data->state.use_range) { + curl_off_t from, to; + char *ptr; + char *ptr2; + CURLofft to_t; + CURLofft from_t; + + from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from); + if(from_t == CURL_OFFT_FLOW) + return CURLE_RANGE_ERROR; + while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) + ptr++; + to_t = curlx_strtoofft(ptr, &ptr2, 10, &to); + if(to_t == CURL_OFFT_FLOW) + return CURLE_RANGE_ERROR; + if((to_t == CURL_OFFT_INVAL) /* no "to" value given */ + || (to >= size)) { + to = size - 1; + } + if(from_t) { + /* from is relative to end of file */ + from = size - to; + to = size - 1; + } + if(from > size) { + failf(data, "Offset (%" + CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" + CURL_FORMAT_CURL_OFF_T ")", from, + (curl_off_t)attrs.filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + if(from > to) { + from = to; + size = 0; + } + else { + if((to - from) == CURL_OFF_T_MAX) + return CURLE_RANGE_ERROR; + size = to - from + 1; + } + + SFTP_SEEK(sshc->sftp_handle, from); + } + data->req.size = size; + data->req.maxdownload = size; + Curl_pgrsSetDownloadSize(data, size); + } + + /* We can resume if we can seek to the resume position */ + if(data->state.resume_from) { + if(data->state.resume_from < 0) { + /* We are supposed to download the last abs(from) bytes */ + if((curl_off_t)attrs.filesize < -data->state.resume_from) { + failf(data, "Offset (%" + CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" + CURL_FORMAT_CURL_OFF_T ")", + data->state.resume_from, (curl_off_t)attrs.filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* download from where? */ + data->state.resume_from += attrs.filesize; + } + else { + if((curl_off_t)attrs.filesize < data->state.resume_from) { + failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T + ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", + data->state.resume_from, (curl_off_t)attrs.filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + } + /* Now store the number of bytes we are expected to download */ + data->req.size = attrs.filesize - data->state.resume_from; + data->req.maxdownload = attrs.filesize - data->state.resume_from; + Curl_pgrsSetDownloadSize(data, + attrs.filesize - data->state.resume_from); + SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); + } + } + + /* Setup the actual download */ + if(data->req.size == 0) { + /* no data to transfer */ + Curl_xfer_setup_nop(data); + infof(data, "File already completely downloaded"); + state(data, SSH_STOP); + break; + } + Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + conn->writesockfd = conn->sockfd; + + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh2 recv function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_IN; + + if(result) { + /* this should never occur; the close state should be entered + at the time the error occurs */ + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = result; + } + else { + state(data, SSH_STOP); + } + break; + + case SSH_SFTP_CLOSE: + if(sshc->sftp_handle) { + rc = libssh2_sftp_close(sshc->sftp_handle); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc < 0) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); + } + sshc->sftp_handle = NULL; + } + + Curl_safefree(sshp->path); + + DEBUGF(infof(data, "SFTP DONE done")); + + /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT + After nextstate is executed, the control should come back to + SSH_SFTP_CLOSE to pass the correct result back */ + if(sshc->nextstate != SSH_NO_STATE && + sshc->nextstate != SSH_SFTP_CLOSE) { + state(data, sshc->nextstate); + sshc->nextstate = SSH_SFTP_CLOSE; + } + else { + state(data, SSH_STOP); + result = sshc->actualcode; + } + break; + + case SSH_SFTP_SHUTDOWN: + /* during times we get here due to a broken transfer and then the + sftp_handle might not have been taken down so make sure that is done + before we proceed */ + + if(sshc->sftp_handle) { + rc = libssh2_sftp_close(sshc->sftp_handle); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc < 0) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, + NULL, 0); + infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); + } + sshc->sftp_handle = NULL; + } + if(sshc->sftp_session) { + rc = libssh2_sftp_shutdown(sshc->sftp_session); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc < 0) { + infof(data, "Failed to stop libssh2 sftp subsystem"); + } + sshc->sftp_session = NULL; + } + + Curl_safefree(sshc->homedir); + data->state.most_recent_ftp_entrypath = NULL; + + state(data, SSH_SESSION_DISCONNECT); + break; + + case SSH_SCP_TRANS_INIT: + result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + if(result) { + sshc->actualcode = result; + state(data, SSH_STOP); + break; + } + + if(data->state.upload) { + if(data->state.infilesize < 0) { + failf(data, "SCP requires a known file size for upload"); + sshc->actualcode = CURLE_UPLOAD_FAILED; + state(data, SSH_SCP_CHANNEL_FREE); + break; + } + state(data, SSH_SCP_UPLOAD_INIT); + } + else { + state(data, SSH_SCP_DOWNLOAD_INIT); + } + break; + + case SSH_SCP_UPLOAD_INIT: + /* + * libssh2 requires that the destination path is a full path that + * includes the destination file and name OR ends in a "/" . If this is + * not done the destination file will be named the same name as the last + * directory in the path. + */ + sshc->ssh_channel = + SCP_SEND(sshc->ssh_session, sshp->path, data->set.new_file_perms, + data->state.infilesize); + if(!sshc->ssh_channel) { + int ssh_err; + char *err_msg = NULL; + + if(libssh2_session_last_errno(sshc->ssh_session) == + LIBSSH2_ERROR_EAGAIN) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + + ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0)); + failf(data, "%s", err_msg); + state(data, SSH_SCP_CHANNEL_FREE); + sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err); + /* Map generic errors to upload failed */ + if(sshc->actualcode == CURLE_SSH || + sshc->actualcode == CURLE_REMOTE_FILE_NOT_FOUND) + sshc->actualcode = CURLE_UPLOAD_FAILED; + break; + } + + /* upload data */ + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + conn->sockfd = conn->writesockfd; + + if(result) { + state(data, SSH_SCP_CHANNEL_FREE); + sshc->actualcode = result; + } + else { + /* store this original bitmask setup to use later on if we cannot + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh2 scp send function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_OUT; + + state(data, SSH_STOP); + } + break; + + case SSH_SCP_DOWNLOAD_INIT: + { + curl_off_t bytecount; + + /* + * We must check the remote file; if it is a directory no values will + * be set in sb + */ + + /* + * If support for >2GB files exists, use it. + */ + + /* get a fresh new channel from the ssh layer */ +#if LIBSSH2_VERSION_NUM < 0x010700 + struct stat sb; + memset(&sb, 0, sizeof(struct stat)); + sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session, + sshp->path, &sb); +#else + libssh2_struct_stat sb; + memset(&sb, 0, sizeof(libssh2_struct_stat)); + sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session, + sshp->path, &sb); +#endif + + if(!sshc->ssh_channel) { + int ssh_err; + char *err_msg = NULL; + + if(libssh2_session_last_errno(sshc->ssh_session) == + LIBSSH2_ERROR_EAGAIN) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + + + ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0)); + failf(data, "%s", err_msg); + state(data, SSH_SCP_CHANNEL_FREE); + sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err); + break; + } + + /* download data */ + bytecount = (curl_off_t)sb.st_size; + data->req.maxdownload = (curl_off_t)sb.st_size; + Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + conn->writesockfd = conn->sockfd; + + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh2 recv function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_IN; + + if(result) { + state(data, SSH_SCP_CHANNEL_FREE); + sshc->actualcode = result; + } + else + state(data, SSH_STOP); + } + break; + + case SSH_SCP_DONE: + if(data->state.upload) + state(data, SSH_SCP_SEND_EOF); + else + state(data, SSH_SCP_CHANNEL_FREE); + break; + + case SSH_SCP_SEND_EOF: + if(sshc->ssh_channel) { + rc = libssh2_channel_send_eof(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to send libssh2 channel EOF: %d %s", + rc, err_msg); + } + } + state(data, SSH_SCP_WAIT_EOF); + break; + + case SSH_SCP_WAIT_EOF: + if(sshc->ssh_channel) { + rc = libssh2_channel_wait_eof(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to get channel EOF: %d %s", rc, err_msg); + } + } + state(data, SSH_SCP_WAIT_CLOSE); + break; + + case SSH_SCP_WAIT_CLOSE: + if(sshc->ssh_channel) { + rc = libssh2_channel_wait_closed(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Channel failed to close: %d %s", rc, err_msg); + } + } + state(data, SSH_SCP_CHANNEL_FREE); + break; + + case SSH_SCP_CHANNEL_FREE: + if(sshc->ssh_channel) { + rc = libssh2_channel_free(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc < 0) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to free libssh2 scp subsystem: %d %s", + rc, err_msg); + } + sshc->ssh_channel = NULL; + } + DEBUGF(infof(data, "SCP DONE phase complete")); +#if 0 /* PREV */ + state(data, SSH_SESSION_DISCONNECT); +#endif + state(data, SSH_STOP); + result = sshc->actualcode; + break; + + case SSH_SESSION_DISCONNECT: + /* during weird times when we have been prematurely aborted, the channel + is still alive when we reach this state and we MUST kill the channel + properly first */ + if(sshc->ssh_channel) { + rc = libssh2_channel_free(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc < 0) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to free libssh2 scp subsystem: %d %s", + rc, err_msg); + } + sshc->ssh_channel = NULL; + } + + if(sshc->ssh_session) { + rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown"); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc < 0) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to disconnect libssh2 session: %d %s", + rc, err_msg); + } + } + + Curl_safefree(sshc->homedir); + data->state.most_recent_ftp_entrypath = NULL; + + state(data, SSH_SESSION_FREE); + break; + + case SSH_SESSION_FREE: +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + if(sshc->kh) { + libssh2_knownhost_free(sshc->kh); + sshc->kh = NULL; + } +#endif + +#ifdef HAVE_LIBSSH2_AGENT_API + if(sshc->ssh_agent) { + rc = libssh2_agent_disconnect(sshc->ssh_agent); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc < 0) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to disconnect from libssh2 agent: %d %s", + rc, err_msg); + } + libssh2_agent_free(sshc->ssh_agent); + sshc->ssh_agent = NULL; + + /* NB: there is no need to free identities, they are part of internal + agent stuff */ + sshc->sshagent_identity = NULL; + sshc->sshagent_prev_identity = NULL; + } +#endif + + if(sshc->ssh_session) { + rc = libssh2_session_free(sshc->ssh_session); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + if(rc < 0) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg); + } + sshc->ssh_session = NULL; + } + + /* worst-case scenario cleanup */ + + DEBUGASSERT(sshc->ssh_session == NULL); + DEBUGASSERT(sshc->ssh_channel == NULL); + DEBUGASSERT(sshc->sftp_session == NULL); + DEBUGASSERT(sshc->sftp_handle == NULL); +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + DEBUGASSERT(sshc->kh == NULL); +#endif +#ifdef HAVE_LIBSSH2_AGENT_API + DEBUGASSERT(sshc->ssh_agent == NULL); +#endif + + Curl_safefree(sshc->rsa_pub); + Curl_safefree(sshc->rsa); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + Curl_safefree(sshc->homedir); + + /* the code we are about to return */ + result = sshc->actualcode; + + memset(sshc, 0, sizeof(struct ssh_conn)); + + connclose(conn, "SSH session free"); + sshc->state = SSH_SESSION_FREE; /* current */ + sshc->nextstate = SSH_NO_STATE; + state(data, SSH_STOP); + break; + + case SSH_QUIT: + default: + /* internal error */ + sshc->nextstate = SSH_NO_STATE; + state(data, SSH_STOP); + break; + } + + } while(!rc && (sshc->state != SSH_STOP)); + + if(rc == LIBSSH2_ERROR_EAGAIN) { + /* we would block, we need to wait for the socket to be ready (in the + right direction too)! */ + *block = TRUE; + } + + return result; +} + +/* called by the multi interface to figure out what socket(s) to wait for and + for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ +static int ssh_getsock(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *sock) +{ + int bitmap = GETSOCK_BLANK; + (void)data; + + sock[0] = conn->sock[FIRSTSOCKET]; + + if(conn->waitfor & KEEP_RECV) + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + + if(conn->waitfor & KEEP_SEND) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + return bitmap; +} + +/* + * When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this + * function is used to figure out in what direction and stores this info so + * that the multi interface can take advantage of it. Make sure to call this + * function in all cases so that when it _does not_ return EAGAIN we can + * restore the default wait bits. + */ +static void ssh_block2waitfor(struct Curl_easy *data, bool block) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + int dir = 0; + if(block) { + dir = libssh2_session_block_directions(sshc->ssh_session); + if(dir) { + /* translate the libssh2 define bits into our own bit defines */ + conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) | + ((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0); + } + } + if(!dir) + /* It did not block or libssh2 did not reveal in which direction, put back + the original set */ + conn->waitfor = sshc->orig_waitfor; +} + +/* called repeatedly until done from multi.c */ +static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result = CURLE_OK; + bool block; /* we store the status and use that to provide a ssh_getsock() + implementation */ + do { + result = ssh_statemach_act(data, &block); + *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; + /* if there is no error, it is not done and it did not EWOULDBLOCK, then + try again */ + } while(!result && !*done && !block); + ssh_block2waitfor(data, block); + + return result; +} + +static CURLcode ssh_block_statemach(struct Curl_easy *data, + struct connectdata *conn, + bool disconnect) +{ + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result = CURLE_OK; + struct curltime dis = Curl_now(); + + while((sshc->state != SSH_STOP) && !result) { + bool block; + timediff_t left = 1000; + struct curltime now = Curl_now(); + + result = ssh_statemach_act(data, &block); + if(result) + break; + + if(!disconnect) { + if(Curl_pgrsUpdate(data)) + return CURLE_ABORTED_BY_CALLBACK; + + result = Curl_speedcheck(data, now); + if(result) + break; + + left = Curl_timeleft(data, NULL, FALSE); + if(left < 0) { + failf(data, "Operation timed out"); + return CURLE_OPERATION_TIMEDOUT; + } + } + else if(Curl_timediff(now, dis) > 1000) { + /* disconnect timeout */ + failf(data, "Disconnect timed out"); + result = CURLE_OK; + break; + } + + if(block) { + int dir = libssh2_session_block_directions(sshc->ssh_session); + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + curl_socket_t fd_read = CURL_SOCKET_BAD; + curl_socket_t fd_write = CURL_SOCKET_BAD; + if(LIBSSH2_SESSION_BLOCK_INBOUND & dir) + fd_read = sock; + if(LIBSSH2_SESSION_BLOCK_OUTBOUND & dir) + fd_write = sock; + /* wait for the socket to become ready */ + (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, + left>1000?1000:left); + } + } + + return result; +} + +/* + * SSH setup and connection + */ +static CURLcode ssh_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + struct SSHPROTO *ssh; + (void)conn; + + data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO)); + if(!ssh) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static Curl_recv scp_recv, sftp_recv; +static Curl_send scp_send, sftp_send; + +#ifndef CURL_DISABLE_PROXY +static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer, + size_t length, int flags, void **abstract) +{ + struct Curl_easy *data = (struct Curl_easy *)*abstract; + ssize_t nread; + CURLcode result; + struct connectdata *conn = data->conn; + Curl_recv *backup = conn->recv[0]; + struct ssh_conn *ssh = &conn->proto.sshc; + int socknum = Curl_conn_sockindex(data, sock); + (void)flags; + + /* swap in the TLS reader function for this call only, and then swap back + the SSH one again */ + conn->recv[0] = ssh->tls_recv; + result = Curl_conn_recv(data, socknum, buffer, length, &nread); + conn->recv[0] = backup; + if(result == CURLE_AGAIN) + return -EAGAIN; /* magic return code for libssh2 */ + else if(result) + return -1; /* generic error */ + Curl_debug(data, CURLINFO_DATA_IN, (char *)buffer, (size_t)nread); + return nread; +} + +static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer, + size_t length, int flags, void **abstract) +{ + struct Curl_easy *data = (struct Curl_easy *)*abstract; + size_t nwrite; + CURLcode result; + struct connectdata *conn = data->conn; + Curl_send *backup = conn->send[0]; + struct ssh_conn *ssh = &conn->proto.sshc; + int socknum = Curl_conn_sockindex(data, sock); + (void)flags; + + /* swap in the TLS writer function for this call only, and then swap back + the SSH one again */ + conn->send[0] = ssh->tls_send; + result = Curl_conn_send(data, socknum, buffer, length, &nwrite); + conn->send[0] = backup; + if(result == CURLE_AGAIN) + return -EAGAIN; /* magic return code for libssh2 */ + else if(result) + return -1; /* error */ + Curl_debug(data, CURLINFO_DATA_OUT, (char *)buffer, nwrite); + return (ssize_t)nwrite; +} +#endif + +/* + * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to + * do protocol-specific actions at connect-time. + */ +static CURLcode ssh_connect(struct Curl_easy *data, bool *done) +{ +#ifdef CURL_LIBSSH2_DEBUG + curl_socket_t sock; +#endif + struct ssh_conn *sshc; + CURLcode result; + struct connectdata *conn = data->conn; + + /* initialize per-handle data if not already */ + if(!data->req.p.ssh) { + result = ssh_setup_connection(data, conn); + if(result) + return result; + } + + /* We default to persistent connections. We set this already in this connect + function to make the reuse checks properly be able to check this bit. */ + connkeep(conn, "SSH default"); + + sshc = &conn->proto.sshc; + +#ifdef CURL_LIBSSH2_DEBUG + if(conn->user) { + infof(data, "User: %s", conn->user); + } + if(conn->passwd) { + infof(data, "Password: %s", conn->passwd); + } + sock = conn->sock[FIRSTSOCKET]; +#endif /* CURL_LIBSSH2_DEBUG */ + + /* libcurl MUST to set custom memory functions so that the kbd_callback + function's memory allocations can be properly freed */ + sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc, + my_libssh2_free, + my_libssh2_realloc, data); + + if(!sshc->ssh_session) { + failf(data, "Failure initialising ssh session"); + return CURLE_FAILED_INIT; + } + + /* Set the packet read timeout if the libssh2 version supports it */ +#if LIBSSH2_VERSION_NUM >= 0x010B00 + if(data->set.server_response_timeout > 0) { + libssh2_session_set_read_timeout(sshc->ssh_session, + (long)(data->set.server_response_timeout / 1000)); + } +#endif + +#ifndef CURL_DISABLE_PROXY + if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { + /* + Setup libssh2 callbacks to make it read/write TLS from the socket. + + ssize_t + recvcb(libssh2_socket_t sock, void *buffer, size_t length, + int flags, void **abstract); + + ssize_t + sendcb(libssh2_socket_t sock, const void *buffer, size_t length, + int flags, void **abstract); + + */ +#if LIBSSH2_VERSION_NUM >= 0x010b01 + infof(data, "Uses HTTPS proxy"); + libssh2_session_callback_set2(sshc->ssh_session, + LIBSSH2_CALLBACK_RECV, + (libssh2_cb_generic *)ssh_tls_recv); + libssh2_session_callback_set2(sshc->ssh_session, + LIBSSH2_CALLBACK_SEND, + (libssh2_cb_generic *)ssh_tls_send); +#else + /* + * This crazy union dance is here to avoid assigning a void pointer a + * function pointer as it is invalid C. The problem is of course that + * libssh2 has such an API... + */ + union receive { + void *recvp; + ssize_t (*recvptr)(libssh2_socket_t, void *, size_t, int, void **); + }; + union transfer { + void *sendp; + ssize_t (*sendptr)(libssh2_socket_t, const void *, size_t, int, void **); + }; + union receive sshrecv; + union transfer sshsend; + + sshrecv.recvptr = ssh_tls_recv; + sshsend.sendptr = ssh_tls_send; + + infof(data, "Uses HTTPS proxy"); + libssh2_session_callback_set(sshc->ssh_session, + LIBSSH2_CALLBACK_RECV, sshrecv.recvp); + libssh2_session_callback_set(sshc->ssh_session, + LIBSSH2_CALLBACK_SEND, sshsend.sendp); +#endif + + /* Store the underlying TLS recv/send function pointers to be used when + reading from the proxy */ + sshc->tls_recv = conn->recv[FIRSTSOCKET]; + sshc->tls_send = conn->send[FIRSTSOCKET]; + } + +#endif /* CURL_DISABLE_PROXY */ + if(conn->handler->protocol & CURLPROTO_SCP) { + conn->recv[FIRSTSOCKET] = scp_recv; + conn->send[FIRSTSOCKET] = scp_send; + } + else { + conn->recv[FIRSTSOCKET] = sftp_recv; + conn->send[FIRSTSOCKET] = sftp_send; + } + + if(data->set.ssh_compression) { +#if LIBSSH2_VERSION_NUM >= 0x010208 + if(libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0) +#endif + infof(data, "Failed to enable compression for ssh session"); + } + +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + if(data->set.str[STRING_SSH_KNOWNHOSTS]) { + int rc; + sshc->kh = libssh2_knownhost_init(sshc->ssh_session); + if(!sshc->kh) { + libssh2_session_free(sshc->ssh_session); + sshc->ssh_session = NULL; + return CURLE_FAILED_INIT; + } + + /* read all known hosts from there */ + rc = libssh2_knownhost_readfile(sshc->kh, + data->set.str[STRING_SSH_KNOWNHOSTS], + LIBSSH2_KNOWNHOST_FILE_OPENSSH); + if(rc < 0) + infof(data, "Failed to read known hosts from %s", + data->set.str[STRING_SSH_KNOWNHOSTS]); + } +#endif /* HAVE_LIBSSH2_KNOWNHOST_API */ + +#ifdef CURL_LIBSSH2_DEBUG + libssh2_trace(sshc->ssh_session, ~0); + infof(data, "SSH socket: %d", (int)sock); +#endif /* CURL_LIBSSH2_DEBUG */ + + state(data, SSH_INIT); + + result = ssh_multi_statemach(data, done); + + return result; +} + +/* + *********************************************************************** + * + * scp_perform() + * + * This is the actual DO function for SCP. Get a file according to + * the options previously setup. + */ + +static +CURLcode scp_perform(struct Curl_easy *data, + bool *connected, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + + DEBUGF(infof(data, "DO phase starts")); + + *dophase_done = FALSE; /* not done yet */ + + /* start the first command in the DO phase */ + state(data, SSH_SCP_TRANS_INIT); + + /* run the state-machine */ + result = ssh_multi_statemach(data, dophase_done); + + *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +/* called from multi.c while DOing */ +static CURLcode scp_doing(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result; + result = ssh_multi_statemach(data, dophase_done); + + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + return result; +} + +/* + * The DO function is generic for both protocols. There was previously two + * separate ones but this way means less duplicated code. + */ + +static CURLcode ssh_do(struct Curl_easy *data, bool *done) +{ + CURLcode result; + bool connected = 0; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + + *done = FALSE; /* default to false */ + + data->req.size = -1; /* make sure this is unknown at this point */ + + sshc->actualcode = CURLE_OK; /* reset error code */ + sshc->secondCreateDirs = 0; /* reset the create dir attempt state + variable */ + + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); + + if(conn->handler->protocol & CURLPROTO_SCP) + result = scp_perform(data, &connected, done); + else + result = sftp_perform(data, &connected, done); + + return result; +} + +/* BLOCKING, but the function is using the state machine so the only reason + this is still blocking is that the multi interface code has no support for + disconnecting operations that takes a while */ +static CURLcode scp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + CURLcode result = CURLE_OK; + struct ssh_conn *sshc = &conn->proto.sshc; + (void) dead_connection; + + if(sshc->ssh_session) { + /* only if there is a session still around to use! */ + state(data, SSH_SESSION_DISCONNECT); + result = ssh_block_statemach(data, conn, TRUE); + } + + return result; +} + +/* generic done function for both SCP and SFTP called from their specific + done functions */ +static CURLcode ssh_done(struct Curl_easy *data, CURLcode status) +{ + CURLcode result = CURLE_OK; + struct SSHPROTO *sshp = data->req.p.ssh; + struct connectdata *conn = data->conn; + + if(!status) + /* run the state-machine */ + result = ssh_block_statemach(data, conn, FALSE); + else + result = status; + + Curl_safefree(sshp->path); + Curl_safefree(sshp->readdir_filename); + Curl_safefree(sshp->readdir_longentry); + Curl_dyn_free(&sshp->readdir); + + if(Curl_pgrsDone(data)) + return CURLE_ABORTED_BY_CALLBACK; + + data->req.keepon = 0; /* clear all bits */ + return result; +} + + +static CURLcode scp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + (void)premature; /* not used */ + + if(!status) + state(data, SSH_SCP_DONE); + + return ssh_done(data, status); + +} + +static ssize_t scp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, CURLcode *err) +{ + ssize_t nwrite; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + (void)sockindex; /* we only support SCP on the fixed known primary socket */ + + /* libssh2_channel_write() returns int! */ + nwrite = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len); + + ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE); + + if(nwrite == LIBSSH2_ERROR_EAGAIN) { + *err = CURLE_AGAIN; + nwrite = 0; + } + else if(nwrite < LIBSSH2_ERROR_NONE) { + *err = libssh2_session_error_to_CURLE((int)nwrite); + nwrite = -1; + } + + return nwrite; +} + +static ssize_t scp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, CURLcode *err) +{ + ssize_t nread; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + (void)sockindex; /* we only support SCP on the fixed known primary socket */ + + /* libssh2_channel_read() returns int */ + nread = (ssize_t) libssh2_channel_read(sshc->ssh_channel, mem, len); + + ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE); + if(nread == LIBSSH2_ERROR_EAGAIN) { + *err = CURLE_AGAIN; + nread = -1; + } + + return nread; +} + +/* + * =============== SFTP =============== + */ + +/* + *********************************************************************** + * + * sftp_perform() + * + * This is the actual DO function for SFTP. Get a file/directory according to + * the options previously setup. + */ + +static +CURLcode sftp_perform(struct Curl_easy *data, + bool *connected, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + + DEBUGF(infof(data, "DO phase starts")); + + *dophase_done = FALSE; /* not done yet */ + + /* start the first command in the DO phase */ + state(data, SSH_SFTP_QUOTE_INIT); + + /* run the state-machine */ + result = ssh_multi_statemach(data, dophase_done); + + *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +/* called from multi.c while DOing */ +static CURLcode sftp_doing(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = ssh_multi_statemach(data, dophase_done); + + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + return result; +} + +/* BLOCKING, but the function is using the state machine so the only reason + this is still blocking is that the multi interface code has no support for + disconnecting operations that takes a while */ +static CURLcode sftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) +{ + CURLcode result = CURLE_OK; + struct ssh_conn *sshc = &conn->proto.sshc; + (void) dead_connection; + + DEBUGF(infof(data, "SSH DISCONNECT starts now")); + + if(sshc->ssh_session) { + /* only if there is a session still around to use! */ + state(data, SSH_SFTP_SHUTDOWN); + result = ssh_block_statemach(data, conn, TRUE); + } + + DEBUGF(infof(data, "SSH DISCONNECT is done")); + + return result; + +} + +static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + + if(!status) { + /* Post quote commands are executed after the SFTP_CLOSE state to avoid + errors that could happen due to open file handles during POSTQUOTE + operation */ + if(!premature && data->set.postquote && !conn->bits.retry) + sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT; + state(data, SSH_SFTP_CLOSE); + } + return ssh_done(data, status); +} + +/* return number of sent bytes */ +static ssize_t sftp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, CURLcode *err) +{ + ssize_t nwrite; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + (void)sockindex; + + nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len); + + ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE); + + if(nwrite == LIBSSH2_ERROR_EAGAIN) { + *err = CURLE_AGAIN; + nwrite = 0; + } + else if(nwrite < LIBSSH2_ERROR_NONE) { + *err = libssh2_session_error_to_CURLE((int)nwrite); + nwrite = -1; + } + + return nwrite; +} + +/* + * Return number of received (decrypted) bytes + * or <0 on error + */ +static ssize_t sftp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, CURLcode *err) +{ + ssize_t nread; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + (void)sockindex; + + nread = libssh2_sftp_read(sshc->sftp_handle, mem, len); + + ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE); + + if(nread == LIBSSH2_ERROR_EAGAIN) { + *err = CURLE_AGAIN; + nread = -1; + + } + else if(nread < 0) { + *err = libssh2_session_error_to_CURLE((int)nread); + } + return nread; +} + +static const char *sftp_libssh2_strerror(unsigned long err) +{ + switch(err) { + case LIBSSH2_FX_NO_SUCH_FILE: + return "No such file or directory"; + + case LIBSSH2_FX_PERMISSION_DENIED: + return "Permission denied"; + + case LIBSSH2_FX_FAILURE: + return "Operation failed"; + + case LIBSSH2_FX_BAD_MESSAGE: + return "Bad message from SFTP server"; + + case LIBSSH2_FX_NO_CONNECTION: + return "Not connected to SFTP server"; + + case LIBSSH2_FX_CONNECTION_LOST: + return "Connection to SFTP server lost"; + + case LIBSSH2_FX_OP_UNSUPPORTED: + return "Operation not supported by SFTP server"; + + case LIBSSH2_FX_INVALID_HANDLE: + return "Invalid handle"; + + case LIBSSH2_FX_NO_SUCH_PATH: + return "No such file or directory"; + + case LIBSSH2_FX_FILE_ALREADY_EXISTS: + return "File already exists"; + + case LIBSSH2_FX_WRITE_PROTECT: + return "File is write protected"; + + case LIBSSH2_FX_NO_MEDIA: + return "No media"; + + case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: + return "Disk full"; + + case LIBSSH2_FX_QUOTA_EXCEEDED: + return "User quota exceeded"; + + case LIBSSH2_FX_UNKNOWN_PRINCIPLE: + return "Unknown principle"; + + case LIBSSH2_FX_LOCK_CONFlICT: + return "File lock conflict"; + + case LIBSSH2_FX_DIR_NOT_EMPTY: + return "Directory not empty"; + + case LIBSSH2_FX_NOT_A_DIRECTORY: + return "Not a directory"; + + case LIBSSH2_FX_INVALID_FILENAME: + return "Invalid filename"; + + case LIBSSH2_FX_LINK_LOOP: + return "Link points to itself"; + } + return "Unknown error in libssh2"; +} + +CURLcode Curl_ssh_init(void) +{ +#ifdef HAVE_LIBSSH2_INIT + if(libssh2_init(0)) { + DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n")); + return CURLE_FAILED_INIT; + } +#endif + return CURLE_OK; +} + +void Curl_ssh_cleanup(void) +{ +#ifdef HAVE_LIBSSH2_EXIT + (void)libssh2_exit(); +#endif +} + +void Curl_ssh_version(char *buffer, size_t buflen) +{ + (void)msnprintf(buffer, buflen, "libssh2/%s", CURL_LIBSSH2_VERSION); +} + +/* The SSH session is associated with the *CONNECTION* but the callback user + * pointer is an easy handle pointer. This function allows us to reassign the + * user pointer to the *CURRENT* (new) easy handle. + */ +static void ssh_attach(struct Curl_easy *data, struct connectdata *conn) +{ + DEBUGASSERT(data); + DEBUGASSERT(conn); + if(conn->handler->protocol & PROTO_FAMILY_SSH) { + struct ssh_conn *sshc = &conn->proto.sshc; + if(sshc->ssh_session) { + /* only re-attach if the session already exists */ + void **abstract = libssh2_session_abstract(sshc->ssh_session); + *abstract = data; + } + } +} +#endif /* USE_LIBSSH2 */ diff --git a/third_party/curl/lib/vssh/ssh.h b/third_party/curl/lib/vssh/ssh.h new file mode 100644 index 0000000000..64ef332988 --- /dev/null +++ b/third_party/curl/lib/vssh/ssh.h @@ -0,0 +1,273 @@ +#ifndef HEADER_CURL_SSH_H +#define HEADER_CURL_SSH_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_LIBSSH2) +#include +#include +#elif defined(USE_LIBSSH) +#include +#include +#elif defined(USE_WOLFSSH) +#include +#include +#endif + +/**************************************************************************** + * SSH unique setup + ***************************************************************************/ +typedef enum { + SSH_NO_STATE = -1, /* Used for "nextState" so say there is none */ + SSH_STOP = 0, /* do nothing state, stops the state machine */ + + SSH_INIT, /* First state in SSH-CONNECT */ + SSH_S_STARTUP, /* Session startup */ + SSH_HOSTKEY, /* verify hostkey */ + SSH_AUTHLIST, + SSH_AUTH_PKEY_INIT, + SSH_AUTH_PKEY, + SSH_AUTH_PASS_INIT, + SSH_AUTH_PASS, + SSH_AUTH_AGENT_INIT, /* initialize then wait for connection to agent */ + SSH_AUTH_AGENT_LIST, /* ask for list then wait for entire list to come */ + SSH_AUTH_AGENT, /* attempt one key at a time */ + SSH_AUTH_HOST_INIT, + SSH_AUTH_HOST, + SSH_AUTH_KEY_INIT, + SSH_AUTH_KEY, + SSH_AUTH_GSSAPI, + SSH_AUTH_DONE, + SSH_SFTP_INIT, + SSH_SFTP_REALPATH, /* Last state in SSH-CONNECT */ + + SSH_SFTP_QUOTE_INIT, /* First state in SFTP-DO */ + SSH_SFTP_POSTQUOTE_INIT, /* (Possibly) First state in SFTP-DONE */ + SSH_SFTP_QUOTE, + SSH_SFTP_NEXT_QUOTE, + SSH_SFTP_QUOTE_STAT, + SSH_SFTP_QUOTE_SETSTAT, + SSH_SFTP_QUOTE_SYMLINK, + SSH_SFTP_QUOTE_MKDIR, + SSH_SFTP_QUOTE_RENAME, + SSH_SFTP_QUOTE_RMDIR, + SSH_SFTP_QUOTE_UNLINK, + SSH_SFTP_QUOTE_STATVFS, + SSH_SFTP_GETINFO, + SSH_SFTP_FILETIME, + SSH_SFTP_TRANS_INIT, + SSH_SFTP_UPLOAD_INIT, + SSH_SFTP_CREATE_DIRS_INIT, + SSH_SFTP_CREATE_DIRS, + SSH_SFTP_CREATE_DIRS_MKDIR, + SSH_SFTP_READDIR_INIT, + SSH_SFTP_READDIR, + SSH_SFTP_READDIR_LINK, + SSH_SFTP_READDIR_BOTTOM, + SSH_SFTP_READDIR_DONE, + SSH_SFTP_DOWNLOAD_INIT, + SSH_SFTP_DOWNLOAD_STAT, /* Last state in SFTP-DO */ + SSH_SFTP_CLOSE, /* Last state in SFTP-DONE */ + SSH_SFTP_SHUTDOWN, /* First state in SFTP-DISCONNECT */ + SSH_SCP_TRANS_INIT, /* First state in SCP-DO */ + SSH_SCP_UPLOAD_INIT, + SSH_SCP_DOWNLOAD_INIT, + SSH_SCP_DOWNLOAD, + SSH_SCP_DONE, + SSH_SCP_SEND_EOF, + SSH_SCP_WAIT_EOF, + SSH_SCP_WAIT_CLOSE, + SSH_SCP_CHANNEL_FREE, /* Last state in SCP-DONE */ + SSH_SESSION_DISCONNECT, /* First state in SCP-DISCONNECT */ + SSH_SESSION_FREE, /* Last state in SCP/SFTP-DISCONNECT */ + SSH_QUIT, + SSH_LAST /* never used */ +} sshstate; + +/* this struct is used in the HandleData struct which is part of the + Curl_easy, which means this is used on a per-easy handle basis. + Everything that is strictly related to a connection is banned from this + struct. */ +struct SSHPROTO { + char *path; /* the path we operate on */ +#ifdef USE_LIBSSH2 + struct dynbuf readdir_link; + struct dynbuf readdir; + char *readdir_filename; + char *readdir_longentry; + + LIBSSH2_SFTP_ATTRIBUTES quote_attrs; /* used by the SFTP_QUOTE state */ + + /* Here's a set of struct members used by the SFTP_READDIR state */ + LIBSSH2_SFTP_ATTRIBUTES readdir_attrs; +#endif +}; + +/* ssh_conn is used for struct connection-oriented data in the connectdata + struct */ +struct ssh_conn { + const char *authlist; /* List of auth. methods, managed by libssh2 */ + + /* common */ + const char *passphrase; /* pass-phrase to use */ + char *rsa_pub; /* strdup'ed public key file */ + char *rsa; /* strdup'ed private key file */ + bool authed; /* the connection has been authenticated fine */ + bool acceptfail; /* used by the SFTP_QUOTE (continue if + quote command fails) */ + sshstate state; /* always use ssh.c:state() to change state! */ + sshstate nextstate; /* the state to goto after stopping */ + CURLcode actualcode; /* the actual error code */ + struct curl_slist *quote_item; /* for the quote option */ + char *quote_path1; /* two generic pointers for the QUOTE stuff */ + char *quote_path2; + + char *homedir; /* when doing SFTP we figure out home dir in the + connect phase */ + /* end of READDIR stuff */ + + int secondCreateDirs; /* counter use by the code to see if the + second attempt has been made to change + to/create a directory */ + int orig_waitfor; /* default READ/WRITE bits wait for */ + char *slash_pos; /* used by the SFTP_CREATE_DIRS state */ + +#if defined(USE_LIBSSH) + char *readdir_linkPath; + size_t readdir_len; + struct dynbuf readdir_buf; +/* our variables */ + unsigned kbd_state; /* 0 or 1 */ + ssh_key privkey; + ssh_key pubkey; + unsigned int auth_methods; + ssh_session ssh_session; + ssh_scp scp_session; + sftp_session sftp_session; + sftp_file sftp_file; + sftp_dir sftp_dir; + + unsigned sftp_recv_state; /* 0 or 1 */ + int sftp_file_index; /* for async read */ + sftp_attributes readdir_attrs; /* used by the SFTP readdir actions */ + sftp_attributes readdir_link_attrs; /* used by the SFTP readdir actions */ + sftp_attributes quote_attrs; /* used by the SFTP_QUOTE state */ + + const char *readdir_filename; /* points within readdir_attrs */ + const char *readdir_longentry; + char *readdir_tmp; +#elif defined(USE_LIBSSH2) + LIBSSH2_SESSION *ssh_session; /* Secure Shell session */ + LIBSSH2_CHANNEL *ssh_channel; /* Secure Shell channel handle */ + LIBSSH2_SFTP *sftp_session; /* SFTP handle */ + LIBSSH2_SFTP_HANDLE *sftp_handle; + +#ifndef CURL_DISABLE_PROXY + /* for HTTPS proxy storage */ + Curl_recv *tls_recv; + Curl_send *tls_send; +#endif + +#ifdef HAVE_LIBSSH2_AGENT_API + LIBSSH2_AGENT *ssh_agent; /* proxy to ssh-agent/pageant */ + struct libssh2_agent_publickey *sshagent_identity, + *sshagent_prev_identity; +#endif + + /* note that HAVE_LIBSSH2_KNOWNHOST_API is a define set in the libssh2.h + header */ +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + LIBSSH2_KNOWNHOSTS *kh; +#endif +#elif defined(USE_WOLFSSH) + WOLFSSH *ssh_session; + WOLFSSH_CTX *ctx; + word32 handleSz; + byte handle[WOLFSSH_MAX_HANDLE]; + curl_off_t offset; +#endif /* USE_LIBSSH */ +}; + +#if defined(USE_LIBSSH2) + +/* Feature detection based on version numbers to better work with + non-configure platforms */ + +#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x001000) +# error "SCP/SFTP protocols require libssh2 0.16 or later" +#endif + +#if LIBSSH2_VERSION_NUM >= 0x010000 +#define HAVE_LIBSSH2_SFTP_SEEK64 1 +#endif + +#if LIBSSH2_VERSION_NUM >= 0x010100 +#define HAVE_LIBSSH2_VERSION 1 +#endif + +#if LIBSSH2_VERSION_NUM >= 0x010205 +#define HAVE_LIBSSH2_INIT 1 +#define HAVE_LIBSSH2_EXIT 1 +#endif + +#if LIBSSH2_VERSION_NUM >= 0x010206 +#define HAVE_LIBSSH2_KNOWNHOST_CHECKP 1 +#define HAVE_LIBSSH2_SCP_SEND64 1 +#endif + +#if LIBSSH2_VERSION_NUM >= 0x010208 +#define HAVE_LIBSSH2_SESSION_HANDSHAKE 1 +#endif + +#ifdef HAVE_LIBSSH2_VERSION +/* get it runtime if possible */ +#define CURL_LIBSSH2_VERSION libssh2_version(0) +#else +/* use build-time if runtime not possible */ +#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION +#endif + +#endif /* USE_LIBSSH2 */ + +#ifdef USE_SSH + +extern const struct Curl_handler Curl_handler_scp; +extern const struct Curl_handler Curl_handler_sftp; + +/* generic SSH backend functions */ +CURLcode Curl_ssh_init(void); +void Curl_ssh_cleanup(void); +void Curl_ssh_version(char *buffer, size_t buflen); +void Curl_ssh_attach(struct Curl_easy *data, + struct connectdata *conn); +#else +/* for non-SSH builds */ +#define Curl_ssh_cleanup() +#define Curl_ssh_attach(x,y) +#define Curl_ssh_init() 0 +#endif + +#endif /* HEADER_CURL_SSH_H */ diff --git a/third_party/curl/lib/vssh/wolfssh.c b/third_party/curl/lib/vssh/wolfssh.c new file mode 100644 index 0000000000..1ed24fe4e4 --- /dev/null +++ b/third_party/curl/lib/vssh/wolfssh.c @@ -0,0 +1,1169 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_WOLFSSH + +#include + +#include +#include +#include "urldata.h" +#include "cfilters.h" +#include "connect.h" +#include "sendf.h" +#include "progress.h" +#include "curl_path.h" +#include "strtoofft.h" +#include "transfer.h" +#include "speedcheck.h" +#include "select.h" +#include "multiif.h" +#include "warnless.h" +#include "strdup.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +static CURLcode wssh_connect(struct Curl_easy *data, bool *done); +static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done); +static CURLcode wssh_do(struct Curl_easy *data, bool *done); +#if 0 +static CURLcode wscp_done(struct Curl_easy *data, + CURLcode, bool premature); +static CURLcode wscp_doing(struct Curl_easy *data, + bool *dophase_done); +static CURLcode wscp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection); +#endif +static CURLcode wsftp_done(struct Curl_easy *data, + CURLcode, bool premature); +static CURLcode wsftp_doing(struct Curl_easy *data, + bool *dophase_done); +static CURLcode wsftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead); +static int wssh_getsock(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *sock); +static CURLcode wssh_setup_connection(struct Curl_easy *data, + struct connectdata *conn); + +#if 0 +/* + * SCP protocol handler. + */ + +const struct Curl_handler Curl_handler_scp = { + "SCP", /* scheme */ + wssh_setup_connection, /* setup_connection */ + wssh_do, /* do_it */ + wscp_done, /* done */ + ZERO_NULL, /* do_more */ + wssh_connect, /* connect_it */ + wssh_multi_statemach, /* connecting */ + wscp_doing, /* doing */ + wssh_getsock, /* proto_getsock */ + wssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + wssh_getsock, /* perform_getsock */ + wscp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_SSH, /* defport */ + CURLPROTO_SCP, /* protocol */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION + | PROTOPT_NOURLQUERY /* flags */ +}; + +#endif + +/* + * SFTP protocol handler. + */ + +const struct Curl_handler Curl_handler_sftp = { + "SFTP", /* scheme */ + wssh_setup_connection, /* setup_connection */ + wssh_do, /* do_it */ + wsftp_done, /* done */ + ZERO_NULL, /* do_more */ + wssh_connect, /* connect_it */ + wssh_multi_statemach, /* connecting */ + wsftp_doing, /* doing */ + wssh_getsock, /* proto_getsock */ + wssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + wssh_getsock, /* perform_getsock */ + wsftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_SSH, /* defport */ + CURLPROTO_SFTP, /* protocol */ + CURLPROTO_SFTP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION + | PROTOPT_NOURLQUERY /* flags */ +}; + +/* + * SSH State machine related code + */ +/* This is the ONLY way to change SSH state! */ +static void state(struct Curl_easy *data, sshstate nowstate) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char * const names[] = { + "SSH_STOP", + "SSH_INIT", + "SSH_S_STARTUP", + "SSH_HOSTKEY", + "SSH_AUTHLIST", + "SSH_AUTH_PKEY_INIT", + "SSH_AUTH_PKEY", + "SSH_AUTH_PASS_INIT", + "SSH_AUTH_PASS", + "SSH_AUTH_AGENT_INIT", + "SSH_AUTH_AGENT_LIST", + "SSH_AUTH_AGENT", + "SSH_AUTH_HOST_INIT", + "SSH_AUTH_HOST", + "SSH_AUTH_KEY_INIT", + "SSH_AUTH_KEY", + "SSH_AUTH_GSSAPI", + "SSH_AUTH_DONE", + "SSH_SFTP_INIT", + "SSH_SFTP_REALPATH", + "SSH_SFTP_QUOTE_INIT", + "SSH_SFTP_POSTQUOTE_INIT", + "SSH_SFTP_QUOTE", + "SSH_SFTP_NEXT_QUOTE", + "SSH_SFTP_QUOTE_STAT", + "SSH_SFTP_QUOTE_SETSTAT", + "SSH_SFTP_QUOTE_SYMLINK", + "SSH_SFTP_QUOTE_MKDIR", + "SSH_SFTP_QUOTE_RENAME", + "SSH_SFTP_QUOTE_RMDIR", + "SSH_SFTP_QUOTE_UNLINK", + "SSH_SFTP_QUOTE_STATVFS", + "SSH_SFTP_GETINFO", + "SSH_SFTP_FILETIME", + "SSH_SFTP_TRANS_INIT", + "SSH_SFTP_UPLOAD_INIT", + "SSH_SFTP_CREATE_DIRS_INIT", + "SSH_SFTP_CREATE_DIRS", + "SSH_SFTP_CREATE_DIRS_MKDIR", + "SSH_SFTP_READDIR_INIT", + "SSH_SFTP_READDIR", + "SSH_SFTP_READDIR_LINK", + "SSH_SFTP_READDIR_BOTTOM", + "SSH_SFTP_READDIR_DONE", + "SSH_SFTP_DOWNLOAD_INIT", + "SSH_SFTP_DOWNLOAD_STAT", + "SSH_SFTP_CLOSE", + "SSH_SFTP_SHUTDOWN", + "SSH_SCP_TRANS_INIT", + "SSH_SCP_UPLOAD_INIT", + "SSH_SCP_DOWNLOAD_INIT", + "SSH_SCP_DOWNLOAD", + "SSH_SCP_DONE", + "SSH_SCP_SEND_EOF", + "SSH_SCP_WAIT_EOF", + "SSH_SCP_WAIT_CLOSE", + "SSH_SCP_CHANNEL_FREE", + "SSH_SESSION_DISCONNECT", + "SSH_SESSION_FREE", + "QUIT" + }; + + /* a precaution to make sure the lists are in sync */ + DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST); + + if(sshc->state != nowstate) { + infof(data, "wolfssh %p state change from %s to %s", + (void *)sshc, names[sshc->state], names[nowstate]); + } +#endif + + sshc->state = nowstate; +} + +static ssize_t wscp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, CURLcode *err) +{ + ssize_t nwrite = 0; + (void)data; + (void)sockindex; /* we only support SCP on the fixed known primary socket */ + (void)mem; + (void)len; + (void)err; + + return nwrite; +} + +static ssize_t wscp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, CURLcode *err) +{ + ssize_t nread = 0; + (void)data; + (void)sockindex; /* we only support SCP on the fixed known primary socket */ + (void)mem; + (void)len; + (void)err; + + return nread; +} + +/* return number of sent bytes */ +static ssize_t wsftp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, CURLcode *err) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + word32 offset[2]; + int rc; + (void)sockindex; + + offset[0] = (word32)sshc->offset&0xFFFFFFFF; + offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF; + + rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle, + sshc->handleSz, + &offset[0], + (byte *)mem, (word32)len); + + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + conn->waitfor = KEEP_RECV; + *err = CURLE_AGAIN; + return -1; + } + else if(rc == WS_WANT_WRITE) { + conn->waitfor = KEEP_SEND; + *err = CURLE_AGAIN; + return -1; + } + if(rc < 0) { + failf(data, "wolfSSH_SFTP_SendWritePacket returned %d", rc); + return -1; + } + DEBUGASSERT(rc == (int)len); + infof(data, "sent %zu bytes SFTP from offset %" CURL_FORMAT_CURL_OFF_T, + len, sshc->offset); + sshc->offset += len; + return (ssize_t)rc; +} + +/* + * Return number of received (decrypted) bytes + * or <0 on error + */ +static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, CURLcode *err) +{ + int rc; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + word32 offset[2]; + (void)sockindex; + + offset[0] = (word32)sshc->offset&0xFFFFFFFF; + offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF; + + rc = wolfSSH_SFTP_SendReadPacket(sshc->ssh_session, sshc->handle, + sshc->handleSz, + &offset[0], + (byte *)mem, (word32)len); + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + conn->waitfor = KEEP_RECV; + *err = CURLE_AGAIN; + return -1; + } + else if(rc == WS_WANT_WRITE) { + conn->waitfor = KEEP_SEND; + *err = CURLE_AGAIN; + return -1; + } + + DEBUGASSERT(rc <= (int)len); + + if(rc < 0) { + failf(data, "wolfSSH_SFTP_SendReadPacket returned %d", rc); + return -1; + } + sshc->offset += len; + + return (ssize_t)rc; +} + +/* + * SSH setup and connection + */ +static CURLcode wssh_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + struct SSHPROTO *ssh; + (void)conn; + + data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO)); + if(!ssh) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static int userauth(byte authtype, + WS_UserAuthData* authdata, + void *ctx) +{ + struct Curl_easy *data = ctx; + DEBUGF(infof(data, "wolfssh callback: type %s", + authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" : + "PUBLICCKEY")); + if(authtype == WOLFSSH_USERAUTH_PASSWORD) { + authdata->sf.password.password = (byte *)data->conn->passwd; + authdata->sf.password.passwordSz = (word32) strlen(data->conn->passwd); + } + + return 0; +} + +static CURLcode wssh_connect(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int rc; + + /* initialize per-handle data if not already */ + if(!data->req.p.ssh) + wssh_setup_connection(data, conn); + + /* We default to persistent connections. We set this already in this connect + function to make the reuse checks properly be able to check this bit. */ + connkeep(conn, "SSH default"); + + if(conn->handler->protocol & CURLPROTO_SCP) { + conn->recv[FIRSTSOCKET] = wscp_recv; + conn->send[FIRSTSOCKET] = wscp_send; + } + else { + conn->recv[FIRSTSOCKET] = wsftp_recv; + conn->send[FIRSTSOCKET] = wsftp_send; + } + sshc = &conn->proto.sshc; + sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); + if(!sshc->ctx) { + failf(data, "No wolfSSH context"); + goto error; + } + + sshc->ssh_session = wolfSSH_new(sshc->ctx); + if(!sshc->ssh_session) { + failf(data, "No wolfSSH session"); + goto error; + } + + rc = wolfSSH_SetUsername(sshc->ssh_session, conn->user); + if(rc != WS_SUCCESS) { + failf(data, "wolfSSH failed to set username"); + goto error; + } + + /* set callback for authentication */ + wolfSSH_SetUserAuth(sshc->ctx, userauth); + wolfSSH_SetUserAuthCtx(sshc->ssh_session, data); + + rc = wolfSSH_set_fd(sshc->ssh_session, (int)sock); + if(rc) { + failf(data, "wolfSSH failed to set socket"); + goto error; + } + +#if 0 + wolfSSH_Debugging_ON(); +#endif + + *done = TRUE; + if(conn->handler->protocol & CURLPROTO_SCP) + state(data, SSH_INIT); + else + state(data, SSH_SFTP_INIT); + + return wssh_multi_statemach(data, done); +error: + wolfSSH_free(sshc->ssh_session); + wolfSSH_CTX_free(sshc->ctx); + return CURLE_FAILED_INIT; +} + +/* + * wssh_statemach_act() runs the SSH state machine as far as it can without + * blocking and without reaching the end. The data the pointer 'block' points + * to will be set to TRUE if the wolfssh function returns EAGAIN meaning it + * wants to be called again when the socket is ready + */ + +static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + struct SSHPROTO *sftp_scp = data->req.p.ssh; + WS_SFTPNAME *name; + int rc = 0; + *block = FALSE; /* we are not blocking by default */ + + do { + switch(sshc->state) { + case SSH_INIT: + state(data, SSH_S_STARTUP); + break; + + case SSH_S_STARTUP: + rc = wolfSSH_connect(sshc->ssh_session); + if(rc != WS_SUCCESS) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc != WS_SUCCESS) { + state(data, SSH_STOP); + return CURLE_SSH; + } + infof(data, "wolfssh connected"); + state(data, SSH_STOP); + break; + case SSH_STOP: + break; + + case SSH_SFTP_INIT: + rc = wolfSSH_SFTP_connect(sshc->ssh_session); + if(rc != WS_SUCCESS) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + infof(data, "wolfssh SFTP connected"); + state(data, SSH_SFTP_REALPATH); + } + else { + failf(data, "wolfssh SFTP connect error %d", rc); + return CURLE_SSH; + } + break; + case SSH_SFTP_REALPATH: + name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)"."); + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(name && (rc == WS_SUCCESS)) { + sshc->homedir = Curl_memdup0(name->fName, name->fSz); + if(!sshc->homedir) + sshc->actualcode = CURLE_OUT_OF_MEMORY; + wolfSSH_SFTPNAME_list_free(name); + state(data, SSH_STOP); + return CURLE_OK; + } + failf(data, "wolfssh SFTP realpath %d", rc); + return CURLE_SSH; + + case SSH_SFTP_QUOTE_INIT: + result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path); + if(result) { + sshc->actualcode = result; + state(data, SSH_STOP); + break; + } + + if(data->set.quote) { + infof(data, "Sending quote commands"); + sshc->quote_item = data->set.quote; + state(data, SSH_SFTP_QUOTE); + } + else { + state(data, SSH_SFTP_GETINFO); + } + break; + case SSH_SFTP_GETINFO: + if(data->set.get_filetime) { + state(data, SSH_SFTP_FILETIME); + } + else { + state(data, SSH_SFTP_TRANS_INIT); + } + break; + case SSH_SFTP_TRANS_INIT: + if(data->state.upload) + state(data, SSH_SFTP_UPLOAD_INIT); + else { + if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/') + state(data, SSH_SFTP_READDIR_INIT); + else + state(data, SSH_SFTP_DOWNLOAD_INIT); + } + break; + case SSH_SFTP_UPLOAD_INIT: { + word32 flags; + WS_SFTP_FILEATRB createattrs; + if(data->state.resume_from) { + WS_SFTP_FILEATRB attrs; + if(data->state.resume_from < 0) { + rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, + &attrs); + if(rc != WS_SUCCESS) + break; + + if(rc) { + data->state.resume_from = 0; + } + else { + curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0]; + if(size < 0) { + failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + data->state.resume_from = size; + } + } + } + + if(data->set.remote_append) + /* Try to open for append, but create if nonexisting */ + flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND; + else if(data->state.resume_from > 0) + /* If we have restart position then open for append */ + flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND; + else + /* Clear file before writing (normal behavior) */ + flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC; + + memset(&createattrs, 0, sizeof(createattrs)); + createattrs.per = (word32)data->set.new_file_perms; + sshc->handleSz = sizeof(sshc->handle); + rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path, + flags, &createattrs, + sshc->handle, &sshc->handleSz); + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + infof(data, "wolfssh SFTP open succeeded"); + } + else { + failf(data, "wolfssh SFTP upload open failed: %d", rc); + return CURLE_SSH; + } + state(data, SSH_SFTP_DOWNLOAD_STAT); + + /* If we have a restart point then we need to seek to the correct + position. */ + if(data->state.resume_from > 0) { + /* Let's read off the proper amount of bytes from the input. */ + int seekerr = CURL_SEEKFUNC_OK; + if(data->set.seek_func) { + Curl_set_in_callback(data, true); + seekerr = data->set.seek_func(data->set.seek_client, + data->state.resume_from, SEEK_SET); + Curl_set_in_callback(data, false); + } + + if(seekerr != CURL_SEEKFUNC_OK) { + curl_off_t passed = 0; + + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_FTP_COULDNT_USE_REST; + } + /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ + do { + char scratch[4*1024]; + size_t readthisamountnow = + (data->state.resume_from - passed > + (curl_off_t)sizeof(scratch)) ? + sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed); + + size_t actuallyread; + Curl_set_in_callback(data, true); + actuallyread = data->state.fread_func(scratch, 1, + readthisamountnow, + data->state.in); + Curl_set_in_callback(data, false); + + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + return CURLE_FTP_COULDNT_USE_REST; + } + } while(passed < data->state.resume_from); + } + + /* now, decrease the size of the read */ + if(data->state.infilesize > 0) { + data->state.infilesize -= data->state.resume_from; + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + + sshc->offset += data->state.resume_from; + } + if(data->state.infilesize > 0) { + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + /* upload data */ + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + conn->sockfd = conn->writesockfd; + + if(result) { + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = result; + } + else { + /* store this original bitmask setup to use later on if we cannot + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh2 sftp send function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_OUT; + + /* since we do not really wait for anything at this point, we want the + state machine to move on as soon as possible so we set a very short + timeout here */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + + state(data, SSH_STOP); + } + break; + } + case SSH_SFTP_DOWNLOAD_INIT: + sshc->handleSz = sizeof(sshc->handle); + rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path, + WOLFSSH_FXF_READ, NULL, + sshc->handle, &sshc->handleSz); + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + infof(data, "wolfssh SFTP open succeeded"); + state(data, SSH_SFTP_DOWNLOAD_STAT); + return CURLE_OK; + } + + failf(data, "wolfssh SFTP open failed: %d", rc); + return CURLE_SSH; + + case SSH_SFTP_DOWNLOAD_STAT: { + WS_SFTP_FILEATRB attrs; + curl_off_t size; + + rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, &attrs); + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + infof(data, "wolfssh STAT succeeded"); + } + else { + failf(data, "wolfssh SFTP open failed: %d", rc); + data->req.size = -1; + data->req.maxdownload = -1; + Curl_pgrsSetDownloadSize(data, -1); + return CURLE_SSH; + } + + size = ((curl_off_t)attrs.sz[1] <<32) | attrs.sz[0]; + + data->req.size = size; + data->req.maxdownload = size; + Curl_pgrsSetDownloadSize(data, size); + + infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size); + + /* We cannot seek with wolfSSH so resuming and range requests are not + possible */ + if(data->state.use_range || data->state.resume_from) { + infof(data, "wolfSSH cannot do range/seek on SFTP"); + return CURLE_BAD_DOWNLOAD_RESUME; + } + + /* Setup the actual download */ + if(data->req.size == 0) { + /* no data to transfer */ + Curl_xfer_setup_nop(data); + infof(data, "File already completely downloaded"); + state(data, SSH_STOP); + break; + } + Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + conn->writesockfd = conn->sockfd; + + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh2 recv function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_IN; + + if(result) { + /* this should never occur; the close state should be entered + at the time the error occurs */ + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = result; + } + else { + state(data, SSH_STOP); + } + break; + } + case SSH_SFTP_CLOSE: + if(sshc->handleSz) + rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle, + sshc->handleSz); + else + rc = WS_SUCCESS; /* directory listing */ + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + state(data, SSH_STOP); + return CURLE_OK; + } + + failf(data, "wolfssh SFTP CLOSE failed: %d", rc); + return CURLE_SSH; + + case SSH_SFTP_READDIR_INIT: + Curl_pgrsSetDownloadSize(data, -1); + if(data->req.no_body) { + state(data, SSH_STOP); + break; + } + state(data, SSH_SFTP_READDIR); + break; + + case SSH_SFTP_READDIR: + name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path); + if(!name) + rc = wolfSSH_get_error(sshc->ssh_session); + else + rc = WS_SUCCESS; + + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(name && (rc == WS_SUCCESS)) { + WS_SFTPNAME *origname = name; + result = CURLE_OK; + while(name) { + char *line = aprintf("%s\n", + data->set.list_only ? + name->fName : name->lName); + if(!line) { + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + result = Curl_client_write(data, CLIENTWRITE_BODY, + line, strlen(line)); + free(line); + if(result) { + sshc->actualcode = result; + break; + } + name = name->next; + } + wolfSSH_SFTPNAME_list_free(origname); + state(data, SSH_STOP); + return result; + } + failf(data, "wolfssh SFTP ls failed: %d", rc); + return CURLE_SSH; + + case SSH_SFTP_SHUTDOWN: + Curl_safefree(sshc->homedir); + wolfSSH_free(sshc->ssh_session); + wolfSSH_CTX_free(sshc->ctx); + state(data, SSH_STOP); + return CURLE_OK; + default: + break; + } + } while(!rc && (sshc->state != SSH_STOP)); + return result; +} + +/* called repeatedly until done from multi.c */ +static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result = CURLE_OK; + bool block; /* we store the status and use that to provide a ssh_getsock() + implementation */ + do { + result = wssh_statemach_act(data, &block); + *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; + /* if there is no error, it is not done and it did not EWOULDBLOCK, then + try again */ + if(*done) { + DEBUGF(infof(data, "wssh_statemach_act says DONE")); + } + } while(!result && !*done && !block); + + return result; +} + +static +CURLcode wscp_perform(struct Curl_easy *data, + bool *connected, + bool *dophase_done) +{ + (void)data; + (void)connected; + (void)dophase_done; + return CURLE_OK; +} + +static +CURLcode wsftp_perform(struct Curl_easy *data, + bool *connected, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + + DEBUGF(infof(data, "DO phase starts")); + + *dophase_done = FALSE; /* not done yet */ + + /* start the first command in the DO phase */ + state(data, SSH_SFTP_QUOTE_INIT); + + /* run the state-machine */ + result = wssh_multi_statemach(data, dophase_done); + + *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +/* + * The DO function is generic for both protocols. + */ +static CURLcode wssh_do(struct Curl_easy *data, bool *done) +{ + CURLcode result; + bool connected = 0; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + + *done = FALSE; /* default to false */ + data->req.size = -1; /* make sure this is unknown at this point */ + sshc->actualcode = CURLE_OK; /* reset error code */ + sshc->secondCreateDirs = 0; /* reset the create dir attempt state + variable */ + + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); + + if(conn->handler->protocol & CURLPROTO_SCP) + result = wscp_perform(data, &connected, done); + else + result = wsftp_perform(data, &connected, done); + + return result; +} + +static CURLcode wssh_block_statemach(struct Curl_easy *data, + bool disconnect) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result = CURLE_OK; + + while((sshc->state != SSH_STOP) && !result) { + bool block; + timediff_t left = 1000; + struct curltime now = Curl_now(); + + result = wssh_statemach_act(data, &block); + if(result) + break; + + if(!disconnect) { + if(Curl_pgrsUpdate(data)) + return CURLE_ABORTED_BY_CALLBACK; + + result = Curl_speedcheck(data, now); + if(result) + break; + + left = Curl_timeleft(data, NULL, FALSE); + if(left < 0) { + failf(data, "Operation timed out"); + return CURLE_OPERATION_TIMEDOUT; + } + } + + if(!result) { + int dir = conn->waitfor; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + curl_socket_t fd_read = CURL_SOCKET_BAD; + curl_socket_t fd_write = CURL_SOCKET_BAD; + if(dir == KEEP_RECV) + fd_read = sock; + else if(dir == KEEP_SEND) + fd_write = sock; + + /* wait for the socket to become ready */ + (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, + left>1000?1000:left); /* ignore result */ + } + } + + return result; +} + +/* generic done function for both SCP and SFTP called from their specific + done functions */ +static CURLcode wssh_done(struct Curl_easy *data, CURLcode status) +{ + CURLcode result = CURLE_OK; + struct SSHPROTO *sftp_scp = data->req.p.ssh; + + if(!status) { + /* run the state-machine */ + result = wssh_block_statemach(data, FALSE); + } + else + result = status; + + if(sftp_scp) + Curl_safefree(sftp_scp->path); + if(Curl_pgrsDone(data)) + return CURLE_ABORTED_BY_CALLBACK; + + data->req.keepon = 0; /* clear all bits */ + return result; +} + +#if 0 +static CURLcode wscp_done(struct Curl_easy *data, + CURLcode code, bool premature) +{ + CURLcode result = CURLE_OK; + (void)conn; + (void)code; + (void)premature; + + return result; +} + +static CURLcode wscp_doing(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + (void)conn; + (void)dophase_done; + + return result; +} + +static CURLcode wscp_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) +{ + CURLcode result = CURLE_OK; + (void)data; + (void)conn; + (void)dead_connection; + + return result; +} +#endif + +static CURLcode wsftp_done(struct Curl_easy *data, + CURLcode code, bool premature) +{ + (void)premature; + state(data, SSH_SFTP_CLOSE); + + return wssh_done(data, code); +} + +static CURLcode wsftp_doing(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = wssh_multi_statemach(data, dophase_done); + + if(*dophase_done) { + DEBUGF(infof(data, "DO phase is complete")); + } + return result; +} + +static CURLcode wsftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead) +{ + CURLcode result = CURLE_OK; + (void)dead; + + DEBUGF(infof(data, "SSH DISCONNECT starts now")); + + if(conn->proto.sshc.ssh_session) { + /* only if there is a session still around to use! */ + state(data, SSH_SFTP_SHUTDOWN); + result = wssh_block_statemach(data, TRUE); + } + + DEBUGF(infof(data, "SSH DISCONNECT is done")); + return result; +} + +static int wssh_getsock(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *sock) +{ + int bitmap = GETSOCK_BLANK; + int dir = conn->waitfor; + (void)data; + sock[0] = conn->sock[FIRSTSOCKET]; + + if(dir == KEEP_RECV) + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + else if(dir == KEEP_SEND) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + return bitmap; +} + +void Curl_ssh_version(char *buffer, size_t buflen) +{ + (void)msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING); +} + +CURLcode Curl_ssh_init(void) +{ + if(WS_SUCCESS != wolfSSH_Init()) { + DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n")); + return CURLE_FAILED_INIT; + } + + return CURLE_OK; +} +void Curl_ssh_cleanup(void) +{ + (void)wolfSSH_Cleanup(); +} + +#endif /* USE_WOLFSSH */ diff --git a/third_party/curl/lib/vtls/bearssl.c b/third_party/curl/lib/vtls/bearssl.c new file mode 100644 index 0000000000..47c5528c29 --- /dev/null +++ b/third_party/curl/lib/vtls/bearssl.c @@ -0,0 +1,1162 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Michael Forney, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_BEARSSL + +#include + +#include "bearssl.h" +#include "cipher_suite.h" +#include "urldata.h" +#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" + +/* 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; + br_x509_decoder_context decoder; + bool verifyhost; + bool verifypeer; + int cert_num; +}; + +struct bearssl_ssl_backend_data { + br_ssl_client_context ctx; + struct x509_context x509; + unsigned char buf[BR_SSL_BUFSIZE_BIDI]; + br_x509_trust_anchor *anchors; + size_t anchors_len; + const char *protocols[ALPN_ENTRIES_MAX]; + /* SSL client context is active */ + bool active; + /* size of pending write, yet to be flushed */ + size_t pending_write; + BIT(sent_shutdown); +}; + +struct cafile_parser { + CURLcode err; + bool in_cert; + br_x509_decoder_context xc; + /* array of trust anchors loaded from CAfile */ + br_x509_trust_anchor *anchors; + size_t anchors_len; + /* buffer for DN data */ + unsigned char dn[1024]; + size_t dn_len; +}; + +#define CAFILE_SOURCE_PATH 1 +#define CAFILE_SOURCE_BLOB 2 +struct cafile_source { + int type; + const char *data; + size_t len; +}; + +static void append_dn(void *ctx, const void *buf, size_t len) +{ + struct cafile_parser *ca = ctx; + + if(ca->err != CURLE_OK || !ca->in_cert) + return; + if(sizeof(ca->dn) - ca->dn_len < len) { + ca->err = CURLE_FAILED_INIT; + return; + } + memcpy(ca->dn + ca->dn_len, buf, len); + ca->dn_len += len; +} + +static void x509_push(void *ctx, const void *buf, size_t len) +{ + struct cafile_parser *ca = ctx; + + if(ca->in_cert) + br_x509_decoder_push(&ca->xc, buf, len); +} + +static CURLcode load_cafile(struct cafile_source *source, + br_x509_trust_anchor **anchors, + size_t *anchors_len) +{ + struct cafile_parser ca; + br_pem_decoder_context pc; + br_x509_trust_anchor *ta; + size_t ta_size; + br_x509_trust_anchor *new_anchors; + size_t new_anchors_len; + br_x509_pkey *pkey; + FILE *fp = 0; + unsigned char buf[BUFSIZ]; + const unsigned char *p = NULL; + const char *name; + size_t n = 0, i, pushed; + + DEBUGASSERT(source->type == CAFILE_SOURCE_PATH + || source->type == CAFILE_SOURCE_BLOB); + + if(source->type == CAFILE_SOURCE_PATH) { + fp = fopen(source->data, "rb"); + if(!fp) + return CURLE_SSL_CACERT_BADFILE; + } + + if(source->type == CAFILE_SOURCE_BLOB && source->len > (size_t)INT_MAX) + return CURLE_SSL_CACERT_BADFILE; + + ca.err = CURLE_OK; + ca.in_cert = FALSE; + ca.anchors = NULL; + ca.anchors_len = 0; + br_pem_decoder_init(&pc); + br_pem_decoder_setdest(&pc, x509_push, &ca); + do { + if(source->type == CAFILE_SOURCE_PATH) { + n = fread(buf, 1, sizeof(buf), fp); + if(n == 0) + break; + p = buf; + } + else if(source->type == CAFILE_SOURCE_BLOB) { + n = source->len; + p = (unsigned char *) source->data; + } + while(n) { + pushed = br_pem_decoder_push(&pc, p, n); + if(ca.err) + goto fail; + p += pushed; + n -= pushed; + + switch(br_pem_decoder_event(&pc)) { + case 0: + break; + case BR_PEM_BEGIN_OBJ: + name = br_pem_decoder_name(&pc); + if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE")) + break; + br_x509_decoder_init(&ca.xc, append_dn, &ca); + ca.in_cert = TRUE; + ca.dn_len = 0; + break; + case BR_PEM_END_OBJ: + if(!ca.in_cert) + break; + ca.in_cert = FALSE; + if(br_x509_decoder_last_error(&ca.xc)) { + ca.err = CURLE_SSL_CACERT_BADFILE; + goto fail; + } + /* add trust anchor */ + if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) { + ca.err = CURLE_OUT_OF_MEMORY; + goto fail; + } + new_anchors_len = ca.anchors_len + 1; + new_anchors = realloc(ca.anchors, + new_anchors_len * sizeof(ca.anchors[0])); + if(!new_anchors) { + ca.err = CURLE_OUT_OF_MEMORY; + goto fail; + } + ca.anchors = new_anchors; + ca.anchors_len = new_anchors_len; + ta = &ca.anchors[ca.anchors_len - 1]; + ta->dn.data = NULL; + ta->flags = 0; + if(br_x509_decoder_isCA(&ca.xc)) + ta->flags |= BR_X509_TA_CA; + pkey = br_x509_decoder_get_pkey(&ca.xc); + if(!pkey) { + ca.err = CURLE_SSL_CACERT_BADFILE; + goto fail; + } + ta->pkey = *pkey; + + /* calculate space needed for trust anchor data */ + ta_size = ca.dn_len; + switch(pkey->key_type) { + case BR_KEYTYPE_RSA: + ta_size += pkey->key.rsa.nlen + pkey->key.rsa.elen; + break; + case BR_KEYTYPE_EC: + ta_size += pkey->key.ec.qlen; + break; + default: + ca.err = CURLE_FAILED_INIT; + goto fail; + } + + /* fill in trust anchor DN and public key data */ + ta->dn.data = malloc(ta_size); + if(!ta->dn.data) { + ca.err = CURLE_OUT_OF_MEMORY; + goto fail; + } + memcpy(ta->dn.data, ca.dn, ca.dn_len); + ta->dn.len = ca.dn_len; + switch(pkey->key_type) { + case BR_KEYTYPE_RSA: + ta->pkey.key.rsa.n = ta->dn.data + ta->dn.len; + memcpy(ta->pkey.key.rsa.n, pkey->key.rsa.n, pkey->key.rsa.nlen); + ta->pkey.key.rsa.e = ta->pkey.key.rsa.n + ta->pkey.key.rsa.nlen; + memcpy(ta->pkey.key.rsa.e, pkey->key.rsa.e, pkey->key.rsa.elen); + break; + case BR_KEYTYPE_EC: + ta->pkey.key.ec.q = ta->dn.data + ta->dn.len; + memcpy(ta->pkey.key.ec.q, pkey->key.ec.q, pkey->key.ec.qlen); + break; + } + break; + default: + ca.err = CURLE_SSL_CACERT_BADFILE; + goto fail; + } + } + } while(source->type != CAFILE_SOURCE_BLOB); + if(fp && ferror(fp)) + ca.err = CURLE_READ_ERROR; + else if(ca.in_cert) + ca.err = CURLE_SSL_CACERT_BADFILE; + +fail: + if(fp) + fclose(fp); + if(ca.err == CURLE_OK) { + *anchors = ca.anchors; + *anchors_len = ca.anchors_len; + } + else { + for(i = 0; i < ca.anchors_len; ++i) + free(ca.anchors[i].dn.data); + free(ca.anchors); + } + + return ca.err; +} + +static void x509_start_chain(const br_x509_class **ctx, + const char *server_name) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + if(!x509->verifypeer) { + x509->cert_num = 0; + return; + } + + if(!x509->verifyhost) + server_name = NULL; + x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name); +} + +static void x509_start_cert(const br_x509_class **ctx, uint32_t length) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + if(!x509->verifypeer) { + /* Only decode the first cert in the chain to obtain the public key */ + if(x509->cert_num == 0) + br_x509_decoder_init(&x509->decoder, NULL, NULL); + return; + } + + x509->minimal.vtable->start_cert(&x509->minimal.vtable, length); +} + +static void x509_append(const br_x509_class **ctx, const unsigned char *buf, + size_t len) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + if(!x509->verifypeer) { + if(x509->cert_num == 0) + br_x509_decoder_push(&x509->decoder, buf, len); + return; + } + + x509->minimal.vtable->append(&x509->minimal.vtable, buf, len); +} + +static void x509_end_cert(const br_x509_class **ctx) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + if(!x509->verifypeer) { + x509->cert_num++; + return; + } + + x509->minimal.vtable->end_cert(&x509->minimal.vtable); +} + +static unsigned x509_end_chain(const br_x509_class **ctx) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + if(!x509->verifypeer) { + return (unsigned)br_x509_decoder_last_error(&x509->decoder); + } + + return x509->minimal.vtable->end_chain(&x509->minimal.vtable); +} + +static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx, + unsigned *usages) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + if(!x509->verifypeer) { + /* Nothing in the chain is verified, just return the public key of the + first certificate and allow its usage for both TLS_RSA_* and + TLS_ECDHE_* */ + if(usages) + *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; + return br_x509_decoder_get_pkey(&x509->decoder); + } + + return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages); +} + +static const br_x509_class x509_vtable = { + sizeof(struct x509_context), + x509_start_chain, + x509_start_cert, + x509_append, + x509_end_cert, + x509_end_chain, + x509_get_pkey +}; + +static const uint16_t ciphertable[] = { + /* RFC 2246 TLS 1.0 */ + BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ + + /* RFC 3268 TLS 1.0 AES */ + BR_TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ + BR_TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ + + /* RFC 5246 TLS 1.2 */ + BR_TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ + BR_TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ + + /* RFC 5288 TLS 1.2 AES GCM */ + BR_TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ + BR_TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ + + /* RFC 4492 TLS 1.0 ECC */ + BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */ + BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */ + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ + BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */ + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */ + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */ + BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */ + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ + + /* RFC 5289 TLS 1.2 ECC HMAC SHA256/384 */ + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */ + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */ + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */ + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */ + + /* RFC 5289 TLS 1.2 GCM */ + BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ + BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ + BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */ + BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */ + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ + BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ + BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */ + BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */ + +#ifdef BR_TLS_RSA_WITH_AES_128_CCM + /* RFC 6655 TLS 1.2 CCM + Supported since BearSSL 0.6 */ + BR_TLS_RSA_WITH_AES_128_CCM, /* 0xC09C */ + BR_TLS_RSA_WITH_AES_256_CCM, /* 0xC09D */ + BR_TLS_RSA_WITH_AES_128_CCM_8, /* 0xC0A0 */ + BR_TLS_RSA_WITH_AES_256_CCM_8, /* 0xC0A1 */ + + /* RFC 7251 TLS 1.2 ECC CCM + Supported since BearSSL 0.6 */ + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, /* 0xC0AC */ + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, /* 0xC0AD */ + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, /* 0xC0AE */ + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, /* 0xC0AF */ +#endif + + /* RFC 7905 TLS 1.2 ChaCha20-Poly1305 + Supported since BearSSL 0.2 */ + BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ + BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ +}; + +#define NUM_OF_CIPHERS (sizeof(ciphertable) / sizeof(ciphertable[0])) + +static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data, + br_ssl_engine_context *ssl_eng, + const char *ciphers) +{ + uint16_t selected[NUM_OF_CIPHERS]; + size_t count = 0, i; + const char *ptr, *end; + + for(ptr = ciphers; ptr[0] != '\0' && count < NUM_OF_CIPHERS; ptr = end) { + uint16_t id = Curl_cipher_suite_walk_str(&ptr, &end); + + /* Check if cipher is supported */ + if(id) { + for(i = 0; i < NUM_OF_CIPHERS && ciphertable[i] != id; i++); + if(i == NUM_OF_CIPHERS) + id = 0; + } + if(!id) { + if(ptr[0] != '\0') + infof(data, "BearSSL: unknown cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); + continue; + } + + /* No duplicates allowed */ + for(i = 0; i < count && selected[i] != id; i++); + if(i < count) { + infof(data, "BearSSL: duplicate cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); + continue; + } + + selected[count++] = id; + } + + if(count == 0) { + failf(data, "BearSSL: no supported cipher in list"); + return CURLE_SSL_CIPHER; + } + + br_ssl_engine_set_suites(ssl_eng, selected, count); + return CURLE_OK; +} + +static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : conn_config->CAfile); + const char *hostname = connssl->peer.hostname; + const bool verifypeer = conn_config->verifypeer; + const bool verifyhost = conn_config->verifyhost; + CURLcode ret; + unsigned version_min, version_max; + int session_set = 0; + + DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step1"); + + switch(conn_config->version) { + case CURL_SSLVERSION_SSLv2: + failf(data, "BearSSL does not support SSLv2"); + return CURLE_SSL_CONNECT_ERROR; + case CURL_SSLVERSION_SSLv3: + failf(data, "BearSSL does not support SSLv3"); + return CURLE_SSL_CONNECT_ERROR; + case CURL_SSLVERSION_TLSv1_0: + version_min = BR_TLS10; + version_max = BR_TLS10; + break; + case CURL_SSLVERSION_TLSv1_1: + version_min = BR_TLS11; + version_max = BR_TLS11; + break; + case CURL_SSLVERSION_TLSv1_2: + version_min = BR_TLS12; + version_max = BR_TLS12; + break; + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + version_min = BR_TLS10; + version_max = BR_TLS12; + break; + default: + failf(data, "BearSSL: unknown CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + + if(verifypeer) { + if(ca_info_blob) { + struct cafile_source source; + source.type = CAFILE_SOURCE_BLOB; + source.data = ca_info_blob->data; + source.len = ca_info_blob->len; + + CURL_TRC_CF(data, cf, "connect_step1, load ca_info_blob"); + ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); + if(ret != CURLE_OK) { + failf(data, "error importing CA certificate blob"); + return ret; + } + } + + if(ssl_cafile) { + struct cafile_source source; + source.type = CAFILE_SOURCE_PATH; + source.data = ssl_cafile; + source.len = 0; + + CURL_TRC_CF(data, cf, "connect_step1, load cafile"); + ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); + if(ret != CURLE_OK) { + failf(data, "error setting certificate verify locations." + " CAfile: %s", ssl_cafile); + return ret; + } + } + } + + /* initialize SSL context */ + br_ssl_client_init_full(&backend->ctx, &backend->x509.minimal, + backend->anchors, backend->anchors_len); + br_ssl_engine_set_versions(&backend->ctx.eng, version_min, version_max); + br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf, + sizeof(backend->buf), 1); + + if(conn_config->cipher_list) { + /* Override the ciphers as specified. For the default cipher list see the + BearSSL source code of br_ssl_client_init_full() */ + CURL_TRC_CF(data, cf, "connect_step1, set ciphers"); + ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng, + conn_config->cipher_list); + if(ret) + return ret; + } + + /* initialize X.509 context */ + backend->x509.vtable = &x509_vtable; + backend->x509.verifypeer = verifypeer; + backend->x509.verifyhost = verifyhost; + br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable); + + if(ssl_config->primary.cache_session) { + void *session; + + CURL_TRC_CF(data, cf, "connect_step1, check session cache"); + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &session, NULL)) { + br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); + session_set = 1; + infof(data, "BearSSL: reusing session ID"); + } + Curl_ssl_sessionid_unlock(data); + } + + if(connssl->alpn) { + struct alpn_proto_buf proto; + size_t i; + + for(i = 0; i < connssl->alpn->count; ++i) { + backend->protocols[i] = connssl->alpn->entries[i]; + } + br_ssl_engine_set_protocol_names(&backend->ctx.eng, backend->protocols, + connssl->alpn->count); + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } + + if(connssl->peer.type != CURL_SSL_PEER_DNS) { + if(verifyhost) { + failf(data, "BearSSL: " + "host verification of IP address is not supported"); + return CURLE_PEER_FAILED_VERIFICATION; + } + hostname = NULL; + } + else { + if(!connssl->peer.sni) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + hostname = connssl->peer.sni; + CURL_TRC_CF(data, cf, "connect_step1, SNI set"); + } + + /* give application a chance to interfere with SSL set up. */ + if(data->set.ssl.fsslctx) { + Curl_set_in_callback(data, true); + ret = (*data->set.ssl.fsslctx)(data, &backend->ctx, + data->set.ssl.fsslctxp); + Curl_set_in_callback(data, false); + if(ret) { + failf(data, "BearSSL: error signaled by ssl ctx callback"); + return ret; + } + } + + if(!br_ssl_client_reset(&backend->ctx, hostname, session_set)) + return CURLE_FAILED_INIT; + backend->active = TRUE; + + connssl->connecting_state = ssl_connect_2; + + return CURLE_OK; +} + +static void bearssl_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + if(!cf->connected) { + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + if(sock != CURL_SOCKET_BAD) { + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + unsigned state = br_ssl_engine_current_state(&backend->ctx.eng); + + if(state & BR_SSL_SENDREC) { + Curl_pollset_set_out_only(data, ps, sock); + } + else { + Curl_pollset_set_in_only(data, ps, sock); + } + } + } +} + +static CURLcode bearssl_run_until(struct Curl_cfilter *cf, + struct Curl_easy *data, + unsigned target) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + unsigned state; + unsigned char *buf; + size_t len; + ssize_t ret; + CURLcode result; + int err; + + DEBUGASSERT(backend); + + for(;;) { + state = br_ssl_engine_current_state(&backend->ctx.eng); + if(state & BR_SSL_CLOSED) { + err = br_ssl_engine_last_error(&backend->ctx.eng); + switch(err) { + case BR_ERR_OK: + /* TLS close notify */ + if(connssl->state != ssl_connection_complete) { + failf(data, "SSL: connection closed during handshake"); + return CURLE_SSL_CONNECT_ERROR; + } + return CURLE_OK; + case BR_ERR_X509_EXPIRED: + failf(data, "SSL: X.509 verification: " + "certificate is expired or not yet valid"); + return CURLE_PEER_FAILED_VERIFICATION; + case BR_ERR_X509_BAD_SERVER_NAME: + failf(data, "SSL: X.509 verification: " + "expected server name was not found in the chain"); + return CURLE_PEER_FAILED_VERIFICATION; + case BR_ERR_X509_NOT_TRUSTED: + failf(data, "SSL: X.509 verification: " + "chain could not be linked to a trust anchor"); + return CURLE_PEER_FAILED_VERIFICATION; + } + /* X.509 errors are documented to have the range 32..63 */ + if(err >= 32 && err < 64) + return CURLE_PEER_FAILED_VERIFICATION; + return CURLE_SSL_CONNECT_ERROR; + } + if(state & target) + return CURLE_OK; + if(state & BR_SSL_SENDREC) { + buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len); + ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, &result); + CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result); + if(ret <= 0) { + if(result == CURLE_AGAIN) + connssl->io_need |= CURL_SSL_IO_NEED_SEND; + return result; + } + br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret); + } + else if(state & BR_SSL_RECVREC) { + buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len); + ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result); + CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %zd, %d", len, ret, result); + if(ret == 0) { + failf(data, "SSL: EOF without close notify"); + return CURLE_RECV_ERROR; + } + if(ret <= 0) { + if(result == CURLE_AGAIN) + connssl->io_need |= CURL_SSL_IO_NEED_RECV; + return result; + } + br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret); + } + } +} + +static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + br_ssl_session_parameters session; + char cipher_str[64]; + char ver_str[16]; + CURLcode ret; + + DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step2"); + + ret = bearssl_run_until(cf, data, BR_SSL_SENDAPP | BR_SSL_RECVAPP); + if(ret == CURLE_AGAIN) + return CURLE_OK; + if(ret == CURLE_OK) { + unsigned int tver; + + if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { + failf(data, "SSL: connection closed during handshake"); + return CURLE_SSL_CONNECT_ERROR; + } + connssl->connecting_state = ssl_connect_3; + /* Informational message */ + tver = br_ssl_engine_get_version(&backend->ctx.eng); + if(tver == BR_TLS12) + strcpy(ver_str, "TLSv1.2"); + else if(tver == BR_TLS11) + strcpy(ver_str, "TLSv1.1"); + else if(tver == BR_TLS10) + strcpy(ver_str, "TLSv1.0"); + else { + msnprintf(ver_str, sizeof(ver_str), "TLS 0x%04x", tver); + } + br_ssl_engine_get_session_parameters(&backend->ctx.eng, &session); + Curl_cipher_suite_get_str(session.cipher_suite, cipher_str, + sizeof(cipher_str), true); + infof(data, "BearSSL: %s connection using %s", ver_str, cipher_str); + } + return ret; +} + +static void bearssl_session_free(void *sessionid, size_t idsize) +{ + (void)idsize; + free(sessionid); +} + +static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode ret; + + DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step3"); + + if(connssl->alpn) { + const char *proto; + + proto = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); + Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto, + proto? strlen(proto) : 0); + } + + if(ssl_config->primary.cache_session) { + br_ssl_session_parameters *session; + + session = malloc(sizeof(*session)); + if(!session) + return CURLE_OUT_OF_MEMORY; + br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); + Curl_ssl_sessionid_lock(data); + ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, session, 0, + bearssl_session_free); + Curl_ssl_sessionid_unlock(data); + if(ret) + return ret; + } + + connssl->connecting_state = ssl_connect_done; + + return CURLE_OK; +} + +static ssize_t bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + unsigned char *app; + size_t applen; + + DEBUGASSERT(backend); + + for(;;) { + *err = bearssl_run_until(cf, data, BR_SSL_SENDAPP); + if(*err) + return -1; + app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen); + if(!app) { + failf(data, "SSL: connection closed during write"); + *err = CURLE_SEND_ERROR; + return -1; + } + if(backend->pending_write) { + applen = backend->pending_write; + backend->pending_write = 0; + return applen; + } + if(applen > len) + applen = len; + memcpy(app, buf, applen); + br_ssl_engine_sendapp_ack(&backend->ctx.eng, applen); + br_ssl_engine_flush(&backend->ctx.eng, 0); + backend->pending_write = applen; + } +} + +static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + unsigned char *app; + size_t applen; + + DEBUGASSERT(backend); + + *err = bearssl_run_until(cf, data, BR_SSL_RECVAPP); + if(*err != CURLE_OK) + return -1; + app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen); + if(!app) + return 0; + if(applen > len) + applen = len; + memcpy(buf, app, applen); + br_ssl_engine_recvapp_ack(&backend->ctx.eng, applen); + + return applen; +} + +static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool nonblocking, + bool *done) +{ + CURLcode ret; + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + timediff_t timeout_ms; + int what; + + CURL_TRC_CF(data, cf, "connect_common(blocking=%d)", !nonblocking); + /* check if the connection has already been established */ + if(ssl_connection_complete == connssl->state) { + CURL_TRC_CF(data, cf, "connect_common, connected"); + *done = TRUE; + return CURLE_OK; + } + + if(ssl_connect_1 == connssl->connecting_state) { + ret = bearssl_connect_step1(cf, data); + if(ret) + return ret; + } + + while(ssl_connect_2 == connssl->connecting_state) { + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { + + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; + + CURL_TRC_CF(data, cf, "connect_common, check socket"); + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + nonblocking?0:timeout_ms); + CURL_TRC_CF(data, cf, "connect_common, check socket -> %d", what); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + return CURLE_SSL_CONNECT_ERROR; + } + else if(0 == what) { + if(nonblocking) { + *done = FALSE; + return CURLE_OK; + } + else { + /* timeout */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + } + /* socket is readable or writable */ + } + + /* Run transaction, and return to the caller if it failed or if this + * connection is done nonblocking and this loop would execute again. This + * permits the owner of a multi handle to abort a connection attempt + * before step2 has completed while ensuring that a client using select() + * or epoll() will always have a valid fdset to wait on. + */ + connssl->io_need = CURL_SSL_IO_NEED_NONE; + ret = bearssl_connect_step2(cf, data); + if(ret || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) + return ret; + } + + if(ssl_connect_3 == connssl->connecting_state) { + ret = bearssl_connect_step3(cf, data); + if(ret) + return ret; + } + + if(ssl_connect_done == connssl->connecting_state) { + connssl->state = ssl_connection_complete; + *done = TRUE; + } + else + *done = FALSE; + + /* Reset our connect state machine */ + connssl->connecting_state = ssl_connect_1; + + return CURLE_OK; +} + +static size_t bearssl_version(char *buffer, size_t size) +{ + return msnprintf(buffer, size, "BearSSL"); +} + +static bool bearssl_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct ssl_connect_data *ctx = cf->ctx; + struct bearssl_ssl_backend_data *backend; + + (void)data; + DEBUGASSERT(ctx && ctx->backend); + backend = (struct bearssl_ssl_backend_data *)ctx->backend; + return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP; +} + +static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM, + unsigned char *entropy, size_t length) +{ + static br_hmac_drbg_context ctx; + static bool seeded = FALSE; + + if(!seeded) { + br_prng_seeder seeder; + + br_hmac_drbg_init(&ctx, &br_sha256_vtable, NULL, 0); + seeder = br_prng_seeder_system(NULL); + if(!seeder || !seeder(&ctx.vtable)) + return CURLE_FAILED_INIT; + seeded = TRUE; + } + br_hmac_drbg_generate(&ctx, entropy, length); + + return CURLE_OK; +} + +static CURLcode bearssl_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode ret; + bool done = FALSE; + + ret = bearssl_connect_common(cf, data, FALSE, &done); + if(ret) + return ret; + + DEBUGASSERT(done); + + return CURLE_OK; +} + +static CURLcode bearssl_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + return bearssl_connect_common(cf, data, TRUE, done); +} + +static void *bearssl_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) +{ + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + DEBUGASSERT(backend); + return &backend->ctx; +} + +static CURLcode bearssl_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + CURLcode result; + + DEBUGASSERT(backend); + if(!backend->active || cf->shutdown) { + *done = TRUE; + return CURLE_OK; + } + + *done = FALSE; + if(!backend->sent_shutdown) { + (void)send_shutdown; /* unknown how to suppress our close notify */ + br_ssl_engine_close(&backend->ctx.eng); + backend->sent_shutdown = TRUE; + } + + result = bearssl_run_until(cf, data, BR_SSL_CLOSED); + if(result == CURLE_OK) { + *done = TRUE; + } + else if(result == CURLE_AGAIN) + result = CURLE_OK; + else + CURL_TRC_CF(data, cf, "shutdown error: %d", result); + + cf->shutdown = (result || *done); + return result; +} + +static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + size_t i; + + (void)data; + DEBUGASSERT(backend); + + backend->active = FALSE; + if(backend->anchors) { + for(i = 0; i < backend->anchors_len; ++i) + free(backend->anchors[i].dn.data); + Curl_safefree(backend->anchors); + } +} + +static CURLcode bearssl_sha256sum(const unsigned char *input, + size_t inputlen, + unsigned char *sha256sum, + size_t sha256len UNUSED_PARAM) +{ + br_sha256_context ctx; + + br_sha256_init(&ctx); + br_sha256_update(&ctx, input, inputlen); + br_sha256_out(&ctx, sha256sum); + return CURLE_OK; +} + +const struct Curl_ssl Curl_ssl_bearssl = { + { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */ + SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY, + sizeof(struct bearssl_ssl_backend_data), + + Curl_none_init, /* init */ + Curl_none_cleanup, /* cleanup */ + bearssl_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + bearssl_shutdown, /* shutdown */ + bearssl_data_pending, /* data_pending */ + bearssl_random, /* random */ + Curl_none_cert_status_request, /* cert_status_request */ + bearssl_connect, /* connect */ + bearssl_connect_nonblocking, /* connect_nonblocking */ + bearssl_adjust_pollset, /* adjust_pollset */ + bearssl_get_internals, /* get_internals */ + bearssl_close, /* close_one */ + Curl_none_close_all, /* close_all */ + Curl_none_set_engine, /* set_engine */ + Curl_none_set_engine_default, /* set_engine_default */ + Curl_none_engines_list, /* engines_list */ + Curl_none_false_start, /* false_start */ + bearssl_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ + NULL, /* disassociate_connection */ + bearssl_recv, /* recv decrypted data */ + bearssl_send, /* send data to encrypt */ +}; + +#endif /* USE_BEARSSL */ diff --git a/third_party/curl/lib/vtls/bearssl.h b/third_party/curl/lib/vtls/bearssl.h new file mode 100644 index 0000000000..b3651b092c --- /dev/null +++ b/third_party/curl/lib/vtls/bearssl.h @@ -0,0 +1,34 @@ +#ifndef HEADER_CURL_BEARSSL_H +#define HEADER_CURL_BEARSSL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Michael Forney, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_BEARSSL + +extern const struct Curl_ssl Curl_ssl_bearssl; + +#endif /* USE_BEARSSL */ +#endif /* HEADER_CURL_BEARSSL_H */ diff --git a/third_party/curl/lib/vtls/cipher_suite.c b/third_party/curl/lib/vtls/cipher_suite.c new file mode 100644 index 0000000000..83ac5ddfae --- /dev/null +++ b/third_party/curl/lib/vtls/cipher_suite.c @@ -0,0 +1,884 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Jan Venekamp, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || defined(USE_BEARSSL) +#include "cipher_suite.h" +#include "curl_printf.h" +#include "strcase.h" +#include + +/* + * To support the CURLOPT_SSL_CIPHER_LIST option on SSL backends + * that do not support it natively, but do support setting a list of + * IANA ids, we need a list of all supported cipher suite names + * (OpenSSL and IANA) to be able to look up the IANA ids. + * + * To keep the binary size of this list down we compress each entry + * down to 2 + 6 bytes using the C preprocessor. + */ + +/* + * mbedTLS NOTE: mbedTLS has mbedtls_ssl_get_ciphersuite_id() to + * convert a string representation to an IANA id, we do not use that + * because it does not support "standard" OpenSSL cipher suite + * names, nor IANA names. + */ + +/* NOTE: also see tests/unit/unit3205.c */ + +/* Text for cipher suite parts (max 64 entries), + keep indexes below in sync with this! */ +static const char *cs_txt = + "\0" + "TLS" "\0" + "WITH" "\0" + "128" "\0" + "256" "\0" + "3DES" "\0" + "8" "\0" + "AES" "\0" + "AES128" "\0" + "AES256" "\0" + "CBC" "\0" + "CBC3" "\0" + "CCM" "\0" + "CCM8" "\0" + "CHACHA20" "\0" + "DES" "\0" + "DHE" "\0" + "ECDH" "\0" + "ECDHE" "\0" + "ECDSA" "\0" + "EDE" "\0" + "GCM" "\0" + "MD5" "\0" + "NULL" "\0" + "POLY1305" "\0" + "PSK" "\0" + "RSA" "\0" + "SHA" "\0" + "SHA256" "\0" + "SHA384" "\0" +#if defined(USE_MBEDTLS) + "ARIA" "\0" + "ARIA128" "\0" + "ARIA256" "\0" + "CAMELLIA" "\0" + "CAMELLIA128" "\0" + "CAMELLIA256" "\0" +#endif +#if defined(USE_SECTRANSP) + "40" "\0" + "ADH" "\0" + "AECDH" "\0" + "anon" "\0" + "DES40" "\0" + "DH" "\0" + "DSS" "\0" + "EDH" "\0" + "EXP" "\0" + "EXPORT" "\0" + "IDEA" "\0" + "RC2" "\0" + "RC4" "\0" +#endif +; +/* Indexes of above cs_txt */ +enum { + CS_TXT_IDX_, + CS_TXT_IDX_TLS, + CS_TXT_IDX_WITH, + CS_TXT_IDX_128, + CS_TXT_IDX_256, + CS_TXT_IDX_3DES, + CS_TXT_IDX_8, + CS_TXT_IDX_AES, + CS_TXT_IDX_AES128, + CS_TXT_IDX_AES256, + CS_TXT_IDX_CBC, + CS_TXT_IDX_CBC3, + CS_TXT_IDX_CCM, + CS_TXT_IDX_CCM8, + CS_TXT_IDX_CHACHA20, + CS_TXT_IDX_DES, + CS_TXT_IDX_DHE, + CS_TXT_IDX_ECDH, + CS_TXT_IDX_ECDHE, + CS_TXT_IDX_ECDSA, + CS_TXT_IDX_EDE, + CS_TXT_IDX_GCM, + CS_TXT_IDX_MD5, + CS_TXT_IDX_NULL, + CS_TXT_IDX_POLY1305, + CS_TXT_IDX_PSK, + CS_TXT_IDX_RSA, + CS_TXT_IDX_SHA, + CS_TXT_IDX_SHA256, + CS_TXT_IDX_SHA384, +#if defined(USE_MBEDTLS) + CS_TXT_IDX_ARIA, + CS_TXT_IDX_ARIA128, + CS_TXT_IDX_ARIA256, + CS_TXT_IDX_CAMELLIA, + CS_TXT_IDX_CAMELLIA128, + CS_TXT_IDX_CAMELLIA256, +#endif +#if defined(USE_SECTRANSP) + CS_TXT_IDX_40, + CS_TXT_IDX_ADH, + CS_TXT_IDX_AECDH, + CS_TXT_IDX_anon, + CS_TXT_IDX_DES40, + CS_TXT_IDX_DH, + CS_TXT_IDX_DSS, + CS_TXT_IDX_EDH, + CS_TXT_IDX_EXP, + CS_TXT_IDX_EXPORT, + CS_TXT_IDX_IDEA, + CS_TXT_IDX_RC2, + CS_TXT_IDX_RC4, +#endif + CS_TXT_LEN, +}; + +#define CS_ZIP_IDX(a, b, c, d, e, f, g, h) \ +{ \ + (uint8_t) ((a) << 2 | ((b) & 0x3F) >> 4), \ + (uint8_t) ((b) << 4 | ((c) & 0x3F) >> 2), \ + (uint8_t) ((c) << 6 | ((d) & 0x3F)), \ + (uint8_t) ((e) << 2 | ((f) & 0x3F) >> 4), \ + (uint8_t) ((f) << 4 | ((g) & 0x3F) >> 2), \ + (uint8_t) ((g) << 6 | ((h) & 0x3F)) \ +} +#define CS_ENTRY(id, a, b, c, d, e, f, g, h) \ +{ \ + id, \ + CS_ZIP_IDX( \ + CS_TXT_IDX_ ## a, CS_TXT_IDX_ ## b, \ + CS_TXT_IDX_ ## c, CS_TXT_IDX_ ## d, \ + CS_TXT_IDX_ ## e, CS_TXT_IDX_ ## f, \ + CS_TXT_IDX_ ## g, CS_TXT_IDX_ ## h \ + ) \ +} + +struct cs_entry { + uint16_t id; + uint8_t zip[6]; +}; + +/* !checksrc! disable COMMANOSPACE all */ +static const struct cs_entry cs_list [] = { + CS_ENTRY(0x002F, TLS,RSA,WITH,AES,128,CBC,SHA,), + CS_ENTRY(0x002F, AES128,SHA,,,,,,), + CS_ENTRY(0x0035, TLS,RSA,WITH,AES,256,CBC,SHA,), + CS_ENTRY(0x0035, AES256,SHA,,,,,,), + CS_ENTRY(0x003C, TLS,RSA,WITH,AES,128,CBC,SHA256,), + CS_ENTRY(0x003C, AES128,SHA256,,,,,,), + CS_ENTRY(0x003D, TLS,RSA,WITH,AES,256,CBC,SHA256,), + CS_ENTRY(0x003D, AES256,SHA256,,,,,,), + CS_ENTRY(0x009C, TLS,RSA,WITH,AES,128,GCM,SHA256,), + CS_ENTRY(0x009C, AES128,GCM,SHA256,,,,,), + CS_ENTRY(0x009D, TLS,RSA,WITH,AES,256,GCM,SHA384,), + CS_ENTRY(0x009D, AES256,GCM,SHA384,,,,,), + CS_ENTRY(0xC004, TLS,ECDH,ECDSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC004, ECDH,ECDSA,AES128,SHA,,,,), + CS_ENTRY(0xC005, TLS,ECDH,ECDSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC005, ECDH,ECDSA,AES256,SHA,,,,), + CS_ENTRY(0xC009, TLS,ECDHE,ECDSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC009, ECDHE,ECDSA,AES128,SHA,,,,), + CS_ENTRY(0xC00A, TLS,ECDHE,ECDSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC00A, ECDHE,ECDSA,AES256,SHA,,,,), + CS_ENTRY(0xC00E, TLS,ECDH,RSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC00E, ECDH,RSA,AES128,SHA,,,,), + CS_ENTRY(0xC00F, TLS,ECDH,RSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC00F, ECDH,RSA,AES256,SHA,,,,), + CS_ENTRY(0xC013, TLS,ECDHE,RSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC013, ECDHE,RSA,AES128,SHA,,,,), + CS_ENTRY(0xC014, TLS,ECDHE,RSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC014, ECDHE,RSA,AES256,SHA,,,,), + CS_ENTRY(0xC023, TLS,ECDHE,ECDSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0xC023, ECDHE,ECDSA,AES128,SHA256,,,,), + CS_ENTRY(0xC024, TLS,ECDHE,ECDSA,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0xC024, ECDHE,ECDSA,AES256,SHA384,,,,), + CS_ENTRY(0xC025, TLS,ECDH,ECDSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0xC025, ECDH,ECDSA,AES128,SHA256,,,,), + CS_ENTRY(0xC026, TLS,ECDH,ECDSA,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0xC026, ECDH,ECDSA,AES256,SHA384,,,,), + CS_ENTRY(0xC027, TLS,ECDHE,RSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0xC027, ECDHE,RSA,AES128,SHA256,,,,), + CS_ENTRY(0xC028, TLS,ECDHE,RSA,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0xC028, ECDHE,RSA,AES256,SHA384,,,,), + CS_ENTRY(0xC029, TLS,ECDH,RSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0xC029, ECDH,RSA,AES128,SHA256,,,,), + CS_ENTRY(0xC02A, TLS,ECDH,RSA,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0xC02A, ECDH,RSA,AES256,SHA384,,,,), + CS_ENTRY(0xC02B, TLS,ECDHE,ECDSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0xC02B, ECDHE,ECDSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0xC02C, TLS,ECDHE,ECDSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0xC02C, ECDHE,ECDSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0xC02D, TLS,ECDH,ECDSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0xC02D, ECDH,ECDSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0xC02E, TLS,ECDH,ECDSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0xC02E, ECDH,ECDSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0xC02F, TLS,ECDHE,RSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0xC02F, ECDHE,RSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0xC030, TLS,ECDHE,RSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0xC030, ECDHE,RSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0xC031, TLS,ECDH,RSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0xC031, ECDH,RSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0xC032, TLS,ECDH,RSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0xC032, ECDH,RSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0xCCA8, TLS,ECDHE,RSA,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCA8, ECDHE,RSA,CHACHA20,POLY1305,,,,), + CS_ENTRY(0xCCA9, TLS,ECDHE,ECDSA,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCA9, ECDHE,ECDSA,CHACHA20,POLY1305,,,,), +#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) + CS_ENTRY(0x0001, TLS,RSA,WITH,NULL,MD5,,,), + CS_ENTRY(0x0001, NULL,MD5,,,,,,), + CS_ENTRY(0x0002, TLS,RSA,WITH,NULL,SHA,,,), + CS_ENTRY(0x0002, NULL,SHA,,,,,,), + CS_ENTRY(0x002C, TLS,PSK,WITH,NULL,SHA,,,), + CS_ENTRY(0x002C, PSK,NULL,SHA,,,,,), + CS_ENTRY(0x002D, TLS,DHE,PSK,WITH,NULL,SHA,,), + CS_ENTRY(0x002D, DHE,PSK,NULL,SHA,,,,), + CS_ENTRY(0x002E, TLS,RSA,PSK,WITH,NULL,SHA,,), + CS_ENTRY(0x002E, RSA,PSK,NULL,SHA,,,,), + CS_ENTRY(0x0033, TLS,DHE,RSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0033, DHE,RSA,AES128,SHA,,,,), + CS_ENTRY(0x0039, TLS,DHE,RSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0039, DHE,RSA,AES256,SHA,,,,), + CS_ENTRY(0x003B, TLS,RSA,WITH,NULL,SHA256,,,), + CS_ENTRY(0x003B, NULL,SHA256,,,,,,), + CS_ENTRY(0x0067, TLS,DHE,RSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x0067, DHE,RSA,AES128,SHA256,,,,), + CS_ENTRY(0x006B, TLS,DHE,RSA,WITH,AES,256,CBC,SHA256), + CS_ENTRY(0x006B, DHE,RSA,AES256,SHA256,,,,), + CS_ENTRY(0x008C, TLS,PSK,WITH,AES,128,CBC,SHA,), + CS_ENTRY(0x008C, PSK,AES128,CBC,SHA,,,,), + CS_ENTRY(0x008D, TLS,PSK,WITH,AES,256,CBC,SHA,), + CS_ENTRY(0x008D, PSK,AES256,CBC,SHA,,,,), + CS_ENTRY(0x0090, TLS,DHE,PSK,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0090, DHE,PSK,AES128,CBC,SHA,,,), + CS_ENTRY(0x0091, TLS,DHE,PSK,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0091, DHE,PSK,AES256,CBC,SHA,,,), + CS_ENTRY(0x0094, TLS,RSA,PSK,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0094, RSA,PSK,AES128,CBC,SHA,,,), + CS_ENTRY(0x0095, TLS,RSA,PSK,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0095, RSA,PSK,AES256,CBC,SHA,,,), + CS_ENTRY(0x009E, TLS,DHE,RSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x009E, DHE,RSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0x009F, TLS,DHE,RSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x009F, DHE,RSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00A8, TLS,PSK,WITH,AES,128,GCM,SHA256,), + CS_ENTRY(0x00A8, PSK,AES128,GCM,SHA256,,,,), + CS_ENTRY(0x00A9, TLS,PSK,WITH,AES,256,GCM,SHA384,), + CS_ENTRY(0x00A9, PSK,AES256,GCM,SHA384,,,,), + CS_ENTRY(0x00AA, TLS,DHE,PSK,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00AA, DHE,PSK,AES128,GCM,SHA256,,,), + CS_ENTRY(0x00AB, TLS,DHE,PSK,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00AB, DHE,PSK,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00AC, TLS,RSA,PSK,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00AC, RSA,PSK,AES128,GCM,SHA256,,,), + CS_ENTRY(0x00AD, TLS,RSA,PSK,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00AD, RSA,PSK,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00AE, TLS,PSK,WITH,AES,128,CBC,SHA256,), + CS_ENTRY(0x00AE, PSK,AES128,CBC,SHA256,,,,), + CS_ENTRY(0x00AF, TLS,PSK,WITH,AES,256,CBC,SHA384,), + CS_ENTRY(0x00AF, PSK,AES256,CBC,SHA384,,,,), + CS_ENTRY(0x00B0, TLS,PSK,WITH,NULL,SHA256,,,), + CS_ENTRY(0x00B0, PSK,NULL,SHA256,,,,,), + CS_ENTRY(0x00B1, TLS,PSK,WITH,NULL,SHA384,,,), + CS_ENTRY(0x00B1, PSK,NULL,SHA384,,,,,), + CS_ENTRY(0x00B2, TLS,DHE,PSK,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x00B2, DHE,PSK,AES128,CBC,SHA256,,,), + CS_ENTRY(0x00B3, TLS,DHE,PSK,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0x00B3, DHE,PSK,AES256,CBC,SHA384,,,), + CS_ENTRY(0x00B4, TLS,DHE,PSK,WITH,NULL,SHA256,,), + CS_ENTRY(0x00B4, DHE,PSK,NULL,SHA256,,,,), + CS_ENTRY(0x00B5, TLS,DHE,PSK,WITH,NULL,SHA384,,), + CS_ENTRY(0x00B5, DHE,PSK,NULL,SHA384,,,,), + CS_ENTRY(0x00B6, TLS,RSA,PSK,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x00B6, RSA,PSK,AES128,CBC,SHA256,,,), + CS_ENTRY(0x00B7, TLS,RSA,PSK,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0x00B7, RSA,PSK,AES256,CBC,SHA384,,,), + CS_ENTRY(0x00B8, TLS,RSA,PSK,WITH,NULL,SHA256,,), + CS_ENTRY(0x00B8, RSA,PSK,NULL,SHA256,,,,), + CS_ENTRY(0x00B9, TLS,RSA,PSK,WITH,NULL,SHA384,,), + CS_ENTRY(0x00B9, RSA,PSK,NULL,SHA384,,,,), + CS_ENTRY(0x1301, TLS,AES,128,GCM,SHA256,,,), + CS_ENTRY(0x1302, TLS,AES,256,GCM,SHA384,,,), + CS_ENTRY(0x1303, TLS,CHACHA20,POLY1305,SHA256,,,,), + CS_ENTRY(0x1304, TLS,AES,128,CCM,SHA256,,,), + CS_ENTRY(0x1305, TLS,AES,128,CCM,8,SHA256,,), + CS_ENTRY(0xC001, TLS,ECDH,ECDSA,WITH,NULL,SHA,,), + CS_ENTRY(0xC001, ECDH,ECDSA,NULL,SHA,,,,), + CS_ENTRY(0xC006, TLS,ECDHE,ECDSA,WITH,NULL,SHA,,), + CS_ENTRY(0xC006, ECDHE,ECDSA,NULL,SHA,,,,), + CS_ENTRY(0xC00B, TLS,ECDH,RSA,WITH,NULL,SHA,,), + CS_ENTRY(0xC00B, ECDH,RSA,NULL,SHA,,,,), + CS_ENTRY(0xC010, TLS,ECDHE,RSA,WITH,NULL,SHA,,), + CS_ENTRY(0xC010, ECDHE,RSA,NULL,SHA,,,,), + CS_ENTRY(0xC035, TLS,ECDHE,PSK,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC035, ECDHE,PSK,AES128,CBC,SHA,,,), + CS_ENTRY(0xC036, TLS,ECDHE,PSK,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC036, ECDHE,PSK,AES256,CBC,SHA,,,), + CS_ENTRY(0xCCAB, TLS,PSK,WITH,CHACHA20,POLY1305,SHA256,,), + CS_ENTRY(0xCCAB, PSK,CHACHA20,POLY1305,,,,,), +#endif +#if defined(USE_SECTRANSP) || defined(USE_BEARSSL) + CS_ENTRY(0x000A, TLS,RSA,WITH,3DES,EDE,CBC,SHA,), + CS_ENTRY(0x000A, DES,CBC3,SHA,,,,,), + CS_ENTRY(0xC003, TLS,ECDH,ECDSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0xC003, ECDH,ECDSA,DES,CBC3,SHA,,,), + CS_ENTRY(0xC008, TLS,ECDHE,ECDSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0xC008, ECDHE,ECDSA,DES,CBC3,SHA,,,), + CS_ENTRY(0xC00D, TLS,ECDH,RSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0xC00D, ECDH,RSA,DES,CBC3,SHA,,,), + CS_ENTRY(0xC012, TLS,ECDHE,RSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0xC012, ECDHE,RSA,DES,CBC3,SHA,,,), +#endif +#if defined(USE_MBEDTLS) || defined(USE_BEARSSL) + CS_ENTRY(0xC09C, TLS,RSA,WITH,AES,128,CCM,,), + CS_ENTRY(0xC09C, AES128,CCM,,,,,,), + CS_ENTRY(0xC09D, TLS,RSA,WITH,AES,256,CCM,,), + CS_ENTRY(0xC09D, AES256,CCM,,,,,,), + CS_ENTRY(0xC0A0, TLS,RSA,WITH,AES,128,CCM,8,), + CS_ENTRY(0xC0A0, AES128,CCM8,,,,,,), + CS_ENTRY(0xC0A1, TLS,RSA,WITH,AES,256,CCM,8,), + CS_ENTRY(0xC0A1, AES256,CCM8,,,,,,), + CS_ENTRY(0xC0AC, TLS,ECDHE,ECDSA,WITH,AES,128,CCM,), + CS_ENTRY(0xC0AC, ECDHE,ECDSA,AES128,CCM,,,,), + CS_ENTRY(0xC0AD, TLS,ECDHE,ECDSA,WITH,AES,256,CCM,), + CS_ENTRY(0xC0AD, ECDHE,ECDSA,AES256,CCM,,,,), + CS_ENTRY(0xC0AE, TLS,ECDHE,ECDSA,WITH,AES,128,CCM,8), + CS_ENTRY(0xC0AE, ECDHE,ECDSA,AES128,CCM8,,,,), + CS_ENTRY(0xC0AF, TLS,ECDHE,ECDSA,WITH,AES,256,CCM,8), + CS_ENTRY(0xC0AF, ECDHE,ECDSA,AES256,CCM8,,,,), +#endif +#if defined(USE_SECTRANSP) + /* entries marked bc are backward compatible aliases for old OpenSSL names */ + CS_ENTRY(0x0003, TLS,RSA,EXPORT,WITH,RC4,40,MD5,), + CS_ENTRY(0x0003, EXP,RC4,MD5,,,,,), + CS_ENTRY(0x0004, TLS,RSA,WITH,RC4,128,MD5,,), + CS_ENTRY(0x0004, RC4,MD5,,,,,,), + CS_ENTRY(0x0005, TLS,RSA,WITH,RC4,128,SHA,,), + CS_ENTRY(0x0005, RC4,SHA,,,,,,), + CS_ENTRY(0x0006, TLS,RSA,EXPORT,WITH,RC2,CBC,40,MD5), + CS_ENTRY(0x0006, EXP,RC2,CBC,MD5,,,,), + CS_ENTRY(0x0007, TLS,RSA,WITH,IDEA,CBC,SHA,,), + CS_ENTRY(0x0007, IDEA,CBC,SHA,,,,,), + CS_ENTRY(0x0008, TLS,RSA,EXPORT,WITH,DES40,CBC,SHA,), + CS_ENTRY(0x0008, EXP,DES,CBC,SHA,,,,), + CS_ENTRY(0x0009, TLS,RSA,WITH,DES,CBC,SHA,,), + CS_ENTRY(0x0009, DES,CBC,SHA,,,,,), + CS_ENTRY(0x000B, TLS,DH,DSS,EXPORT,WITH,DES40,CBC,SHA), + CS_ENTRY(0x000B, EXP,DH,DSS,DES,CBC,SHA,,), + CS_ENTRY(0x000C, TLS,DH,DSS,WITH,DES,CBC,SHA,), + CS_ENTRY(0x000C, DH,DSS,DES,CBC,SHA,,,), + CS_ENTRY(0x000D, TLS,DH,DSS,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x000D, DH,DSS,DES,CBC3,SHA,,,), + CS_ENTRY(0x000E, TLS,DH,RSA,EXPORT,WITH,DES40,CBC,SHA), + CS_ENTRY(0x000E, EXP,DH,RSA,DES,CBC,SHA,,), + CS_ENTRY(0x000F, TLS,DH,RSA,WITH,DES,CBC,SHA,), + CS_ENTRY(0x000F, DH,RSA,DES,CBC,SHA,,,), + CS_ENTRY(0x0010, TLS,DH,RSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x0010, DH,RSA,DES,CBC3,SHA,,,), + CS_ENTRY(0x0011, TLS,DHE,DSS,EXPORT,WITH,DES40,CBC,SHA), + CS_ENTRY(0x0011, EXP,DHE,DSS,DES,CBC,SHA,,), + CS_ENTRY(0x0011, EXP,EDH,DSS,DES,CBC,SHA,,), /* bc */ + CS_ENTRY(0x0012, TLS,DHE,DSS,WITH,DES,CBC,SHA,), + CS_ENTRY(0x0012, DHE,DSS,DES,CBC,SHA,,,), + CS_ENTRY(0x0012, EDH,DSS,DES,CBC,SHA,,,), /* bc */ + CS_ENTRY(0x0013, TLS,DHE,DSS,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x0013, DHE,DSS,DES,CBC3,SHA,,,), + CS_ENTRY(0x0013, EDH,DSS,DES,CBC3,SHA,,,), /* bc */ + CS_ENTRY(0x0014, TLS,DHE,RSA,EXPORT,WITH,DES40,CBC,SHA), + CS_ENTRY(0x0014, EXP,DHE,RSA,DES,CBC,SHA,,), + CS_ENTRY(0x0014, EXP,EDH,RSA,DES,CBC,SHA,,), /* bc */ + CS_ENTRY(0x0015, TLS,DHE,RSA,WITH,DES,CBC,SHA,), + CS_ENTRY(0x0015, DHE,RSA,DES,CBC,SHA,,,), + CS_ENTRY(0x0015, EDH,RSA,DES,CBC,SHA,,,), /* bc */ + CS_ENTRY(0x0016, TLS,DHE,RSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x0016, DHE,RSA,DES,CBC3,SHA,,,), + CS_ENTRY(0x0016, EDH,RSA,DES,CBC3,SHA,,,), /* bc */ + CS_ENTRY(0x0017, TLS,DH,anon,EXPORT,WITH,RC4,40,MD5), + CS_ENTRY(0x0017, EXP,ADH,RC4,MD5,,,,), + CS_ENTRY(0x0018, TLS,DH,anon,WITH,RC4,128,MD5,), + CS_ENTRY(0x0018, ADH,RC4,MD5,,,,,), + CS_ENTRY(0x0019, TLS,DH,anon,EXPORT,WITH,DES40,CBC,SHA), + CS_ENTRY(0x0019, EXP,ADH,DES,CBC,SHA,,,), + CS_ENTRY(0x001A, TLS,DH,anon,WITH,DES,CBC,SHA,), + CS_ENTRY(0x001A, ADH,DES,CBC,SHA,,,,), + CS_ENTRY(0x001B, TLS,DH,anon,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x001B, ADH,DES,CBC3,SHA,,,,), + CS_ENTRY(0x0030, TLS,DH,DSS,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0030, DH,DSS,AES128,SHA,,,,), + CS_ENTRY(0x0031, TLS,DH,RSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0031, DH,RSA,AES128,SHA,,,,), + CS_ENTRY(0x0032, TLS,DHE,DSS,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0032, DHE,DSS,AES128,SHA,,,,), + CS_ENTRY(0x0034, TLS,DH,anon,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0034, ADH,AES128,SHA,,,,,), + CS_ENTRY(0x0036, TLS,DH,DSS,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0036, DH,DSS,AES256,SHA,,,,), + CS_ENTRY(0x0037, TLS,DH,RSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0037, DH,RSA,AES256,SHA,,,,), + CS_ENTRY(0x0038, TLS,DHE,DSS,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0038, DHE,DSS,AES256,SHA,,,,), + CS_ENTRY(0x003A, TLS,DH,anon,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x003A, ADH,AES256,SHA,,,,,), + CS_ENTRY(0x003E, TLS,DH,DSS,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x003E, DH,DSS,AES128,SHA256,,,,), + CS_ENTRY(0x003F, TLS,DH,RSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x003F, DH,RSA,AES128,SHA256,,,,), + CS_ENTRY(0x0040, TLS,DHE,DSS,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x0040, DHE,DSS,AES128,SHA256,,,,), + CS_ENTRY(0x0068, TLS,DH,DSS,WITH,AES,256,CBC,SHA256), + CS_ENTRY(0x0068, DH,DSS,AES256,SHA256,,,,), + CS_ENTRY(0x0069, TLS,DH,RSA,WITH,AES,256,CBC,SHA256), + CS_ENTRY(0x0069, DH,RSA,AES256,SHA256,,,,), + CS_ENTRY(0x006A, TLS,DHE,DSS,WITH,AES,256,CBC,SHA256), + CS_ENTRY(0x006A, DHE,DSS,AES256,SHA256,,,,), + CS_ENTRY(0x006C, TLS,DH,anon,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x006C, ADH,AES128,SHA256,,,,,), + CS_ENTRY(0x006D, TLS,DH,anon,WITH,AES,256,CBC,SHA256), + CS_ENTRY(0x006D, ADH,AES256,SHA256,,,,,), + CS_ENTRY(0x008A, TLS,PSK,WITH,RC4,128,SHA,,), + CS_ENTRY(0x008A, PSK,RC4,SHA,,,,,), + CS_ENTRY(0x008B, TLS,PSK,WITH,3DES,EDE,CBC,SHA,), + CS_ENTRY(0x008B, PSK,3DES,EDE,CBC,SHA,,,), + CS_ENTRY(0x008E, TLS,DHE,PSK,WITH,RC4,128,SHA,), + CS_ENTRY(0x008E, DHE,PSK,RC4,SHA,,,,), + CS_ENTRY(0x008F, TLS,DHE,PSK,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x008F, DHE,PSK,3DES,EDE,CBC,SHA,,), + CS_ENTRY(0x0092, TLS,RSA,PSK,WITH,RC4,128,SHA,), + CS_ENTRY(0x0092, RSA,PSK,RC4,SHA,,,,), + CS_ENTRY(0x0093, TLS,RSA,PSK,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x0093, RSA,PSK,3DES,EDE,CBC,SHA,,), + CS_ENTRY(0x00A0, TLS,DH,RSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00A0, DH,RSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0x00A1, TLS,DH,RSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00A1, DH,RSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00A2, TLS,DHE,DSS,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00A2, DHE,DSS,AES128,GCM,SHA256,,,), + CS_ENTRY(0x00A3, TLS,DHE,DSS,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00A3, DHE,DSS,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00A4, TLS,DH,DSS,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00A4, DH,DSS,AES128,GCM,SHA256,,,), + CS_ENTRY(0x00A5, TLS,DH,DSS,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00A5, DH,DSS,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00A6, TLS,DH,anon,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00A6, ADH,AES128,GCM,SHA256,,,,), + CS_ENTRY(0x00A7, TLS,DH,anon,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00A7, ADH,AES256,GCM,SHA384,,,,), + CS_ENTRY(0xC002, TLS,ECDH,ECDSA,WITH,RC4,128,SHA,), + CS_ENTRY(0xC002, ECDH,ECDSA,RC4,SHA,,,,), + CS_ENTRY(0xC007, TLS,ECDHE,ECDSA,WITH,RC4,128,SHA,), + CS_ENTRY(0xC007, ECDHE,ECDSA,RC4,SHA,,,,), + CS_ENTRY(0xC00C, TLS,ECDH,RSA,WITH,RC4,128,SHA,), + CS_ENTRY(0xC00C, ECDH,RSA,RC4,SHA,,,,), + CS_ENTRY(0xC011, TLS,ECDHE,RSA,WITH,RC4,128,SHA,), + CS_ENTRY(0xC011, ECDHE,RSA,RC4,SHA,,,,), + CS_ENTRY(0xC015, TLS,ECDH,anon,WITH,NULL,SHA,,), + CS_ENTRY(0xC015, AECDH,NULL,SHA,,,,,), + CS_ENTRY(0xC016, TLS,ECDH,anon,WITH,RC4,128,SHA,), + CS_ENTRY(0xC016, AECDH,RC4,SHA,,,,,), + CS_ENTRY(0xC017, TLS,ECDH,anon,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0xC017, AECDH,DES,CBC3,SHA,,,,), + CS_ENTRY(0xC018, TLS,ECDH,anon,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC018, AECDH,AES128,SHA,,,,,), + CS_ENTRY(0xC019, TLS,ECDH,anon,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC019, AECDH,AES256,SHA,,,,,), +#endif +#if defined(USE_MBEDTLS) + /* entries marked ns are "non-standard", they are not in OpenSSL */ + CS_ENTRY(0x0041, TLS,RSA,WITH,CAMELLIA,128,CBC,SHA,), + CS_ENTRY(0x0041, CAMELLIA128,SHA,,,,,,), + CS_ENTRY(0x0045, TLS,DHE,RSA,WITH,CAMELLIA,128,CBC,SHA), + CS_ENTRY(0x0045, DHE,RSA,CAMELLIA128,SHA,,,,), + CS_ENTRY(0x0084, TLS,RSA,WITH,CAMELLIA,256,CBC,SHA,), + CS_ENTRY(0x0084, CAMELLIA256,SHA,,,,,,), + CS_ENTRY(0x0088, TLS,DHE,RSA,WITH,CAMELLIA,256,CBC,SHA), + CS_ENTRY(0x0088, DHE,RSA,CAMELLIA256,SHA,,,,), + CS_ENTRY(0x00BA, TLS,RSA,WITH,CAMELLIA,128,CBC,SHA256,), + CS_ENTRY(0x00BA, CAMELLIA128,SHA256,,,,,,), + CS_ENTRY(0x00BE, TLS,DHE,RSA,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0x00BE, DHE,RSA,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0x00C0, TLS,RSA,WITH,CAMELLIA,256,CBC,SHA256,), + CS_ENTRY(0x00C0, CAMELLIA256,SHA256,,,,,,), + CS_ENTRY(0x00C4, TLS,DHE,RSA,WITH,CAMELLIA,256,CBC,SHA256), + CS_ENTRY(0x00C4, DHE,RSA,CAMELLIA256,SHA256,,,,), + CS_ENTRY(0xC037, TLS,ECDHE,PSK,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0xC037, ECDHE,PSK,AES128,CBC,SHA256,,,), + CS_ENTRY(0xC038, TLS,ECDHE,PSK,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0xC038, ECDHE,PSK,AES256,CBC,SHA384,,,), + CS_ENTRY(0xC039, TLS,ECDHE,PSK,WITH,NULL,SHA,,), + CS_ENTRY(0xC039, ECDHE,PSK,NULL,SHA,,,,), + CS_ENTRY(0xC03A, TLS,ECDHE,PSK,WITH,NULL,SHA256,,), + CS_ENTRY(0xC03A, ECDHE,PSK,NULL,SHA256,,,,), + CS_ENTRY(0xC03B, TLS,ECDHE,PSK,WITH,NULL,SHA384,,), + CS_ENTRY(0xC03B, ECDHE,PSK,NULL,SHA384,,,,), + CS_ENTRY(0xC03C, TLS,RSA,WITH,ARIA,128,CBC,SHA256,), + CS_ENTRY(0xC03C, ARIA128,SHA256,,,,,,), /* ns */ + CS_ENTRY(0xC03D, TLS,RSA,WITH,ARIA,256,CBC,SHA384,), + CS_ENTRY(0xC03D, ARIA256,SHA384,,,,,,), /* ns */ + CS_ENTRY(0xC044, TLS,DHE,RSA,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC044, DHE,RSA,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC045, TLS,DHE,RSA,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC045, DHE,RSA,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC048, TLS,ECDHE,ECDSA,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC048, ECDHE,ECDSA,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC049, TLS,ECDHE,ECDSA,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC049, ECDHE,ECDSA,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC04A, TLS,ECDH,ECDSA,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC04A, ECDH,ECDSA,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC04B, TLS,ECDH,ECDSA,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC04B, ECDH,ECDSA,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC04C, TLS,ECDHE,RSA,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC04C, ECDHE,ARIA128,SHA256,,,,,), /* ns */ + CS_ENTRY(0xC04D, TLS,ECDHE,RSA,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC04D, ECDHE,ARIA256,SHA384,,,,,), /* ns */ + CS_ENTRY(0xC04E, TLS,ECDH,RSA,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC04E, ECDH,ARIA128,SHA256,,,,,), /* ns */ + CS_ENTRY(0xC04F, TLS,ECDH,RSA,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC04F, ECDH,ARIA256,SHA384,,,,,), /* ns */ + CS_ENTRY(0xC050, TLS,RSA,WITH,ARIA,128,GCM,SHA256,), + CS_ENTRY(0xC050, ARIA128,GCM,SHA256,,,,,), + CS_ENTRY(0xC051, TLS,RSA,WITH,ARIA,256,GCM,SHA384,), + CS_ENTRY(0xC051, ARIA256,GCM,SHA384,,,,,), + CS_ENTRY(0xC052, TLS,DHE,RSA,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC052, DHE,RSA,ARIA128,GCM,SHA256,,,), + CS_ENTRY(0xC053, TLS,DHE,RSA,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC053, DHE,RSA,ARIA256,GCM,SHA384,,,), + CS_ENTRY(0xC05C, TLS,ECDHE,ECDSA,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC05C, ECDHE,ECDSA,ARIA128,GCM,SHA256,,,), + CS_ENTRY(0xC05D, TLS,ECDHE,ECDSA,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC05D, ECDHE,ECDSA,ARIA256,GCM,SHA384,,,), + CS_ENTRY(0xC05E, TLS,ECDH,ECDSA,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC05E, ECDH,ECDSA,ARIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC05F, TLS,ECDH,ECDSA,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC05F, ECDH,ECDSA,ARIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC060, TLS,ECDHE,RSA,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC060, ECDHE,ARIA128,GCM,SHA256,,,,), + CS_ENTRY(0xC061, TLS,ECDHE,RSA,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC061, ECDHE,ARIA256,GCM,SHA384,,,,), + CS_ENTRY(0xC062, TLS,ECDH,RSA,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC062, ECDH,ARIA128,GCM,SHA256,,,,), /* ns */ + CS_ENTRY(0xC063, TLS,ECDH,RSA,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC063, ECDH,ARIA256,GCM,SHA384,,,,), /* ns */ + CS_ENTRY(0xC064, TLS,PSK,WITH,ARIA,128,CBC,SHA256,), + CS_ENTRY(0xC064, PSK,ARIA128,SHA256,,,,,), /* ns */ + CS_ENTRY(0xC065, TLS,PSK,WITH,ARIA,256,CBC,SHA384,), + CS_ENTRY(0xC065, PSK,ARIA256,SHA384,,,,,), /* ns */ + CS_ENTRY(0xC066, TLS,DHE,PSK,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC066, DHE,PSK,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC067, TLS,DHE,PSK,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC067, DHE,PSK,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC068, TLS,RSA,PSK,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC068, RSA,PSK,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC069, TLS,RSA,PSK,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC069, RSA,PSK,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC06A, TLS,PSK,WITH,ARIA,128,GCM,SHA256,), + CS_ENTRY(0xC06A, PSK,ARIA128,GCM,SHA256,,,,), + CS_ENTRY(0xC06B, TLS,PSK,WITH,ARIA,256,GCM,SHA384,), + CS_ENTRY(0xC06B, PSK,ARIA256,GCM,SHA384,,,,), + CS_ENTRY(0xC06C, TLS,DHE,PSK,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC06C, DHE,PSK,ARIA128,GCM,SHA256,,,), + CS_ENTRY(0xC06D, TLS,DHE,PSK,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC06D, DHE,PSK,ARIA256,GCM,SHA384,,,), + CS_ENTRY(0xC06E, TLS,RSA,PSK,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC06E, RSA,PSK,ARIA128,GCM,SHA256,,,), + CS_ENTRY(0xC06F, TLS,RSA,PSK,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC06F, RSA,PSK,ARIA256,GCM,SHA384,,,), + CS_ENTRY(0xC070, TLS,ECDHE,PSK,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC070, ECDHE,PSK,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC071, TLS,ECDHE,PSK,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC071, ECDHE,PSK,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC072, TLS,ECDHE,ECDSA,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC072, ECDHE,ECDSA,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0xC073, TLS,ECDHE,ECDSA,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC073, ECDHE,ECDSA,CAMELLIA256,SHA384,,,,), + CS_ENTRY(0xC074, TLS,ECDH,ECDSA,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC074, ECDH,ECDSA,CAMELLIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC075, TLS,ECDH,ECDSA,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC075, ECDH,ECDSA,CAMELLIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC076, TLS,ECDHE,RSA,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC076, ECDHE,RSA,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0xC077, TLS,ECDHE,RSA,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC077, ECDHE,RSA,CAMELLIA256,SHA384,,,,), + CS_ENTRY(0xC078, TLS,ECDH,RSA,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC078, ECDH,CAMELLIA128,SHA256,,,,,), /* ns */ + CS_ENTRY(0xC079, TLS,ECDH,RSA,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC079, ECDH,CAMELLIA256,SHA384,,,,,), /* ns */ + CS_ENTRY(0xC07A, TLS,RSA,WITH,CAMELLIA,128,GCM,SHA256,), + CS_ENTRY(0xC07A, CAMELLIA128,GCM,SHA256,,,,,), /* ns */ + CS_ENTRY(0xC07B, TLS,RSA,WITH,CAMELLIA,256,GCM,SHA384,), + CS_ENTRY(0xC07B, CAMELLIA256,GCM,SHA384,,,,,), /* ns */ + CS_ENTRY(0xC07C, TLS,DHE,RSA,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC07C, DHE,RSA,CAMELLIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC07D, TLS,DHE,RSA,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC07D, DHE,RSA,CAMELLIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC086, TLS,ECDHE,ECDSA,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC086, ECDHE,ECDSA,CAMELLIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC087, TLS,ECDHE,ECDSA,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC087, ECDHE,ECDSA,CAMELLIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC088, TLS,ECDH,ECDSA,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC088, ECDH,ECDSA,CAMELLIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC089, TLS,ECDH,ECDSA,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC089, ECDH,ECDSA,CAMELLIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC08A, TLS,ECDHE,RSA,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC08A, ECDHE,CAMELLIA128,GCM,SHA256,,,,), /* ns */ + CS_ENTRY(0xC08B, TLS,ECDHE,RSA,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC08B, ECDHE,CAMELLIA256,GCM,SHA384,,,,), /* ns */ + CS_ENTRY(0xC08C, TLS,ECDH,RSA,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC08C, ECDH,CAMELLIA128,GCM,SHA256,,,,), /* ns */ + CS_ENTRY(0xC08D, TLS,ECDH,RSA,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC08D, ECDH,CAMELLIA256,GCM,SHA384,,,,), /* ns */ + CS_ENTRY(0xC08E, TLS,PSK,WITH,CAMELLIA,128,GCM,SHA256,), + CS_ENTRY(0xC08E, PSK,CAMELLIA128,GCM,SHA256,,,,), /* ns */ + CS_ENTRY(0xC08F, TLS,PSK,WITH,CAMELLIA,256,GCM,SHA384,), + CS_ENTRY(0xC08F, PSK,CAMELLIA256,GCM,SHA384,,,,), /* ns */ + CS_ENTRY(0xC090, TLS,DHE,PSK,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC090, DHE,PSK,CAMELLIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC091, TLS,DHE,PSK,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC091, DHE,PSK,CAMELLIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC092, TLS,RSA,PSK,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC092, RSA,PSK,CAMELLIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC093, TLS,RSA,PSK,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC093, RSA,PSK,CAMELLIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC094, TLS,PSK,WITH,CAMELLIA,128,CBC,SHA256,), + CS_ENTRY(0xC094, PSK,CAMELLIA128,SHA256,,,,,), + CS_ENTRY(0xC095, TLS,PSK,WITH,CAMELLIA,256,CBC,SHA384,), + CS_ENTRY(0xC095, PSK,CAMELLIA256,SHA384,,,,,), + CS_ENTRY(0xC096, TLS,DHE,PSK,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC096, DHE,PSK,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0xC097, TLS,DHE,PSK,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC097, DHE,PSK,CAMELLIA256,SHA384,,,,), + CS_ENTRY(0xC098, TLS,RSA,PSK,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC098, RSA,PSK,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0xC099, TLS,RSA,PSK,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC099, RSA,PSK,CAMELLIA256,SHA384,,,,), + CS_ENTRY(0xC09A, TLS,ECDHE,PSK,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC09A, ECDHE,PSK,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0xC09B, TLS,ECDHE,PSK,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC09B, ECDHE,PSK,CAMELLIA256,SHA384,,,,), + CS_ENTRY(0xC09E, TLS,DHE,RSA,WITH,AES,128,CCM,), + CS_ENTRY(0xC09E, DHE,RSA,AES128,CCM,,,,), + CS_ENTRY(0xC09F, TLS,DHE,RSA,WITH,AES,256,CCM,), + CS_ENTRY(0xC09F, DHE,RSA,AES256,CCM,,,,), + CS_ENTRY(0xC0A2, TLS,DHE,RSA,WITH,AES,128,CCM,8), + CS_ENTRY(0xC0A2, DHE,RSA,AES128,CCM8,,,,), + CS_ENTRY(0xC0A3, TLS,DHE,RSA,WITH,AES,256,CCM,8), + CS_ENTRY(0xC0A3, DHE,RSA,AES256,CCM8,,,,), + CS_ENTRY(0xC0A4, TLS,PSK,WITH,AES,128,CCM,,), + CS_ENTRY(0xC0A4, PSK,AES128,CCM,,,,,), + CS_ENTRY(0xC0A5, TLS,PSK,WITH,AES,256,CCM,,), + CS_ENTRY(0xC0A5, PSK,AES256,CCM,,,,,), + CS_ENTRY(0xC0A6, TLS,DHE,PSK,WITH,AES,128,CCM,), + CS_ENTRY(0xC0A6, DHE,PSK,AES128,CCM,,,,), + CS_ENTRY(0xC0A7, TLS,DHE,PSK,WITH,AES,256,CCM,), + CS_ENTRY(0xC0A7, DHE,PSK,AES256,CCM,,,,), + CS_ENTRY(0xC0A8, TLS,PSK,WITH,AES,128,CCM,8,), + CS_ENTRY(0xC0A8, PSK,AES128,CCM8,,,,,), + CS_ENTRY(0xC0A9, TLS,PSK,WITH,AES,256,CCM,8,), + CS_ENTRY(0xC0A9, PSK,AES256,CCM8,,,,,), + CS_ENTRY(0xC0AA, TLS,PSK,DHE,WITH,AES,128,CCM,8), + CS_ENTRY(0xC0AA, DHE,PSK,AES128,CCM8,,,,), + CS_ENTRY(0xC0AB, TLS,PSK,DHE,WITH,AES,256,CCM,8), + CS_ENTRY(0xC0AB, DHE,PSK,AES256,CCM8,,,,), + CS_ENTRY(0xCCAA, TLS,DHE,RSA,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCAA, DHE,RSA,CHACHA20,POLY1305,,,,), + CS_ENTRY(0xCCAC, TLS,ECDHE,PSK,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCAC, ECDHE,PSK,CHACHA20,POLY1305,,,,), + CS_ENTRY(0xCCAD, TLS,DHE,PSK,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCAD, DHE,PSK,CHACHA20,POLY1305,,,,), + CS_ENTRY(0xCCAE, TLS,RSA,PSK,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCAE, RSA,PSK,CHACHA20,POLY1305,,,,), +#endif +}; +#define CS_LIST_LEN (sizeof(cs_list) / sizeof(cs_list[0])) + +static int cs_str_to_zip(const char *cs_str, size_t cs_len, + uint8_t zip[6]) +{ + uint8_t indexes[8] = {0}; + const char *entry, *cur; + const char *nxt = cs_str; + const char *end = cs_str + cs_len; + char separator = '-'; + int idx, i = 0; + size_t len; + + /* split the cipher string by '-' or '_' */ + if(strncasecompare(cs_str, "TLS", 3)) + separator = '_'; + + do { + if(i == 8) + return -1; + + /* determine the length of the part */ + cur = nxt; + for(; nxt < end && *nxt != '\0' && *nxt != separator; nxt++); + len = nxt - cur; + + /* lookup index for the part (skip empty string at 0) */ + for(idx = 1, entry = cs_txt + 1; idx < CS_TXT_LEN; idx++) { + size_t elen = strlen(entry); + if(elen == len && strncasecompare(entry, cur, len)) + break; + entry += elen + 1; + } + if(idx == CS_TXT_LEN) + return -1; + + indexes[i++] = (uint8_t) idx; + } while(nxt < end && *(nxt++) != '\0'); + + /* zip the 8 indexes into 48 bits */ + zip[0] = (uint8_t) (indexes[0] << 2 | (indexes[1] & 0x3F) >> 4); + zip[1] = (uint8_t) (indexes[1] << 4 | (indexes[2] & 0x3F) >> 2); + zip[2] = (uint8_t) (indexes[2] << 6 | (indexes[3] & 0x3F)); + zip[3] = (uint8_t) (indexes[4] << 2 | (indexes[5] & 0x3F) >> 4); + zip[4] = (uint8_t) (indexes[5] << 4 | (indexes[6] & 0x3F) >> 2); + zip[5] = (uint8_t) (indexes[6] << 6 | (indexes[7] & 0x3F)); + + return 0; +} + +static int cs_zip_to_str(const uint8_t zip[6], + char *buf, size_t buf_size) +{ + uint8_t indexes[8] = {0}; + const char *entry; + char separator = '-'; + int idx, i, r; + size_t len = 0; + + /* unzip the 8 indexes */ + indexes[0] = zip[0] >> 2; + indexes[1] = ((zip[0] << 4) & 0x3F) | zip[1] >> 4; + indexes[2] = ((zip[1] << 2) & 0x3F) | zip[2] >> 6; + indexes[3] = ((zip[2] << 0) & 0x3F); + indexes[4] = zip[3] >> 2; + indexes[5] = ((zip[3] << 4) & 0x3F) | zip[4] >> 4; + indexes[6] = ((zip[4] << 2) & 0x3F) | zip[5] >> 6; + indexes[7] = ((zip[5] << 0) & 0x3F); + + if(indexes[0] == CS_TXT_IDX_TLS) + separator = '_'; + + for(i = 0; i < 8 && indexes[i] != 0 && len < buf_size; i++) { + if(indexes[i] >= CS_TXT_LEN) + return -1; + + /* lookup the part string for the index (skip empty string at 0) */ + for(idx = 1, entry = cs_txt + 1; idx < indexes[i]; idx++) { + size_t elen = strlen(entry); + entry += elen + 1; + } + + /* append the part string to the buffer */ + if(i > 0) + r = msnprintf(&buf[len], buf_size - len, "%c%s", separator, entry); + else + r = msnprintf(&buf[len], buf_size - len, "%s", entry); + + if(r < 0) + return -1; + len += r; + } + + return 0; +} + +uint16_t Curl_cipher_suite_lookup_id(const char *cs_str, size_t cs_len) +{ + size_t i; + uint8_t zip[6]; + + if(cs_len > 0 && cs_str_to_zip(cs_str, cs_len, zip) == 0) { + for(i = 0; i < CS_LIST_LEN; i++) { + if(memcmp(cs_list[i].zip, zip, sizeof(zip)) == 0) + return cs_list[i].id; + } + } + + return 0; +} + +static bool cs_is_separator(char c) +{ + switch(c) { + case ' ': + case '\t': + case ':': + case ',': + case ';': + return true; + default:; + } + return false; +} + +uint16_t Curl_cipher_suite_walk_str(const char **str, const char **end) +{ + /* move string pointer to first non-separator or end of string */ + for(; cs_is_separator(*str[0]); (*str)++); + + /* move end pointer to next separator or end of string */ + for(*end = *str; *end[0] != '\0' && !cs_is_separator(*end[0]); (*end)++); + + return Curl_cipher_suite_lookup_id(*str, *end - *str); +} + +int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, + bool prefer_rfc) +{ + size_t i, j = CS_LIST_LEN; + int r = -1; + + for(i = 0; i < CS_LIST_LEN; i++) { + if(cs_list[i].id != id) + continue; + if((cs_list[i].zip[0] >> 2 != CS_TXT_IDX_TLS) == !prefer_rfc) { + j = i; + break; + } + if(j == CS_LIST_LEN) + j = i; + } + + if(j < CS_LIST_LEN) + r = cs_zip_to_str(cs_list[j].zip, buf, buf_size); + + if(r < 0) + msnprintf(buf, buf_size, "TLS_UNKNOWN_0x%04x", id); + + return r; +} + +#endif /* defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \ + defined(USE_BEARSSL) */ diff --git a/third_party/curl/lib/vtls/cipher_suite.h b/third_party/curl/lib/vtls/cipher_suite.h new file mode 100644 index 0000000000..1dd4d7b400 --- /dev/null +++ b/third_party/curl/lib/vtls/cipher_suite.h @@ -0,0 +1,47 @@ +#ifndef HEADER_CURL_CIPHER_SUITE_H +#define HEADER_CURL_CIPHER_SUITE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Jan Venekamp, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || defined(USE_BEARSSL) +#include + +/* Lookup IANA id for cipher suite string, returns 0 if not recognized */ +uint16_t Curl_cipher_suite_lookup_id(const char *cs_str, size_t cs_len); + +/* Walk over cipher suite string, update str and end pointers to next + cipher suite in string, returns IANA id of that suite if recognized */ +uint16_t Curl_cipher_suite_walk_str(const char **str, const char **end); + +/* Copy openssl or RFC name for cipher suite in supplied buffer. + Caller is responsible to supply sufficiently large buffer (size + of 64 should suffice), excess bytes are silently truncated. */ +int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, + bool prefer_rfc); + +#endif /* defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \ + defined(USE_BEARSSL) */ +#endif /* HEADER_CURL_CIPHER_SUITE_H */ diff --git a/third_party/curl/lib/vtls/gtls.c b/third_party/curl/lib/vtls/gtls.c new file mode 100644 index 0000000000..39c22c2003 --- /dev/null +++ b/third_party/curl/lib/vtls/gtls.c @@ -0,0 +1,2024 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code + * but vtls.c should ever call or use these functions. + * + * Note: do not use the GnuTLS' *_t variable type names in this source code, + * since they were not present in 1.0.X. + */ + +#include "curl_setup.h" + +#ifdef USE_GNUTLS + +#include +#include +#include +#include +#include + +#include "urldata.h" +#include "sendf.h" +#include "inet_pton.h" +#include "keylog.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 */ +#include "select.h" +#include "strcase.h" +#include "warnless.h" +#include "x509asn1.h" +#include "multiif.h" +#include "curl_printf.h" +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#define QUIC_PRIORITY \ + "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ + "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ + "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ + "%DISABLE_TLS13_COMPAT_MODE" + +/* Enable GnuTLS debugging by defining GTLSDEBUG */ +/*#define GTLSDEBUG */ + +#ifdef GTLSDEBUG +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "|<%d>| %s", level, str); +} +#endif +static bool gtls_inited = FALSE; + +#if !defined(GNUTLS_VERSION_NUMBER) || (GNUTLS_VERSION_NUMBER < 0x03010a) +#error "too old GnuTLS version" +#endif + +# include + +struct gtls_ssl_backend_data { + struct gtls_ctx gtls; +}; + +static ssize_t gtls_push(void *s, const void *buf, size_t blen) +{ + struct Curl_cfilter *cf = s; + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nwritten; + CURLcode result; + + DEBUGASSERT(data); + nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %zd, err=%d", + blen, nwritten, result); + backend->gtls.io_result = result; + if(nwritten < 0) { + gnutls_transport_set_errno(backend->gtls.session, + (CURLE_AGAIN == result)? EAGAIN : EINVAL); + nwritten = -1; + } + return nwritten; +} + +static ssize_t gtls_pull(void *s, void *buf, size_t blen) +{ + struct Curl_cfilter *cf = s; + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nread; + CURLcode result; + + DEBUGASSERT(data); + if(!backend->gtls.shared_creds->trust_setup) { + result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls); + if(result) { + gnutls_transport_set_errno(backend->gtls.session, EINVAL); + backend->gtls.io_result = result; + return -1; + } + } + + nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); + CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %zd, err=%d", + blen, nread, result); + backend->gtls.io_result = result; + if(nread < 0) { + gnutls_transport_set_errno(backend->gtls.session, + (CURLE_AGAIN == result)? EAGAIN : EINVAL); + nread = -1; + } + else if(nread == 0) + connssl->peer_closed = TRUE; + return nread; +} + +/* gtls_init() + * + * Global GnuTLS init, called from Curl_ssl_init(). This calls functions that + * are not thread-safe and thus this function itself is not thread-safe and + * must only be called from within curl_global_init() to keep the thread + * situation under control! + */ +static int gtls_init(void) +{ + int ret = 1; + if(!gtls_inited) { + ret = gnutls_global_init()?0:1; +#ifdef GTLSDEBUG + gnutls_global_set_log_function(tls_log_func); + gnutls_global_set_log_level(2); +#endif + gtls_inited = TRUE; + } + return ret; +} + +static void gtls_cleanup(void) +{ + if(gtls_inited) { + gnutls_global_deinit(); + gtls_inited = FALSE; + } +} + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void showtime(struct Curl_easy *data, + const char *text, + time_t stamp) +{ + struct tm buffer; + const struct tm *tm = &buffer; + char str[96]; + CURLcode result = Curl_gmtime(stamp, &buffer); + if(result) + return; + + msnprintf(str, + sizeof(str), + " %s: %s, %02d %s %4d %02d:%02d:%02d GMT", + text, + Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], + tm->tm_mday, + Curl_month[tm->tm_mon], + tm->tm_year + 1900, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + infof(data, "%s", str); +} +#endif + +static gnutls_datum_t load_file(const char *file) +{ + FILE *f; + gnutls_datum_t loaded_file = { NULL, 0 }; + long filelen; + void *ptr; + + f = fopen(file, "rb"); + if(!f) + return loaded_file; + if(fseek(f, 0, SEEK_END) != 0 + || (filelen = ftell(f)) < 0 + || fseek(f, 0, SEEK_SET) != 0 + || !(ptr = malloc((size_t)filelen))) + goto out; + if(fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) { + free(ptr); + goto out; + } + + loaded_file.data = ptr; + loaded_file.size = (unsigned int)filelen; +out: + fclose(f); + return loaded_file; +} + +static void unload_file(gnutls_datum_t data) +{ + free(data.data); +} + + +/* this function does a SSL/TLS (re-)handshake */ +static CURLcode handshake(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool duringconnect, + bool nonblocking) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + gnutls_session_t session; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + + DEBUGASSERT(backend); + session = backend->gtls.session; + connssl->connecting_state = ssl_connect_2; + + for(;;) { + timediff_t timeout_ms; + int rc; + + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, duringconnect); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { + int what; + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; + + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + nonblocking?0: + timeout_ms?timeout_ms:1000); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + return CURLE_SSL_CONNECT_ERROR; + } + else if(0 == what) { + if(nonblocking) + return CURLE_OK; + else if(timeout_ms) { + /* timeout */ + failf(data, "SSL connection timeout at %ld", (long)timeout_ms); + return CURLE_OPERATION_TIMEDOUT; + } + } + /* socket is readable or writable */ + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + backend->gtls.io_result = CURLE_OK; + rc = gnutls_handshake(session); + + if(!backend->gtls.shared_creds->trust_setup) { + /* After having send off the ClientHello, we prepare the trust + * store to verify the coming certificate from the server */ + CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls); + if(result) + return result; + } + + if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { + connssl->io_need = + gnutls_record_get_direction(session)? + CURL_SSL_IO_NEED_SEND:CURL_SSL_IO_NEED_RECV; + continue; + } + else if((rc < 0) && !gnutls_error_is_fatal(rc)) { + const char *strerr = NULL; + + if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { + gnutls_alert_description_t alert = gnutls_alert_get(session); + strerr = gnutls_alert_get_name(alert); + } + + if(!strerr) + strerr = gnutls_strerror(rc); + + infof(data, "gnutls_handshake() warning: %s", strerr); + continue; + } + else if((rc < 0) && backend->gtls.io_result) { + return backend->gtls.io_result; + } + else if(rc < 0) { + const char *strerr = NULL; + + if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) { + gnutls_alert_description_t alert = gnutls_alert_get(session); + strerr = gnutls_alert_get_name(alert); + } + + if(!strerr) + strerr = gnutls_strerror(rc); + + failf(data, "gnutls_handshake() failed: %s", strerr); + return CURLE_SSL_CONNECT_ERROR; + } + + /* Reset our connect state machine */ + connssl->connecting_state = ssl_connect_1; + return CURLE_OK; + } +} + +static gnutls_x509_crt_fmt_t do_file_type(const char *type) +{ + if(!type || !type[0]) + return GNUTLS_X509_FMT_PEM; + if(strcasecompare(type, "PEM")) + return GNUTLS_X509_FMT_PEM; + if(strcasecompare(type, "DER")) + return GNUTLS_X509_FMT_DER; + return GNUTLS_X509_FMT_PEM; /* default to PEM */ +} + +#define GNUTLS_CIPHERS "NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509" +/* If GnuTLS was compiled without support for SRP it will error out if SRP is + requested in the priority string, so treat it specially + */ +#define GNUTLS_SRP "+SRP" + +static CURLcode +set_ssl_version_min_max(struct Curl_easy *data, + struct ssl_peer *peer, + struct ssl_primary_config *conn_config, + const char **prioritylist, + const char *tls13support) +{ + long ssl_version = conn_config->version; + long ssl_version_max = conn_config->version_max; + + if((ssl_version == CURL_SSLVERSION_DEFAULT) || + (ssl_version == CURL_SSLVERSION_TLSv1)) + ssl_version = CURL_SSLVERSION_TLSv1_0; + if(ssl_version_max == CURL_SSLVERSION_MAX_NONE) + ssl_version_max = CURL_SSLVERSION_MAX_DEFAULT; + + if(peer->transport == TRNSPRT_QUIC) { + if((ssl_version_max != CURL_SSLVERSION_MAX_DEFAULT) && + (ssl_version_max < CURL_SSLVERSION_MAX_TLSv1_3)) { + failf(data, "QUIC needs at least TLS version 1.3"); + return CURLE_SSL_CONNECT_ERROR; + } + *prioritylist = QUIC_PRIORITY; + return CURLE_OK; + } + + if(!tls13support) { + /* If the running GnuTLS does not support TLS 1.3, we must not specify a + prioritylist involving that since it will make GnuTLS return an en + error back at us */ + if((ssl_version_max == CURL_SSLVERSION_MAX_TLSv1_3) || + (ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT)) { + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; + } + } + else if(ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT) { + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; + } + + switch(ssl_version | ssl_version_max) { + case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_0: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.0"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_1: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.1:+VERS-TLS1.0"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_1: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.1"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_2: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.2:+VERS-TLS1.1"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.2"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_3 | CURL_SSLVERSION_MAX_TLSv1_3: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.3"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_3: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_3: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.1"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_3: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.3:+VERS-TLS1.2"; + return CURLE_OK; + } + + failf(data, "GnuTLS: cannot set ssl protocol"); + return CURLE_SSL_CONNECT_ERROR; +} + +CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data, + struct gtls_shared_creds **pcreds) +{ + struct gtls_shared_creds *shared; + int rc; + + *pcreds = NULL; + shared = calloc(1, sizeof(*shared)); + if(!shared) + return CURLE_OUT_OF_MEMORY; + + rc = gnutls_certificate_allocate_credentials(&shared->creds); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); + free(shared); + return CURLE_SSL_CONNECT_ERROR; + } + + shared->refcount = 1; + shared->time = Curl_now(); + *pcreds = shared; + return CURLE_OK; +} + +CURLcode Curl_gtls_shared_creds_up_ref(struct gtls_shared_creds *creds) +{ + DEBUGASSERT(creds); + if(creds->refcount < SIZE_T_MAX) { + ++creds->refcount; + return CURLE_OK; + } + return CURLE_BAD_FUNCTION_ARGUMENT; +} + +void Curl_gtls_shared_creds_free(struct gtls_shared_creds **pcreds) +{ + struct gtls_shared_creds *shared = *pcreds; + *pcreds = NULL; + if(shared) { + --shared->refcount; + if(!shared->refcount) { + gnutls_certificate_free_credentials(shared->creds); + free(shared->CAfile); + free(shared); + } + } +} + +static CURLcode gtls_populate_creds(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_certificate_credentials_t creds) +{ + struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + int rc; + + if(config->verifypeer) { + bool imported_native_ca = false; + + if(ssl_config->native_ca_store) { + rc = gnutls_certificate_set_x509_system_trust(creds); + if(rc < 0) + infof(data, "error reading native ca store (%s), continuing anyway", + gnutls_strerror(rc)); + else { + infof(data, "found %d certificates in native ca store", rc); + if(rc > 0) + imported_native_ca = true; + } + } + + if(config->CAfile) { + /* set the trusted CA cert bundle file */ + gnutls_certificate_set_verify_flags(creds, + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); + + rc = gnutls_certificate_set_x509_trust_file(creds, + config->CAfile, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + infof(data, "error reading ca cert file %s (%s)%s", + config->CAfile, gnutls_strerror(rc), + (imported_native_ca ? ", continuing anyway" : "")); + if(!imported_native_ca) { + ssl_config->certverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } + } + else + infof(data, "found %d certificates in %s", rc, config->CAfile); + } + + if(config->CApath) { + /* set the trusted CA cert directory */ + rc = gnutls_certificate_set_x509_trust_dir(creds, config->CApath, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + infof(data, "error reading ca cert file %s (%s)%s", + config->CApath, gnutls_strerror(rc), + (imported_native_ca ? ", continuing anyway" : "")); + if(!imported_native_ca) { + ssl_config->certverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } + } + else + infof(data, "found %d certificates in %s", rc, config->CApath); + } + } + + if(config->CRLfile) { + /* set the CRL list file */ + rc = gnutls_certificate_set_x509_crl_file(creds, config->CRLfile, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + failf(data, "error reading crl file %s (%s)", + config->CRLfile, gnutls_strerror(rc)); + return CURLE_SSL_CRL_BADFILE; + } + else + infof(data, "found %d CRL in %s", rc, config->CRLfile); + } + + return CURLE_OK; +} + +/* key to use at `multi->proto_hash` */ +#define MPROTO_GTLS_X509_KEY "tls:gtls:x509:share" + +static bool gtls_shared_creds_expired(const struct Curl_easy *data, + const struct gtls_shared_creds *sc) +{ + const struct ssl_general_config *cfg = &data->set.general_ssl; + struct curltime now = Curl_now(); + timediff_t elapsed_ms = Curl_timediff(now, sc->time); + timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + + if(timeout_ms < 0) + return false; + + return elapsed_ms >= timeout_ms; +} + +static bool gtls_shared_creds_different(struct Curl_cfilter *cf, + const struct gtls_shared_creds *sc) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!sc->CAfile || !conn_config->CAfile) + return sc->CAfile != conn_config->CAfile; + + return strcmp(sc->CAfile, conn_config->CAfile); +} + +static struct gtls_shared_creds* +gtls_get_cached_creds(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct gtls_shared_creds *shared_creds; + + if(data->multi) { + shared_creds = Curl_hash_pick(&data->multi->proto_hash, + (void *)MPROTO_GTLS_X509_KEY, + sizeof(MPROTO_GTLS_X509_KEY)-1); + if(shared_creds && shared_creds->creds && + !gtls_shared_creds_expired(data, shared_creds) && + !gtls_shared_creds_different(cf, shared_creds)) { + return shared_creds; + } + } + return NULL; +} + +static void gtls_shared_creds_hash_free(void *key, size_t key_len, void *p) +{ + struct gtls_shared_creds *sc = p; + DEBUGASSERT(key_len == (sizeof(MPROTO_GTLS_X509_KEY)-1)); + DEBUGASSERT(!memcmp(MPROTO_GTLS_X509_KEY, key, key_len)); + (void)key; + (void)key_len; + Curl_gtls_shared_creds_free(&sc); /* down reference */ +} + +static void gtls_set_cached_creds(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct gtls_shared_creds *sc) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + + DEBUGASSERT(sc); + DEBUGASSERT(sc->creds); + DEBUGASSERT(!sc->CAfile); + DEBUGASSERT(sc->refcount == 1); + if(!data->multi) + return; + + if(conn_config->CAfile) { + sc->CAfile = strdup(conn_config->CAfile); + if(!sc->CAfile) + return; + } + + if(Curl_gtls_shared_creds_up_ref(sc)) + return; + + if(!Curl_hash_add2(&data->multi->proto_hash, + (void *)MPROTO_GTLS_X509_KEY, + sizeof(MPROTO_GTLS_X509_KEY)-1, + sc, gtls_shared_creds_hash_free)) { + Curl_gtls_shared_creds_free(&sc); /* down reference again */ + return; + } +} + +CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct gtls_ctx *gtls) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct gtls_shared_creds *cached_creds = NULL; + bool cache_criteria_met; + CURLcode result; + int rc; + + + /* Consider the X509 store cacheable if it comes exclusively from a CAfile, + or no source is provided and we are falling back to OpenSSL's built-in + default. */ + cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) && + conn_config->verifypeer && + !conn_config->CApath && + !conn_config->ca_info_blob && + !ssl_config->primary.CRLfile && + !ssl_config->native_ca_store && + !conn_config->clientcert; /* GnuTLS adds client cert to its credentials! */ + + if(cache_criteria_met) + cached_creds = gtls_get_cached_creds(cf, data); + + if(cached_creds && !Curl_gtls_shared_creds_up_ref(cached_creds)) { + CURL_TRC_CF(data, cf, "using shared trust anchors and CRLs"); + Curl_gtls_shared_creds_free(>ls->shared_creds); + gtls->shared_creds = cached_creds; + rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_CERTIFICATE, + gtls->shared_creds->creds); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { + CURL_TRC_CF(data, cf, "loading trust anchors and CRLs"); + result = gtls_populate_creds(cf, data, gtls->shared_creds->creds); + if(result) + return result; + gtls->shared_creds->trust_setup = TRUE; + if(cache_criteria_met) + gtls_set_cached_creds(cf, data, gtls->shared_creds); + } + return CURLE_OK; +} + +static void gtls_sessionid_free(void *sessionid, size_t idsize) +{ + (void)idsize; + free(sessionid); +} + +static CURLcode gtls_update_session_id(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_session_t session) +{ + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_connect_data *connssl = cf->ctx; + CURLcode result = CURLE_OK; + + if(ssl_config->primary.cache_session) { + /* we always unconditionally get the session id here, as even if we + already got it from the cache and asked to use it in the connection, it + might've been rejected and then a new one is in use now and we need to + detect that. */ + void *connect_sessionid; + size_t connect_idsize = 0; + + /* get the session ID data size */ + gnutls_session_get_data(session, NULL, &connect_idsize); + connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ + if(!connect_sessionid) { + return CURLE_OUT_OF_MEMORY; + } + else { + /* extract session ID to the allocated buffer */ + gnutls_session_get_data(session, connect_sessionid, &connect_idsize); + + CURL_TRC_CF(data, cf, "get session id (len=%zu) and store in cache", + connect_idsize); + Curl_ssl_sessionid_lock(data); + /* store this session id, takes ownership */ + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, + connect_sessionid, connect_idsize, + gtls_sessionid_free); + Curl_ssl_sessionid_unlock(data); + } + } + return result; +} + +static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, + unsigned when, unsigned int incoming, + const gnutls_datum_t *msg) +{ + struct Curl_cfilter *cf = gnutls_session_get_ptr(session); + + (void)msg; + (void)incoming; + if(when) { /* after message has been processed */ + struct Curl_easy *data = CF_DATA_CURRENT(cf); + if(data) { + CURL_TRC_CF(data, cf, "handshake: %s message type %d", + incoming? "incoming" : "outgoing", htype); + switch(htype) { + case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { + gtls_update_session_id(cf, data, session); + break; + } + default: + break; + } + } + } + return 0; +} + +static CURLcode gtls_client_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + struct gtls_ctx *gtls) +{ + struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + unsigned int init_flags; + int rc; + bool sni = TRUE; /* default is SNI enabled */ + const char *prioritylist; + const char *err = NULL; + const char *tls13support; + CURLcode result; + + if(!gtls_inited) + gtls_init(); + + if(config->version == CURL_SSLVERSION_SSLv2) { + failf(data, "GnuTLS does not support SSLv2"); + return CURLE_SSL_CONNECT_ERROR; + } + else if(config->version == CURL_SSLVERSION_SSLv3) + sni = FALSE; /* SSLv3 has no SNI */ + + /* allocate a shared creds struct */ + result = Curl_gtls_shared_creds_create(data, >ls->shared_creds); + if(result) + return result; + +#ifdef USE_GNUTLS_SRP + if(config->username && Curl_auth_allowed_to_host(data)) { + infof(data, "Using TLS-SRP username: %s", config->username); + + rc = gnutls_srp_allocate_client_credentials(>ls->srp_client_cred); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_srp_allocate_client_cred() failed: %s", + gnutls_strerror(rc)); + return CURLE_OUT_OF_MEMORY; + } + + rc = gnutls_srp_set_client_credentials(gtls->srp_client_cred, + config->username, + config->password); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_srp_set_client_cred() failed: %s", + gnutls_strerror(rc)); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + } +#endif + + ssl_config->certverifyresult = 0; + + /* Initialize TLS session as a client */ + init_flags = GNUTLS_CLIENT; + +#if defined(GNUTLS_FORCE_CLIENT_CERT) + init_flags |= GNUTLS_FORCE_CLIENT_CERT; +#endif + +#if defined(GNUTLS_NO_TICKETS) + /* Disable TLS session tickets */ + init_flags |= GNUTLS_NO_TICKETS; +#endif + + rc = gnutls_init(>ls->session, init_flags); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_init() failed: %d", rc); + return CURLE_SSL_CONNECT_ERROR; + } + + if(sni && peer->sni) { + if(gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS, + peer->sni, strlen(peer->sni)) < 0) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + } + + /* Use default priorities */ + rc = gnutls_set_default_priority(gtls->session); + if(rc != GNUTLS_E_SUCCESS) + return CURLE_SSL_CONNECT_ERROR; + + /* "In GnuTLS 3.6.5, TLS 1.3 is enabled by default" */ + tls13support = gnutls_check_version("3.6.5"); + + /* Ensure +SRP comes at the *end* of all relevant strings so that it can be + * removed if a runtime error indicates that SRP is not supported by this + * GnuTLS version */ + + if(config->version == CURL_SSLVERSION_SSLv2 || + config->version == CURL_SSLVERSION_SSLv3) { + failf(data, "GnuTLS does not support SSLv2 or SSLv3"); + return CURLE_SSL_CONNECT_ERROR; + } + + if(config->version == CURL_SSLVERSION_TLSv1_3) { + if(!tls13support) { + failf(data, "This GnuTLS installation does not support TLS 1.3"); + return CURLE_SSL_CONNECT_ERROR; + } + } + + /* At this point we know we have a supported TLS version, so set it */ + result = set_ssl_version_min_max(data, peer, + config, &prioritylist, tls13support); + if(result) + return result; + +#ifdef USE_GNUTLS_SRP + /* Only add SRP to the cipher list if SRP is requested. Otherwise + * GnuTLS will disable TLS 1.3 support. */ + if(config->username) { + char *prioritysrp = aprintf("%s:" GNUTLS_SRP, prioritylist); + if(!prioritysrp) + return CURLE_OUT_OF_MEMORY; + rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err); + free(prioritysrp); + + if((rc == GNUTLS_E_INVALID_REQUEST) && err) { + infof(data, "This GnuTLS does not support SRP"); + } + } + else { +#endif + infof(data, "GnuTLS ciphers: %s", prioritylist); + rc = gnutls_priority_set_direct(gtls->session, prioritylist, &err); +#ifdef USE_GNUTLS_SRP + } +#endif + + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "Error %d setting GnuTLS cipher list starting with %s", + rc, err); + return CURLE_SSL_CONNECT_ERROR; + } + + if(config->clientcert) { + if(!gtls->shared_creds->trust_setup) { + result = Curl_gtls_client_trust_setup(cf, data, gtls); + if(result) + return result; + } + if(ssl_config->key_passwd) { + const unsigned int supported_key_encryption_algorithms = + GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR | + GNUTLS_PKCS_USE_PKCS12_RC2_40 | GNUTLS_PKCS_USE_PBES2_3DES | + GNUTLS_PKCS_USE_PBES2_AES_128 | GNUTLS_PKCS_USE_PBES2_AES_192 | + GNUTLS_PKCS_USE_PBES2_AES_256; + rc = gnutls_certificate_set_x509_key_file2( + gtls->shared_creds->creds, + config->clientcert, + ssl_config->key ? ssl_config->key : config->clientcert, + do_file_type(ssl_config->cert_type), + ssl_config->key_passwd, + supported_key_encryption_algorithms); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, + "error reading X.509 potentially-encrypted key file: %s", + gnutls_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { + if(gnutls_certificate_set_x509_key_file( + gtls->shared_creds->creds, + config->clientcert, + ssl_config->key ? ssl_config->key : config->clientcert, + do_file_type(ssl_config->cert_type) ) != + GNUTLS_E_SUCCESS) { + failf(data, "error reading X.509 key or certificate file"); + return CURLE_SSL_CONNECT_ERROR; + } + } + } + +#ifdef USE_GNUTLS_SRP + /* put the credentials to the current session */ + if(config->username) { + rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_SRP, + gtls->srp_client_cred); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } + } + else +#endif + { + rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_CERTIFICATE, + gtls->shared_creds->creds); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } + } + + if(config->verifystatus) { + rc = gnutls_ocsp_status_request_enable_client(gtls->session, + NULL, 0, NULL); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc); + return CURLE_SSL_CONNECT_ERROR; + } + } + + return CURLE_OK; +} + +static int keylog_callback(gnutls_session_t session, const char *label, + const gnutls_datum_t *secret) +{ + gnutls_datum_t crandom; + gnutls_datum_t srandom; + + gnutls_session_get_random(session, &crandom, &srandom); + if(crandom.size != 32) { + return -1; + } + + Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); + return 0; +} + +CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const unsigned char *alpn, size_t alpn_len, + Curl_gtls_ctx_setup_cb *cb_setup, + void *cb_user_data, + void *ssl_user_data) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + CURLcode result; + + DEBUGASSERT(gctx); + + result = gtls_client_init(cf, data, peer, gctx); + if(result) + return result; + + gnutls_session_set_ptr(gctx->session, ssl_user_data); + + if(cb_setup) { + result = cb_setup(cf, data, cb_user_data); + if(result) + return result; + } + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + gnutls_session_set_keylog_function(gctx->session, keylog_callback); + } + + /* convert the ALPN string from our arguments to a list of strings + * that gnutls wants and will convert internally back to this very + * string for sending to the server. nice. */ + if(alpn && alpn_len) { + gnutls_datum_t alpns[5]; + size_t i, alen = alpn_len; + unsigned char *s = (unsigned char *)alpn; + unsigned char slen; + for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) { + slen = s[0]; + if(slen >= alen) + return CURLE_FAILED_INIT; + alpns[i].data = s + 1; + alpns[i].size = slen; + s += slen + 1; + alen -= (size_t)slen + 1; + } + if(alen) /* not all alpn chars used, wrong format or too many */ + return CURLE_FAILED_INIT; + if(i && gnutls_alpn_set_protocols(gctx->session, + alpns, (unsigned int)i, + GNUTLS_ALPN_MANDATORY)) { + failf(data, "failed setting ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + } + + /* This might be a reconnect, so we check for a session ID in the cache + to speed up things */ + if(conn_config->cache_session) { + void *ssl_sessionid; + size_t ssl_idsize; + + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, &ssl_idsize)) { + /* we got a session id, use it! */ + int rc; + + rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize); + if(rc < 0) + infof(data, "SSL failed to set session ID"); + else + infof(data, "SSL reusing session ID (size=%zu)", ssl_idsize); + } + Curl_ssl_sessionid_unlock(data); + } + return CURLE_OK; +} + +static CURLcode +gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + struct alpn_proto_buf proto; + CURLcode result; + + DEBUGASSERT(backend); + DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); + + if(connssl->state == ssl_connection_complete) + /* to make us tolerant against being called more than once for the + same connection */ + return CURLE_OK; + + memset(&proto, 0, sizeof(proto)); + if(connssl->alpn) { + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result) { + failf(data, "Error determining ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + } + + result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer, + proto.data, proto.len, NULL, NULL, cf); + if(result) + return result; + + gnutls_handshake_set_hook_function(backend->gtls.session, + GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, + gtls_handshake_cb); + + /* register callback functions and handle to send and receive data. */ + gnutls_transport_set_ptr(backend->gtls.session, cf); + gnutls_transport_set_push_function(backend->gtls.session, gtls_push); + gnutls_transport_set_pull_function(backend->gtls.session, gtls_pull); + + return CURLE_OK; +} + +static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, + gnutls_x509_crt_t cert, + const char *pinnedpubkey) +{ + /* Scratch */ + size_t len1 = 0, len2 = 0; + unsigned char *buff1 = NULL; + + gnutls_pubkey_t key = NULL; + + /* Result is returned to caller */ + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + + /* if a path was not specified, do not pin */ + if(!pinnedpubkey) + return CURLE_OK; + + if(!cert) + return result; + + do { + int ret; + + /* Begin Gyrations to get the public key */ + gnutls_pubkey_init(&key); + + ret = gnutls_pubkey_import_x509(key, cert, 0); + if(ret < 0) + break; /* failed */ + + ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, NULL, &len1); + if(ret != GNUTLS_E_SHORT_MEMORY_BUFFER || len1 == 0) + break; /* failed */ + + buff1 = malloc(len1); + if(!buff1) + break; /* failed */ + + len2 = len1; + + ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, buff1, &len2); + if(ret < 0 || len1 != len2) + break; /* failed */ + + /* End Gyrations */ + + /* The one good exit point */ + result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1); + } while(0); + + if(key) + gnutls_pubkey_deinit(key); + + Curl_safefree(buff1); + + return result; +} + +CURLcode +Curl_gtls_verifyserver(struct Curl_easy *data, + gnutls_session_t session, + struct ssl_primary_config *config, + struct ssl_config_data *ssl_config, + struct ssl_peer *peer, + const char *pinned_key) +{ + unsigned int cert_list_size; + const gnutls_datum_t *chainp; + unsigned int verify_status = 0; + gnutls_x509_crt_t x509_cert, x509_issuer; + gnutls_datum_t issuerp; + gnutls_datum_t certfields; + char certname[65] = ""; /* limited to 64 chars by ASN.1 */ + size_t size; + time_t certclock; + int rc; + CURLcode result = CURLE_OK; +#ifndef CURL_DISABLE_VERBOSE_STRINGS + const char *ptr; + int algo; + unsigned int bits; + gnutls_protocol_t version = gnutls_protocol_get_version(session); +#endif + long * const certverifyresult = &ssl_config->certverifyresult; + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */ + ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session), + gnutls_cipher_get(session), + gnutls_mac_get(session)); + + infof(data, "SSL connection using %s / %s", + gnutls_protocol_get_name(version), ptr); +#endif + + /* This function will return the peer's raw certificate (chain) as sent by + the peer. These certificates are in raw format (DER encoded for + X.509). In case of a X.509 then a certificate list may be present. The + first certificate in the list is the peer's certificate, following the + issuer's certificate, then the issuer's issuer etc. */ + + chainp = gnutls_certificate_get_peers(session, &cert_list_size); + if(!chainp) { + if(config->verifypeer || + config->verifyhost || + config->issuercert) { +#ifdef USE_GNUTLS_SRP + if(ssl_config->primary.username && !config->verifypeer && + gnutls_cipher_get(session)) { + /* no peer cert, but auth is ok if we have SRP user and cipher and no + peer verify */ + } + else { +#endif + failf(data, "failed to get server cert"); + *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; + return CURLE_PEER_FAILED_VERIFICATION; +#ifdef USE_GNUTLS_SRP + } +#endif + } + infof(data, " common name: WARNING could not obtain"); + } + + if(data->set.ssl.certinfo && chainp) { + unsigned int i; + + result = Curl_ssl_init_certinfo(data, (int)cert_list_size); + if(result) + return result; + + for(i = 0; i < cert_list_size; i++) { + const char *beg = (const char *) chainp[i].data; + const char *end = beg + chainp[i].size; + + result = Curl_extract_certinfo(data, (int)i, beg, end); + if(result) + return result; + } + } + + if(config->verifypeer) { + /* This function will try to verify the peer's certificate and return its + status (trusted, invalid etc.). The value of status should be one or + more of the gnutls_certificate_status_t enumerated elements bitwise + or'd. To avoid denial of service attacks some default upper limits + regarding the certificate key size and chain size are set. To override + them use gnutls_certificate_set_verify_limits(). */ + + rc = gnutls_certificate_verify_peers2(session, &verify_status); + if(rc < 0) { + failf(data, "server cert verify failed: %d", rc); + *certverifyresult = rc; + return CURLE_SSL_CONNECT_ERROR; + } + + *certverifyresult = verify_status; + + /* verify_status is a bitmask of gnutls_certificate_status bits */ + if(verify_status & GNUTLS_CERT_INVALID) { + if(config->verifypeer) { + failf(data, "server certificate verification failed. CAfile: %s " + "CRLfile: %s", config->CAfile ? config->CAfile: + "none", + ssl_config->primary.CRLfile ? + ssl_config->primary.CRLfile : "none"); + return CURLE_PEER_FAILED_VERIFICATION; + } + else + infof(data, " server certificate verification FAILED"); + } + else + infof(data, " server certificate verification OK"); + } + else + infof(data, " server certificate verification SKIPPED"); + + if(config->verifystatus) { + if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) { + gnutls_datum_t status_request; + gnutls_ocsp_resp_t ocsp_resp; + + gnutls_ocsp_cert_status_t status; + gnutls_x509_crl_reason_t reason; + + rc = gnutls_ocsp_status_request_get(session, &status_request); + + infof(data, " server certificate status verification FAILED"); + + if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + failf(data, "No OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } + + if(rc < 0) { + failf(data, "Invalid OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } + + gnutls_ocsp_resp_init(&ocsp_resp); + + rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request); + if(rc < 0) { + failf(data, "Invalid OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } + + (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, + &status, NULL, NULL, NULL, &reason); + + switch(status) { + case GNUTLS_OCSP_CERT_GOOD: + break; + + case GNUTLS_OCSP_CERT_REVOKED: { + const char *crl_reason; + + switch(reason) { + default: + case GNUTLS_X509_CRLREASON_UNSPECIFIED: + crl_reason = "unspecified reason"; + break; + + case GNUTLS_X509_CRLREASON_KEYCOMPROMISE: + crl_reason = "private key compromised"; + break; + + case GNUTLS_X509_CRLREASON_CACOMPROMISE: + crl_reason = "CA compromised"; + break; + + case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED: + crl_reason = "affiliation has changed"; + break; + + case GNUTLS_X509_CRLREASON_SUPERSEDED: + crl_reason = "certificate superseded"; + break; + + case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION: + crl_reason = "operation has ceased"; + break; + + case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD: + crl_reason = "certificate is on hold"; + break; + + case GNUTLS_X509_CRLREASON_REMOVEFROMCRL: + crl_reason = "will be removed from delta CRL"; + break; + + case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN: + crl_reason = "privilege withdrawn"; + break; + + case GNUTLS_X509_CRLREASON_AACOMPROMISE: + crl_reason = "AA compromised"; + break; + } + + failf(data, "Server certificate was revoked: %s", crl_reason); + break; + } + + default: + case GNUTLS_OCSP_CERT_UNKNOWN: + failf(data, "Server certificate status is unknown"); + break; + } + + gnutls_ocsp_resp_deinit(ocsp_resp); + + return CURLE_SSL_INVALIDCERTSTATUS; + } + else + infof(data, " server certificate status verification OK"); + } + else + infof(data, " server certificate status verification SKIPPED"); + + /* initialize an X.509 certificate structure. */ + gnutls_x509_crt_init(&x509_cert); + + if(chainp) + /* convert the given DER or PEM encoded Certificate to the native + gnutls_x509_crt_t format */ + gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER); + + if(config->issuercert) { + gnutls_x509_crt_init(&x509_issuer); + issuerp = load_file(config->issuercert); + gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM); + rc = (int)gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); + gnutls_x509_crt_deinit(x509_issuer); + unload_file(issuerp); + if(rc <= 0) { + failf(data, "server certificate issuer check failed (IssuerCert: %s)", + config->issuercert?config->issuercert:"none"); + gnutls_x509_crt_deinit(x509_cert); + return CURLE_SSL_ISSUER_ERROR; + } + infof(data, " server certificate issuer check OK (Issuer Cert: %s)", + config->issuercert?config->issuercert:"none"); + } + + size = sizeof(certname); + rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME, + 0, /* the first and only one */ + FALSE, + certname, + &size); + if(rc) { + infof(data, "error fetching CN from cert:%s", + gnutls_strerror(rc)); + } + + /* This function will check if the given certificate's subject matches the + given hostname. This is a basic implementation of the matching described + in RFC2818 (HTTPS), which takes into account wildcards, and the subject + alternative name PKIX extension. Returns non zero on success, and zero on + failure. */ + + /* This function does not handle trailing dots, so if we have an SNI name + use that and fallback to the hostname only if there is no SNI (like for + IP addresses) */ + rc = (int)gnutls_x509_crt_check_hostname(x509_cert, + peer->sni ? peer->sni : + peer->hostname); +#if GNUTLS_VERSION_NUMBER < 0x030306 + /* Before 3.3.6, gnutls_x509_crt_check_hostname() did not check IP + addresses. */ + if(!rc) { +#ifdef USE_IPV6 + #define use_addr in6_addr +#else + #define use_addr in_addr +#endif + unsigned char addrbuf[sizeof(struct use_addr)]; + size_t addrlen = 0; + + if(Curl_inet_pton(AF_INET, peer->hostname, addrbuf) > 0) + addrlen = 4; +#ifdef USE_IPV6 + else if(Curl_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0) + addrlen = 16; +#endif + + if(addrlen) { + unsigned char certaddr[sizeof(struct use_addr)]; + int i; + + for(i = 0; ; i++) { + size_t certaddrlen = sizeof(certaddr); + int ret = gnutls_x509_crt_get_subject_alt_name(x509_cert, i, certaddr, + &certaddrlen, NULL); + /* If this happens, it was not an IP address. */ + if(ret == GNUTLS_E_SHORT_MEMORY_BUFFER) + continue; + if(ret < 0) + break; + if(ret != GNUTLS_SAN_IPADDRESS) + continue; + if(certaddrlen == addrlen && !memcmp(addrbuf, certaddr, addrlen)) { + rc = 1; + break; + } + } + } + } +#endif + if(!rc) { + if(config->verifyhost) { + failf(data, "SSL: certificate subject name (%s) does not match " + "target hostname '%s'", certname, peer->dispname); + gnutls_x509_crt_deinit(x509_cert); + return CURLE_PEER_FAILED_VERIFICATION; + } + else + infof(data, " common name: %s (does not match '%s')", + certname, peer->dispname); + } + else + infof(data, " common name: %s (matched)", certname); + + /* Check for time-based validity */ + certclock = gnutls_x509_crt_get_expiration_time(x509_cert); + + if(certclock == (time_t)-1) { + if(config->verifypeer) { + failf(data, "server cert expiration date verify failed"); + *certverifyresult = GNUTLS_CERT_EXPIRED; + gnutls_x509_crt_deinit(x509_cert); + return CURLE_SSL_CONNECT_ERROR; + } + else + infof(data, " server certificate expiration date verify FAILED"); + } + else { + if(certclock < time(NULL)) { + if(config->verifypeer) { + failf(data, "server certificate expiration date has passed."); + *certverifyresult = GNUTLS_CERT_EXPIRED; + gnutls_x509_crt_deinit(x509_cert); + return CURLE_PEER_FAILED_VERIFICATION; + } + else + infof(data, " server certificate expiration date FAILED"); + } + else + infof(data, " server certificate expiration date OK"); + } + + certclock = gnutls_x509_crt_get_activation_time(x509_cert); + + if(certclock == (time_t)-1) { + if(config->verifypeer) { + failf(data, "server cert activation date verify failed"); + *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED; + gnutls_x509_crt_deinit(x509_cert); + return CURLE_SSL_CONNECT_ERROR; + } + else + infof(data, " server certificate activation date verify FAILED"); + } + else { + if(certclock > time(NULL)) { + if(config->verifypeer) { + failf(data, "server certificate not activated yet."); + *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED; + gnutls_x509_crt_deinit(x509_cert); + return CURLE_PEER_FAILED_VERIFICATION; + } + else + infof(data, " server certificate activation date FAILED"); + } + else + infof(data, " server certificate activation date OK"); + } + + if(pinned_key) { + result = pkp_pin_peer_pubkey(data, x509_cert, pinned_key); + if(result != CURLE_OK) { + failf(data, "SSL: public key does not match pinned public key"); + gnutls_x509_crt_deinit(x509_cert); + return result; + } + } + + /* Show: + + - subject + - start date + - expire date + - common name + - issuer + + */ + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + /* public key algorithm's parameters */ + algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits); + infof(data, " certificate public key: %s", + gnutls_pk_algorithm_get_name((gnutls_pk_algorithm_t)algo)); + + /* version of the X.509 certificate. */ + infof(data, " certificate version: #%d", + gnutls_x509_crt_get_version(x509_cert)); + + + rc = gnutls_x509_crt_get_dn2(x509_cert, &certfields); + if(rc) + infof(data, "Failed to get certificate name"); + else { + infof(data, " subject: %s", certfields.data); + + certclock = gnutls_x509_crt_get_activation_time(x509_cert); + showtime(data, "start date", certclock); + + certclock = gnutls_x509_crt_get_expiration_time(x509_cert); + showtime(data, "expire date", certclock); + + gnutls_free(certfields.data); + } + + rc = gnutls_x509_crt_get_issuer_dn2(x509_cert, &certfields); + if(rc) + infof(data, "Failed to get certificate issuer"); + else { + infof(data, " issuer: %s", certfields.data); + + gnutls_free(certfields.data); + } +#endif + + gnutls_x509_crt_deinit(x509_cert); + + return result; +} + +static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_session_t session) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); +#ifndef CURL_DISABLE_PROXY + const char *pinned_key = Curl_ssl_cf_is_proxy(cf)? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: + data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + const char *pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif + CURLcode result; + + result = Curl_gtls_verifyserver(data, session, conn_config, ssl_config, + &connssl->peer, pinned_key); + if(result) + goto out; + + if(connssl->alpn) { + gnutls_datum_t proto; + int rc; + + rc = gnutls_alpn_get_selected_protocol(session, &proto); + if(rc == 0) + Curl_alpn_set_negotiated(cf, data, proto.data, proto.size); + else + Curl_alpn_set_negotiated(cf, data, NULL, 0); + } + + /* Only on TLSv1.2 or lower do we have the session id now. For + * TLSv1.3 we get it via a SESSION_TICKET message that arrives later. */ + if(gnutls_protocol_get_version(session) < GNUTLS_TLS1_3) + result = gtls_update_session_id(cf, data, session); + +out: + return result; +} + +/* + * This function is called after the TCP connect has completed. Setup the TLS + * layer and do all necessary magic. + */ +/* We use connssl->connecting_state to keep track of the connection status; + there are three states: 'ssl_connect_1' (not started yet or complete), + 'ssl_connect_2' (doing handshake with the server), and + 'ssl_connect_3' (verifying and getting stats). + */ +static CURLcode +gtls_connect_common(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool nonblocking, + bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + CURLcode rc; + CURLcode result = CURLE_OK; + + /* Initiate the connection, if not already done */ + if(ssl_connect_1 == connssl->connecting_state) { + rc = gtls_connect_step1(cf, data); + if(rc) { + result = rc; + goto out; + } + } + + rc = handshake(cf, data, TRUE, nonblocking); + if(rc) { + /* handshake() sets its own error message with failf() */ + result = rc; + goto out; + } + + /* Finish connecting once the handshake is done */ + if(ssl_connect_1 == connssl->connecting_state) { + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + gnutls_session_t session; + DEBUGASSERT(backend); + session = backend->gtls.session; + rc = gtls_verifyserver(cf, data, session); + if(rc) { + result = rc; + goto out; + } + connssl->state = ssl_connection_complete; + } + +out: + *done = ssl_connect_1 == connssl->connecting_state; + + return result; +} + +static CURLcode gtls_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + return gtls_connect_common(cf, data, TRUE, done); +} + +static CURLcode gtls_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode result; + bool done = FALSE; + + result = gtls_connect_common(cf, data, FALSE, &done); + if(result) + return result; + + DEBUGASSERT(done); + + return CURLE_OK; +} + +static bool gtls_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct ssl_connect_data *ctx = cf->ctx; + struct gtls_ssl_backend_data *backend; + + (void)data; + DEBUGASSERT(ctx && ctx->backend); + backend = (struct gtls_ssl_backend_data *)ctx->backend; + if(backend->gtls.session && + 0 != gnutls_record_check_pending(backend->gtls.session)) + return TRUE; + return FALSE; +} + +static ssize_t gtls_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, + size_t len, + CURLcode *curlcode) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + ssize_t rc; + + (void)data; + DEBUGASSERT(backend); + backend->gtls.io_result = CURLE_OK; + rc = gnutls_record_send(backend->gtls.session, mem, len); + + if(rc < 0) { + *curlcode = (rc == GNUTLS_E_AGAIN)? + CURLE_AGAIN : + (backend->gtls.io_result? backend->gtls.io_result : CURLE_SEND_ERROR); + + rc = -1; + } + + return rc; +} + +/* + * This function is called to shut down the SSL layer but keep the + * socket open (CCC - Clear Command Channel) + */ +static CURLcode gtls_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + char buf[1024]; + CURLcode result = CURLE_OK; + ssize_t nread; + size_t i; + + DEBUGASSERT(backend); + if(!backend->gtls.session || cf->shutdown) { + *done = TRUE; + goto out; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + + if(!backend->gtls.sent_shutdown) { + /* do this only once */ + backend->gtls.sent_shutdown = TRUE; + if(send_shutdown) { + int ret = gnutls_bye(backend->gtls.session, GNUTLS_SHUT_RDWR); + if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { + CURL_TRC_CF(data, cf, "SSL shutdown, gnutls_bye EAGAIN"); + connssl->io_need = gnutls_record_get_direction(backend->gtls.session)? + CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV; + result = CURLE_OK; + goto out; + } + if(ret != GNUTLS_E_SUCCESS) { + CURL_TRC_CF(data, cf, "SSL shutdown, gnutls_bye error: '%s'(%d)", + gnutls_strerror((int)ret), (int)ret); + result = CURLE_RECV_ERROR; + goto out; + } + } + } + + /* SSL should now have started the shutdown from our side. Since it + * was not complete, we are lacking the close notify from the server. */ + for(i = 0; i < 10; ++i) { + nread = gnutls_record_recv(backend->gtls.session, buf, sizeof(buf)); + if(nread <= 0) + break; + } + if(nread > 0) { + /* still data coming in? */ + } + else if(nread == 0) { + /* We got the close notify alert and are done. */ + *done = TRUE; + } + else if((nread == GNUTLS_E_AGAIN) || (nread == GNUTLS_E_INTERRUPTED)) { + connssl->io_need = gnutls_record_get_direction(backend->gtls.session)? + CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV; + } + else { + CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)", + gnutls_strerror((int)nread), (int)nread); + result = CURLE_RECV_ERROR; + } + +out: + cf->shutdown = (result || *done); + return result; +} + +static void gtls_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + + (void) data; + DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "close"); + if(backend->gtls.session) { + gnutls_deinit(backend->gtls.session); + backend->gtls.session = NULL; + } + if(backend->gtls.shared_creds) { + Curl_gtls_shared_creds_free(&backend->gtls.shared_creds); + } +#ifdef USE_GNUTLS_SRP + if(backend->gtls.srp_client_cred) { + gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred); + backend->gtls.srp_client_cred = NULL; + } +#endif +} + +static ssize_t gtls_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, + size_t buffersize, + CURLcode *curlcode) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + ssize_t ret; + + (void)data; + DEBUGASSERT(backend); + + backend->gtls.io_result = CURLE_OK; + ret = gnutls_record_recv(backend->gtls.session, buf, buffersize); + if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { + *curlcode = CURLE_AGAIN; + ret = -1; + goto out; + } + + if(ret == GNUTLS_E_REHANDSHAKE) { + /* BLOCKING call, this is bad but a work-around for now. Fixing this "the + proper way" takes a whole lot of work. */ + CURLcode result = handshake(cf, data, FALSE, FALSE); + if(result) + /* handshake() writes error message on its own */ + *curlcode = result; + else + *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */ + ret = -1; + goto out; + } + + if(ret < 0) { + failf(data, "GnuTLS recv error (%d): %s", + + (int)ret, gnutls_strerror((int)ret)); + *curlcode = backend->gtls.io_result? + backend->gtls.io_result : CURLE_RECV_ERROR; + ret = -1; + goto out; + } + +out: + return ret; +} + +static size_t gtls_version(char *buffer, size_t size) +{ + return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL)); +} + +/* data might be NULL! */ +static CURLcode gtls_random(struct Curl_easy *data, + unsigned char *entropy, size_t length) +{ + int rc; + (void)data; + rc = gnutls_rnd(GNUTLS_RND_RANDOM, entropy, length); + return rc?CURLE_FAILED_INIT:CURLE_OK; +} + +static CURLcode gtls_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum, /* output */ + size_t sha256len) +{ + struct sha256_ctx SHA256pw; + sha256_init(&SHA256pw); + sha256_update(&SHA256pw, (unsigned int)tmplen, tmp); + sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum); + return CURLE_OK; +} + +static bool gtls_cert_status_request(void) +{ + return TRUE; +} + +static void *gtls_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) +{ + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + (void)info; + DEBUGASSERT(backend); + return backend->gtls.session; +} + +const struct Curl_ssl Curl_ssl_gnutls = { + { CURLSSLBACKEND_GNUTLS, "gnutls" }, /* info */ + + SSLSUPP_CA_PATH | + SSLSUPP_CERTINFO | + SSLSUPP_PINNEDPUBKEY | + SSLSUPP_HTTPS_PROXY | + SSLSUPP_CA_CACHE, + + sizeof(struct gtls_ssl_backend_data), + + gtls_init, /* init */ + gtls_cleanup, /* cleanup */ + gtls_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + gtls_shutdown, /* shutdown */ + gtls_data_pending, /* data_pending */ + gtls_random, /* random */ + gtls_cert_status_request, /* cert_status_request */ + gtls_connect, /* connect */ + gtls_connect_nonblocking, /* connect_nonblocking */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ + gtls_get_internals, /* get_internals */ + gtls_close, /* close_one */ + Curl_none_close_all, /* close_all */ + Curl_none_set_engine, /* set_engine */ + Curl_none_set_engine_default, /* set_engine_default */ + Curl_none_engines_list, /* engines_list */ + Curl_none_false_start, /* false_start */ + gtls_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ + NULL, /* disassociate_connection */ + gtls_recv, /* recv decrypted data */ + gtls_send, /* send data to encrypt */ +}; + +#endif /* USE_GNUTLS */ diff --git a/third_party/curl/lib/vtls/gtls.h b/third_party/curl/lib/vtls/gtls.h new file mode 100644 index 0000000000..b0ca55bfb7 --- /dev/null +++ b/third_party/curl/lib/vtls/gtls.h @@ -0,0 +1,99 @@ +#ifndef HEADER_CURL_GTLS_H +#define HEADER_CURL_GTLS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include + +#ifdef USE_GNUTLS + +#include +#include "timeval.h" + +#ifdef HAVE_GNUTLS_SRP +/* the function exists */ +#ifdef USE_TLS_SRP +/* the functionality is not disabled */ +#define USE_GNUTLS_SRP +#endif +#endif + +struct Curl_easy; +struct Curl_cfilter; +struct ssl_primary_config; +struct ssl_config_data; +struct ssl_peer; + +struct gtls_shared_creds { + gnutls_certificate_credentials_t creds; + char *CAfile; /* CAfile path used to generate X509 store */ + struct curltime time; /* when the shared creds was created */ + size_t refcount; + BIT(trust_setup); /* x509 anchors + CRLs have been set up */ +}; + +CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data, + struct gtls_shared_creds **pcreds); +CURLcode Curl_gtls_shared_creds_up_ref(struct gtls_shared_creds *creds); +void Curl_gtls_shared_creds_free(struct gtls_shared_creds **pcreds); + +struct gtls_ctx { + gnutls_session_t session; + struct gtls_shared_creds *shared_creds; +#ifdef USE_GNUTLS_SRP + gnutls_srp_client_credentials_t srp_client_cred; +#endif + CURLcode io_result; /* result of last IO cfilter operation */ + BIT(sent_shutdown); +}; + +typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data); + +CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const unsigned char *alpn, size_t alpn_len, + Curl_gtls_ctx_setup_cb *cb_setup, + void *cb_user_data, + void *ssl_user_data); + +CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct gtls_ctx *gtls); + +CURLcode Curl_gtls_verifyserver(struct Curl_easy *data, + gnutls_session_t session, + struct ssl_primary_config *config, + struct ssl_config_data *ssl_config, + struct ssl_peer *peer, + const char *pinned_key); + +extern const struct Curl_ssl Curl_ssl_gnutls; + +#endif /* USE_GNUTLS */ +#endif /* HEADER_CURL_GTLS_H */ diff --git a/third_party/curl/lib/vtls/hostcheck.c b/third_party/curl/lib/vtls/hostcheck.c new file mode 100644 index 0000000000..e46439a5ec --- /dev/null +++ b/third_party/curl/lib/vtls/hostcheck.c @@ -0,0 +1,135 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_OPENSSL) \ + || defined(USE_SCHANNEL) +/* these backends use functions from this file */ + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETINET_IN6_H +#include +#endif +#include "curl_memrchr.h" + +#include "hostcheck.h" +#include "strcase.h" +#include "hostip.h" + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* check the two input strings with given length, but do not + assume they end in nul-bytes */ +static bool pmatch(const char *hostname, size_t hostlen, + const char *pattern, size_t patternlen) +{ + if(hostlen != patternlen) + return FALSE; + return strncasecompare(hostname, pattern, hostlen); +} + +/* + * Match a hostname against a wildcard pattern. + * E.g. + * "foo.host.com" matches "*.host.com". + * + * We use the matching rule described in RFC6125, section 6.4.3. + * https://datatracker.ietf.org/doc/html/rfc6125#section-6.4.3 + * + * In addition: ignore trailing dots in the hostnames and wildcards, so that + * the names are used normalized. This is what the browsers do. + * + * Do not allow wildcard matching on IP numbers. There are apparently + * certificates being used with an IP address in the CN field, thus making no + * apparent distinction between a name and an IP. We need to detect the use of + * an IP address and not wildcard match on such names. + * + * Only match on "*" being used for the leftmost label, not "a*", "a*b" nor + * "*b". + * + * Return TRUE on a match. FALSE if not. + * + * @unittest: 1397 + */ + +static bool hostmatch(const char *hostname, + size_t hostlen, + const char *pattern, + size_t patternlen) +{ + const char *pattern_label_end; + + DEBUGASSERT(pattern); + DEBUGASSERT(patternlen); + DEBUGASSERT(hostname); + DEBUGASSERT(hostlen); + + /* normalize pattern and hostname by stripping off trailing dots */ + if(hostname[hostlen-1]=='.') + hostlen--; + if(pattern[patternlen-1]=='.') + patternlen--; + + if(strncmp(pattern, "*.", 2)) + return pmatch(hostname, hostlen, pattern, patternlen); + + /* detect IP address as hostname and fail the match if so */ + else if(Curl_host_is_ipnum(hostname)) + return FALSE; + + /* We require at least 2 dots in the pattern to avoid too wide wildcard + match. */ + pattern_label_end = memchr(pattern, '.', patternlen); + if(!pattern_label_end || + (memrchr(pattern, '.', patternlen) == pattern_label_end)) + return pmatch(hostname, hostlen, pattern, patternlen); + else { + const char *hostname_label_end = memchr(hostname, '.', hostlen); + if(hostname_label_end) { + size_t skiphost = hostname_label_end - hostname; + size_t skiplen = pattern_label_end - pattern; + return pmatch(hostname_label_end, hostlen - skiphost, + pattern_label_end, patternlen - skiplen); + } + } + return FALSE; +} + +/* + * Curl_cert_hostcheck() returns TRUE if a match and FALSE if not. + */ +bool Curl_cert_hostcheck(const char *match, size_t matchlen, + const char *hostname, size_t hostlen) +{ + if(match && *match && hostname && *hostname) + return hostmatch(hostname, hostlen, match, matchlen); + return FALSE; +} + +#endif /* OPENSSL or SCHANNEL */ diff --git a/third_party/curl/lib/vtls/hostcheck.h b/third_party/curl/lib/vtls/hostcheck.h new file mode 100644 index 0000000000..6b4e379644 --- /dev/null +++ b/third_party/curl/lib/vtls/hostcheck.h @@ -0,0 +1,33 @@ +#ifndef HEADER_CURL_HOSTCHECK_H +#define HEADER_CURL_HOSTCHECK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include + +/* returns TRUE if there is a match */ +bool Curl_cert_hostcheck(const char *match_pattern, size_t matchlen, + const char *hostname, size_t hostlen); + +#endif /* HEADER_CURL_HOSTCHECK_H */ diff --git a/third_party/curl/lib/vtls/keylog.c b/third_party/curl/lib/vtls/keylog.c new file mode 100644 index 0000000000..ab7baaaeca --- /dev/null +++ b/third_party/curl/lib/vtls/keylog.c @@ -0,0 +1,167 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if defined(USE_OPENSSL) || \ + defined(USE_GNUTLS) || \ + defined(USE_WOLFSSL) || \ + (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ + defined(USE_QUICHE) + +#include "keylog.h" +#include + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1) + +#define CLIENT_RANDOM_SIZE 32 + +/* + * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the + * secret size depends on the cipher suite's hash function which is 32 bytes + * for SHA-256 and 48 bytes for SHA-384. + */ +#define SECRET_MAXLEN 48 + + +/* The fp for the open SSLKEYLOGFILE, or NULL if not open */ +static FILE *keylog_file_fp; + +void +Curl_tls_keylog_open(void) +{ + char *keylog_file_name; + + if(!keylog_file_fp) { + keylog_file_name = curl_getenv("SSLKEYLOGFILE"); + if(keylog_file_name) { + keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT); + if(keylog_file_fp) { +#ifdef _WIN32 + if(setvbuf(keylog_file_fp, NULL, _IONBF, 0)) +#else + if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) +#endif + { + fclose(keylog_file_fp); + keylog_file_fp = NULL; + } + } + Curl_safefree(keylog_file_name); + } + } +} + +void +Curl_tls_keylog_close(void) +{ + if(keylog_file_fp) { + fclose(keylog_file_fp); + keylog_file_fp = NULL; + } +} + +bool +Curl_tls_keylog_enabled(void) +{ + return keylog_file_fp != NULL; +} + +bool +Curl_tls_keylog_write_line(const char *line) +{ + /* The current maximum valid keylog line length LF and NUL is 195. */ + size_t linelen; + char buf[256]; + + if(!keylog_file_fp || !line) { + return false; + } + + linelen = strlen(line); + if(linelen == 0 || linelen > sizeof(buf) - 2) { + /* Empty line or too big to fit in a LF and NUL. */ + return false; + } + + memcpy(buf, line, linelen); + if(line[linelen - 1] != '\n') { + buf[linelen++] = '\n'; + } + buf[linelen] = '\0'; + + /* Using fputs here instead of fprintf since libcurl's fprintf replacement + may not be thread-safe. */ + fputs(buf, keylog_file_fp); + return true; +} + +bool +Curl_tls_keylog_write(const char *label, + const unsigned char client_random[CLIENT_RANDOM_SIZE], + const unsigned char *secret, size_t secretlen) +{ + const char *hex = "0123456789ABCDEF"; + size_t pos, i; + char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 + + 2 * SECRET_MAXLEN + 1 + 1]; + + if(!keylog_file_fp) { + return false; + } + + pos = strlen(label); + if(pos > KEYLOG_LABEL_MAXLEN || !secretlen || secretlen > SECRET_MAXLEN) { + /* Should never happen - sanity check anyway. */ + return false; + } + + memcpy(line, label, pos); + line[pos++] = ' '; + + /* Client Random */ + for(i = 0; i < CLIENT_RANDOM_SIZE; i++) { + line[pos++] = hex[client_random[i] >> 4]; + line[pos++] = hex[client_random[i] & 0xF]; + } + line[pos++] = ' '; + + /* Secret */ + for(i = 0; i < secretlen; i++) { + line[pos++] = hex[secret[i] >> 4]; + line[pos++] = hex[secret[i] & 0xF]; + } + line[pos++] = '\n'; + line[pos] = '\0'; + + /* Using fputs here instead of fprintf since libcurl's fprintf replacement + may not be thread-safe. */ + fputs(line, keylog_file_fp); + return true; +} + +#endif /* TLS or QUIC backend */ diff --git a/third_party/curl/lib/vtls/keylog.h b/third_party/curl/lib/vtls/keylog.h new file mode 100644 index 0000000000..eff5bf38f3 --- /dev/null +++ b/third_party/curl/lib/vtls/keylog.h @@ -0,0 +1,58 @@ +#ifndef HEADER_CURL_KEYLOG_H +#define HEADER_CURL_KEYLOG_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +/* + * Opens the TLS key log file if requested by the user. The SSLKEYLOGFILE + * environment variable specifies the output file. + */ +void Curl_tls_keylog_open(void); + +/* + * Closes the TLS key log file if not already. + */ +void Curl_tls_keylog_close(void); + +/* + * Returns true if the user successfully enabled the TLS key log file. + */ +bool Curl_tls_keylog_enabled(void); + +/* + * Appends a key log file entry. + * Returns true iff the key log file is open and a valid entry was provided. + */ +bool Curl_tls_keylog_write(const char *label, + const unsigned char client_random[32], + const unsigned char *secret, size_t secretlen); + +/* + * Appends a line to the key log file, ensure it is terminated by a LF. + * Returns true iff the key log file is open and a valid line was provided. + */ +bool Curl_tls_keylog_write_line(const char *line); + +#endif /* HEADER_CURL_KEYLOG_H */ diff --git a/third_party/curl/lib/vtls/mbedtls.c b/third_party/curl/lib/vtls/mbedtls.c new file mode 100644 index 0000000000..2b6042df32 --- /dev/null +++ b/third_party/curl/lib/vtls/mbedtls.c @@ -0,0 +1,1700 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Hoi-Ho Chan, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Source file for all mbedTLS-specific code for the TLS/SSL layer. No code + * but vtls.c should ever call or use these functions. + * + */ + +#include "curl_setup.h" + +#ifdef USE_MBEDTLS + +/* Define this to enable lots of debugging for mbedTLS */ +/* #define MBEDTLS_DEBUG */ + +#ifdef __GNUC__ +#pragma GCC diagnostic push +/* mbedTLS (as of v3.5.1) has a duplicate function declaration + in its public headers. Disable the warning that detects it. */ +#pragma GCC diagnostic ignored "-Wredundant-decls" +#endif + +#include +#if MBEDTLS_VERSION_NUMBER >= 0x02040000 +#include +#else +#include +#endif +#include +#include + +#include +#include +#include +#include + +#if MBEDTLS_VERSION_MAJOR >= 2 +# ifdef MBEDTLS_DEBUG +# include +# endif +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#include "cipher_suite.h" +#include "strcase.h" +#include "urldata.h" +#include "sendf.h" +#include "inet_pton.h" +#include "mbedtls.h" +#include "vtls.h" +#include "vtls_int.h" +#include "x509asn1.h" +#include "parsedate.h" +#include "connect.h" /* for the connect timeout */ +#include "select.h" +#include "multiif.h" +#include "mbedtls_threadlock.h" +#include "strdup.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* ALPN for http2 */ +#ifdef USE_HTTP2 +# undef HAS_ALPN +# ifdef MBEDTLS_SSL_ALPN +# define HAS_ALPN +# endif +#endif + +struct mbed_ssl_backend_data { + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context entropy; + mbedtls_ssl_context ssl; + mbedtls_x509_crt cacert; + mbedtls_x509_crt clicert; +#ifdef MBEDTLS_X509_CRL_PARSE_C + mbedtls_x509_crl crl; +#endif + mbedtls_pk_context pk; + mbedtls_ssl_config config; +#ifdef HAS_ALPN + const char *protocols[3]; +#endif + int *ciphersuites; + BIT(initialized); /* mbedtls_ssl_context is initialized */ + BIT(sent_shutdown); +}; + +/* apply threading? */ +#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ + defined(_WIN32) +#define THREADING_SUPPORT +#endif + +#ifndef MBEDTLS_ERROR_C +#define mbedtls_strerror(a,b,c) b[0] = 0 +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && MBEDTLS_VERSION_NUMBER >= 0x03060000 +#define TLS13_SUPPORT +#endif + +#if defined(THREADING_SUPPORT) +static mbedtls_entropy_context ts_entropy; + +static int entropy_init_initialized = 0; + +static void entropy_init_mutex(mbedtls_entropy_context *ctx) +{ + /* lock 0 = entropy_init_mutex() */ + Curl_mbedtlsthreadlock_lock_function(0); + if(entropy_init_initialized == 0) { + mbedtls_entropy_init(ctx); + entropy_init_initialized = 1; + } + Curl_mbedtlsthreadlock_unlock_function(0); +} + +static void entropy_cleanup_mutex(mbedtls_entropy_context *ctx) +{ + /* lock 0 = use same lock as init */ + Curl_mbedtlsthreadlock_lock_function(0); + if(entropy_init_initialized == 1) { + mbedtls_entropy_free(ctx); + entropy_init_initialized = 0; + } + Curl_mbedtlsthreadlock_unlock_function(0); +} + +static int entropy_func_mutex(void *data, unsigned char *output, size_t len) +{ + int ret; + /* lock 1 = entropy_func_mutex() */ + Curl_mbedtlsthreadlock_lock_function(1); + ret = mbedtls_entropy_func(data, output, len); + Curl_mbedtlsthreadlock_unlock_function(1); + + return ret; +} + +#endif /* THREADING_SUPPORT */ + +#ifdef MBEDTLS_DEBUG +static void mbed_debug(void *context, int level, const char *f_name, + int line_nb, const char *line) +{ + struct Curl_easy *data = (struct Curl_easy *)context; + (void) level; + (void) line_nb; + (void) f_name; + + if(data) { + size_t len = strlen(line); + if(len && (line[len - 1] == '\n')) + /* discount any trailing newline */ + len--; + infof(data, "%.*s", (int)len, line); + } +} +#endif + +static int mbedtls_bio_cf_write(void *bio, + const unsigned char *buf, size_t blen) +{ + struct Curl_cfilter *cf = bio; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nwritten; + CURLcode result; + + DEBUGASSERT(data); + if(!data) + return 0; + + nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result); + CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d", + blen, nwritten, result); + if(nwritten < 0 && CURLE_AGAIN == result) { + nwritten = MBEDTLS_ERR_SSL_WANT_WRITE; + } + return (int)nwritten; +} + +static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) +{ + struct Curl_cfilter *cf = bio; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nread; + CURLcode result; + + DEBUGASSERT(data); + if(!data) + return 0; + /* OpenSSL catches this case, so should we. */ + if(!buf) + return 0; + + nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result); + CURL_TRC_CF(data, cf, "mbedtls_bio_cf_in_read(len=%zu) -> %zd, err=%d", + blen, nread, result); + if(nread < 0 && CURLE_AGAIN == result) { + nread = MBEDTLS_ERR_SSL_WANT_READ; + } + return (int)nread; +} + +/* + * profile + */ +static const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr = +{ + /* Hashes from SHA-1 and above */ + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_RIPEMD160) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA224) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA384) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA512), + 0xFFFFFFF, /* Any PK alg */ + 0xFFFFFFF, /* Any curve */ + 1024, /* RSA min key len */ +}; + +/* See https://web.archive.org/web/20200921194007/tls.mbed.org/discussions/ + generic/howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der +*/ +#define RSA_PUB_DER_MAX_BYTES (38 + 2 * MBEDTLS_MPI_MAX_SIZE) +#define ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES) + +#define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \ + RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES) + +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 +static CURLcode mbedtls_version_from_curl( + mbedtls_ssl_protocol_version* mbedver, long version) +{ + switch(version) { + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + *mbedver = MBEDTLS_SSL_VERSION_TLS1_2; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_3: +#ifdef TLS13_SUPPORT + *mbedver = MBEDTLS_SSL_VERSION_TLS1_3; + return CURLE_OK; +#else + break; +#endif + } + + return CURLE_SSL_CONNECT_ERROR; +} +#else +static CURLcode mbedtls_version_from_curl(int *mbedver, long version) +{ +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + switch(version) { + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + *mbedver = MBEDTLS_SSL_MINOR_VERSION_3; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_3: + break; + } +#else + switch(version) { + case CURL_SSLVERSION_TLSv1_0: + *mbedver = MBEDTLS_SSL_MINOR_VERSION_1; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_1: + *mbedver = MBEDTLS_SSL_MINOR_VERSION_2; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_2: + *mbedver = MBEDTLS_SSL_MINOR_VERSION_3; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_3: + break; + } +#endif + + return CURLE_SSL_CONNECT_ERROR; +} +#endif + +static CURLcode +set_ssl_version_min_max(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + mbedtls_ssl_protocol_version mbedtls_ver_min = MBEDTLS_SSL_VERSION_TLS1_2; +#ifdef TLS13_SUPPORT + mbedtls_ssl_protocol_version mbedtls_ver_max = MBEDTLS_SSL_VERSION_TLS1_3; +#else + mbedtls_ssl_protocol_version mbedtls_ver_max = MBEDTLS_SSL_VERSION_TLS1_2; +#endif +#elif MBEDTLS_VERSION_NUMBER >= 0x03000000 + int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_3; + int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_3; +#else + int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_1; + int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_1; +#endif + long ssl_version = conn_config->version; + long ssl_version_max = conn_config->version_max; + CURLcode result = CURLE_OK; + + DEBUGASSERT(backend); + + switch(ssl_version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + ssl_version = CURL_SSLVERSION_TLSv1_0; + break; + } + + switch(ssl_version_max) { + case CURL_SSLVERSION_MAX_NONE: + case CURL_SSLVERSION_MAX_DEFAULT: +#ifdef TLS13_SUPPORT + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; +#else + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; +#endif + break; + } + + result = mbedtls_version_from_curl(&mbedtls_ver_min, ssl_version); + if(result) { + failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); + return result; + } + result = mbedtls_version_from_curl(&mbedtls_ver_max, ssl_version_max >> 16); + if(result) { + failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); + return result; + } + +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + mbedtls_ssl_conf_min_tls_version(&backend->config, mbedtls_ver_min); + mbedtls_ssl_conf_max_tls_version(&backend->config, mbedtls_ver_max); +#else + mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, + mbedtls_ver_min); + mbedtls_ssl_conf_max_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, + mbedtls_ver_max); +#endif + +#ifdef TLS13_SUPPORT + if(mbedtls_ver_min == MBEDTLS_SSL_VERSION_TLS1_3) { + mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_REQUIRED); + } + else { + mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL); + } +#else + mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL); +#endif + + return result; +} + +/* TLS_ECJPAKE_WITH_AES_128_CCM_8 (0xC0FF) is marked experimental + in mbedTLS. The number is not reserved by IANA nor is the + cipher suite present in other SSL implementations. Provide + provisional support for specifying the cipher suite here. */ +#ifdef MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 +static int +mbed_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, + bool prefer_rfc) +{ + if(id == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8) + msnprintf(buf, buf_size, "%s", "TLS_ECJPAKE_WITH_AES_128_CCM_8"); + else + return Curl_cipher_suite_get_str(id, buf, buf_size, prefer_rfc); + return 0; +} + +static uint16_t +mbed_cipher_suite_walk_str(const char **str, const char **end) +{ + uint16_t id = Curl_cipher_suite_walk_str(str, end); + size_t len = *end - *str; + + if(!id) { + if(strncasecompare("TLS_ECJPAKE_WITH_AES_128_CCM_8", *str, len)) + id = MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8; + } + return id; +} +#else +#define mbed_cipher_suite_get_str Curl_cipher_suite_get_str +#define mbed_cipher_suite_walk_str Curl_cipher_suite_walk_str +#endif + +static CURLcode +mbed_set_selected_ciphers(struct Curl_easy *data, + struct mbed_ssl_backend_data *backend, + const char *ciphers) +{ + const int *supported; + int *selected; + size_t supported_len, count = 0, i; + const char *ptr, *end; + + supported = mbedtls_ssl_list_ciphersuites(); + for(i = 0; supported[i] != 0; i++); + supported_len = i; + + selected = malloc(sizeof(int) * (supported_len + 1)); + if(!selected) + return CURLE_OUT_OF_MEMORY; + + for(ptr = ciphers; ptr[0] != '\0' && count < supported_len; ptr = end) { + uint16_t id = mbed_cipher_suite_walk_str(&ptr, &end); + + /* Check if cipher is supported */ + if(id) { + for(i = 0; i < supported_len && supported[i] != id; i++); + if(i == supported_len) + id = 0; + } + if(!id) { + if(ptr[0] != '\0') + infof(data, "mbedTLS: unknown cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); + continue; + } + + /* No duplicates allowed (so selected cannot overflow) */ + for(i = 0; i < count && selected[i] != id; i++); + if(i < count) { + infof(data, "mbedTLS: duplicate cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); + continue; + } + + selected[count++] = id; + } + + selected[count] = 0; + + if(count == 0) { + free(selected); + failf(data, "mbedTLS: no supported cipher in list"); + return CURLE_SSL_CIPHER; + } + + /* mbedtls_ssl_conf_ciphersuites(): The ciphersuites array is not copied. + It must remain valid for the lifetime of the SSL configuration */ + backend->ciphersuites = selected; + mbedtls_ssl_conf_ciphersuites(&backend->config, backend->ciphersuites); + return CURLE_OK; +} + +#ifdef TLS13_SUPPORT +static int mbed_no_verify(void *udata, mbedtls_x509_crt *crt, + int depth, uint32_t *flags) +{ + (void)udata; + (void)crt; + (void)depth; + /* we clear any faults the mbedtls' own verification found. + * See */ + *flags = 0; + return 0; +} +#endif + +static CURLcode +mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : conn_config->CAfile); + const bool verifypeer = conn_config->verifypeer; + const char * const ssl_capath = conn_config->CApath; + char * const ssl_cert = ssl_config->primary.clientcert; + const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; + const char * const ssl_crlfile = ssl_config->primary.CRLfile; + const char *hostname = connssl->peer.hostname; + int ret = -1; + char errorbuf[128]; + + DEBUGASSERT(backend); + DEBUGASSERT(!backend->initialized); + + if((conn_config->version == CURL_SSLVERSION_SSLv2) || + (conn_config->version == CURL_SSLVERSION_SSLv3)) { + failf(data, "Not supported SSL version"); + return CURLE_NOT_BUILT_IN; + } + +#ifdef TLS13_SUPPORT + ret = psa_crypto_init(); + if(ret != PSA_SUCCESS) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "mbedTLS psa_crypto_init returned (-0x%04X) %s", + -ret, errorbuf); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* TLS13_SUPPORT */ + +#ifdef THREADING_SUPPORT + mbedtls_ctr_drbg_init(&backend->ctr_drbg); + + ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex, + &ts_entropy, NULL, 0); + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", + -ret, errorbuf); + return CURLE_FAILED_INIT; + } +#else + mbedtls_entropy_init(&backend->entropy); + mbedtls_ctr_drbg_init(&backend->ctr_drbg); + + ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, mbedtls_entropy_func, + &backend->entropy, NULL, 0); + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", + -ret, errorbuf); + return CURLE_FAILED_INIT; + } +#endif /* THREADING_SUPPORT */ + + /* Load the trusted CA */ + mbedtls_x509_crt_init(&backend->cacert); + + if(ca_info_blob && verifypeer) { + /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null + terminated even when provided the exact length, forcing us to waste + extra memory here. */ + unsigned char *newblob = Curl_memdup0(ca_info_blob->data, + ca_info_blob->len); + if(!newblob) + return CURLE_OUT_OF_MEMORY; + ret = mbedtls_x509_crt_parse(&backend->cacert, newblob, + ca_info_blob->len + 1); + free(newblob); + if(ret<0) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s", + -ret, errorbuf); + return CURLE_SSL_CERTPROBLEM; + } + } + + if(ssl_cafile && verifypeer) { +#ifdef MBEDTLS_FS_IO + ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile); + + if(ret<0) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s", + ssl_cafile, -ret, errorbuf); + return CURLE_SSL_CACERT_BADFILE; + } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif + } + + if(ssl_capath) { +#ifdef MBEDTLS_FS_IO + ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath); + + if(ret<0) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s", + ssl_capath, -ret, errorbuf); + + if(verifypeer) + return CURLE_SSL_CACERT_BADFILE; + } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif + } + + /* Load the client certificate */ + mbedtls_x509_crt_init(&backend->clicert); + + if(ssl_cert) { +#ifdef MBEDTLS_FS_IO + ret = mbedtls_x509_crt_parse_file(&backend->clicert, ssl_cert); + + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "Error reading client cert file %s - mbedTLS: (-0x%04X) %s", + ssl_cert, -ret, errorbuf); + + return CURLE_SSL_CERTPROBLEM; + } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif + } + + if(ssl_cert_blob) { + /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null + terminated even when provided the exact length, forcing us to waste + extra memory here. */ + unsigned char *newblob = Curl_memdup0(ssl_cert_blob->data, + ssl_cert_blob->len); + if(!newblob) + return CURLE_OUT_OF_MEMORY; + ret = mbedtls_x509_crt_parse(&backend->clicert, newblob, + ssl_cert_blob->len + 1); + free(newblob); + + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "Error reading client cert data %s - mbedTLS: (-0x%04X) %s", + ssl_config->key, -ret, errorbuf); + return CURLE_SSL_CERTPROBLEM; + } + } + + /* Load the client private key */ + mbedtls_pk_init(&backend->pk); + + if(ssl_config->key || ssl_config->key_blob) { + if(ssl_config->key) { +#ifdef MBEDTLS_FS_IO +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, + ssl_config->key_passwd, + mbedtls_ctr_drbg_random, + &backend->ctr_drbg); +#else + ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, + ssl_config->key_passwd); +#endif + + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s", + ssl_config->key, -ret, errorbuf); + return CURLE_SSL_CERTPROBLEM; + } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif + } + else { + const struct curl_blob *ssl_key_blob = ssl_config->key_blob; + const unsigned char *key_data = + (const unsigned char *)ssl_key_blob->data; + const char *passwd = ssl_config->key_passwd; +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, + (const unsigned char *)passwd, + passwd ? strlen(passwd) : 0, + mbedtls_ctr_drbg_random, + &backend->ctr_drbg); +#else + ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, + (const unsigned char *)passwd, + passwd ? strlen(passwd) : 0); +#endif + + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "Error parsing private key - mbedTLS: (-0x%04X) %s", + -ret, errorbuf); + return CURLE_SSL_CERTPROBLEM; + } + } + + if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) || + mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY))) + ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; + } + + /* Load the CRL */ +#ifdef MBEDTLS_X509_CRL_PARSE_C + mbedtls_x509_crl_init(&backend->crl); + + if(ssl_crlfile) { +#ifdef MBEDTLS_FS_IO + ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile); + + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "Error reading CRL file %s - mbedTLS: (-0x%04X) %s", + ssl_crlfile, -ret, errorbuf); + + return CURLE_SSL_CRL_BADFILE; + } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif + } +#else + if(ssl_crlfile) { + failf(data, "mbedtls: crl support not built in"); + return CURLE_NOT_BUILT_IN; + } +#endif + + infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->peer.port); + + mbedtls_ssl_config_init(&backend->config); + ret = mbedtls_ssl_config_defaults(&backend->config, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if(ret) { + failf(data, "mbedTLS: ssl_config failed"); + return CURLE_SSL_CONNECT_ERROR; + } +#ifdef TLS13_SUPPORT + if(!verifypeer) { + /* Default verify behaviour changed in mbedtls v3.6.0 with TLS v1.3. + * On 1.3 connections, the handshake fails by default without trust + * anchors. We override this questionable change by installing our + * own verify callback that clears all errors. */ + mbedtls_ssl_conf_verify(&backend->config, mbed_no_verify, cf); + } +#endif + + + mbedtls_ssl_init(&backend->ssl); + backend->initialized = TRUE; + + /* new profile with RSA min key len = 1024 ... */ + mbedtls_ssl_conf_cert_profile(&backend->config, + &mbedtls_x509_crt_profile_fr); + + switch(conn_config->version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: +#if MBEDTLS_VERSION_NUMBER < 0x03000000 + mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, + MBEDTLS_SSL_MINOR_VERSION_1); + infof(data, "mbedTLS: Set min SSL version to TLS 1.0"); + break; +#endif + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + case CURL_SSLVERSION_TLSv1_3: + { + CURLcode result = set_ssl_version_min_max(cf, data); + if(result != CURLE_OK) + return result; + break; + } + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + + mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random, + &backend->ctr_drbg); + + ret = mbedtls_ssl_setup(&backend->ssl, &backend->config); + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "ssl_setup failed - mbedTLS: (-0x%04X) %s", + -ret, errorbuf); + return CURLE_SSL_CONNECT_ERROR; + } + + mbedtls_ssl_set_bio(&backend->ssl, cf, + mbedtls_bio_cf_write, + mbedtls_bio_cf_read, + NULL /* rev_timeout() */); + + if(conn_config->cipher_list) { + CURLcode result = mbed_set_selected_ciphers(data, backend, + conn_config->cipher_list); + if(result != CURLE_OK) { + failf(data, "mbedTLS: failed to set cipher suites"); + return result; + } + } + else { + mbedtls_ssl_conf_ciphersuites(&backend->config, + mbedtls_ssl_list_ciphersuites()); + } + + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + mbedtls_ssl_conf_renegotiation(&backend->config, + MBEDTLS_SSL_RENEGOTIATION_ENABLED); +#endif + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + mbedtls_ssl_conf_session_tickets(&backend->config, + MBEDTLS_SSL_SESSION_TICKETS_DISABLED); +#endif + + /* Check if there is a cached ID we can/should use here! */ + if(ssl_config->primary.cache_session) { + void *old_session = NULL; + + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &old_session, NULL)) { + ret = mbedtls_ssl_set_session(&backend->ssl, old_session); + if(ret) { + Curl_ssl_sessionid_unlock(data); + failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret); + return CURLE_SSL_CONNECT_ERROR; + } + infof(data, "mbedTLS reusing session"); + } + Curl_ssl_sessionid_unlock(data); + } + + mbedtls_ssl_conf_ca_chain(&backend->config, + &backend->cacert, +#ifdef MBEDTLS_X509_CRL_PARSE_C + &backend->crl); +#else + NULL); +#endif + + if(ssl_config->key || ssl_config->key_blob) { + mbedtls_ssl_conf_own_cert(&backend->config, + &backend->clicert, &backend->pk); + } + + if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni? + connssl->peer.sni : connssl->peer.hostname)) { + /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and + the name to set in the SNI extension. So even if curl connects to a + host specified as an IP address, this function must be used. */ + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + +#ifdef HAS_ALPN + if(connssl->alpn) { + struct alpn_proto_buf proto; + size_t i; + + for(i = 0; i < connssl->alpn->count; ++i) { + backend->protocols[i] = connssl->alpn->entries[i]; + } + /* this function does not clone the protocols array, which is why we need + to keep it around */ + if(mbedtls_ssl_conf_alpn_protocols(&backend->config, + &backend->protocols[0])) { + failf(data, "Failed setting ALPN protocols"); + return CURLE_SSL_CONNECT_ERROR; + } + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } +#endif + +#ifdef MBEDTLS_DEBUG + /* In order to make that work in mbedtls MBEDTLS_DEBUG_C must be defined. */ + mbedtls_ssl_conf_dbg(&backend->config, mbed_debug, data); + /* - 0 No debug + * - 1 Error + * - 2 State change + * - 3 Informational + * - 4 Verbose + */ + mbedtls_debug_set_threshold(4); +#endif + + /* give application a chance to interfere with mbedTLS set up. */ + if(data->set.ssl.fsslctx) { + CURLcode result = (*data->set.ssl.fsslctx)(data, &backend->config, + data->set.ssl.fsslctxp); + if(result != CURLE_OK) { + failf(data, "error signaled by ssl ctx callback"); + return result; + } + } + + connssl->connecting_state = ssl_connect_2; + + return CURLE_OK; +} + +static int count_server_cert(const mbedtls_x509_crt *peercert) +{ + int count = 1; + + DEBUGASSERT(peercert); + + while(peercert->next) { + ++count; + peercert = peercert->next; + } + return count; +} + +static CURLcode collect_server_cert_single(struct Curl_easy *data, + const mbedtls_x509_crt *server_cert, + int idx) +{ + const char *beg, *end; + + DEBUGASSERT(server_cert); + + beg = (const char *)server_cert->raw.p; + end = beg + server_cert->raw.len; + return Curl_extract_certinfo(data, idx, beg, end); +} + +static CURLcode collect_server_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct mbedtls_x509_crt *peercert) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + const bool show_verbose_server_cert = data->set.verbose; +#else + const bool show_verbose_server_cert = false; +#endif + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_PEER_FAILED_VERIFICATION; + int i, count; + + if(!show_verbose_server_cert && !ssl_config->certinfo) + return CURLE_OK; + + if(!peercert) + return result; + + count = count_server_cert(peercert); + result = Curl_ssl_init_certinfo(data, count); + for(i = 0 ; !result && peercert ; i++) { + result = collect_server_cert_single(data, peercert, i); + peercert = peercert->next; + } + return result; +} + +static CURLcode +mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + int ret; + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + const mbedtls_x509_crt *peercert; +#ifndef CURL_DISABLE_PROXY + const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: + data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif + + DEBUGASSERT(backend); + + ret = mbedtls_ssl_handshake(&backend->ssl); + + if(ret == MBEDTLS_ERR_SSL_WANT_READ) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + return CURLE_OK; + } + else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + connssl->io_need = CURL_SSL_IO_NEED_SEND; + return CURLE_OK; + } + else if(ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) { + failf(data, "peer certificate could not be verified"); + return CURLE_PEER_FAILED_VERIFICATION; + } + else if(ret) { + char errorbuf[128]; +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + CURL_TRC_CF(data, cf, "TLS version %04X", + mbedtls_ssl_get_version_number(&backend->ssl)); +#endif + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "ssl_handshake returned: (-0x%04X) %s", + -ret, errorbuf); + return CURLE_SSL_CONNECT_ERROR; + } + +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + { + char cipher_str[64]; + uint16_t cipher_id; + cipher_id = (uint16_t) + mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl); + mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), true); + infof(data, "mbedTLS: Handshake complete, cipher is %s", cipher_str); + } +#else + infof(data, "mbedTLS: Handshake complete"); +#endif + ret = mbedtls_ssl_get_verify_result(&backend->ssl); + + if(!conn_config->verifyhost) + /* Ignore hostname errors if verifyhost is disabled */ + ret &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; + + if(ret && conn_config->verifypeer) { + if(ret & MBEDTLS_X509_BADCERT_EXPIRED) + failf(data, "Cert verify failed: BADCERT_EXPIRED"); + + else if(ret & MBEDTLS_X509_BADCERT_REVOKED) + failf(data, "Cert verify failed: BADCERT_REVOKED"); + + else if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH) + failf(data, "Cert verify failed: BADCERT_CN_MISMATCH"); + + else if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED) + failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED"); + + else if(ret & MBEDTLS_X509_BADCERT_FUTURE) + failf(data, "Cert verify failed: BADCERT_FUTURE"); + + return CURLE_PEER_FAILED_VERIFICATION; + } + + peercert = mbedtls_ssl_get_peer_cert(&backend->ssl); + + if(peercert) { + const CURLcode result = collect_server_cert(cf, data, peercert); + if(result) + return result; + } + + if(peercert && data->set.verbose) { +#ifndef MBEDTLS_X509_REMOVE_INFO + const size_t bufsize = 16384; + char *buffer = malloc(bufsize); + + if(!buffer) + return CURLE_OUT_OF_MEMORY; + + if(mbedtls_x509_crt_info(buffer, bufsize, "* ", peercert) > 0) + infof(data, "Dumping cert info: %s", buffer); + else + infof(data, "Unable to dump certificate information"); + + free(buffer); +#else + infof(data, "Unable to dump certificate information"); +#endif + } + + if(pinnedpubkey) { + int size; + CURLcode result; + mbedtls_x509_crt *p = NULL; + unsigned char *pubkey = NULL; + +#if MBEDTLS_VERSION_NUMBER == 0x03000000 + if(!peercert || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p) || + !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) { +#else + if(!peercert || !peercert->raw.p || !peercert->raw.len) { +#endif + failf(data, "Failed due to missing peer certificate"); + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + } + + p = calloc(1, sizeof(*p)); + + if(!p) + return CURLE_OUT_OF_MEMORY; + + pubkey = malloc(PUB_DER_MAX_BYTES); + + if(!pubkey) { + result = CURLE_OUT_OF_MEMORY; + goto pinnedpubkey_error; + } + + mbedtls_x509_crt_init(p); + + /* Make a copy of our const peercert because mbedtls_pk_write_pubkey_der + needs a non-const key, for now. + https://github.com/ARMmbed/mbedtls/issues/396 */ +#if MBEDTLS_VERSION_NUMBER == 0x03000000 + if(mbedtls_x509_crt_parse_der(p, + peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p), + peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len))) { +#else + if(mbedtls_x509_crt_parse_der(p, peercert->raw.p, peercert->raw.len)) { +#endif + failf(data, "Failed copying peer certificate"); + result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + goto pinnedpubkey_error; + } + +#if MBEDTLS_VERSION_NUMBER == 0x03000000 + size = mbedtls_pk_write_pubkey_der(&p->MBEDTLS_PRIVATE(pk), pubkey, + PUB_DER_MAX_BYTES); +#else + size = mbedtls_pk_write_pubkey_der(&p->pk, pubkey, PUB_DER_MAX_BYTES); +#endif + + if(size <= 0) { + failf(data, "Failed copying public key from peer certificate"); + result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + goto pinnedpubkey_error; + } + + /* mbedtls_pk_write_pubkey_der writes data at the end of the buffer. */ + result = Curl_pin_peer_pubkey(data, + pinnedpubkey, + &pubkey[PUB_DER_MAX_BYTES - size], size); +pinnedpubkey_error: + mbedtls_x509_crt_free(p); + free(p); + free(pubkey); + if(result) { + return result; + } + } + +#ifdef HAS_ALPN + if(connssl->alpn) { + const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl); + + Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto, + proto? strlen(proto) : 0); + } +#endif + + connssl->connecting_state = ssl_connect_3; + infof(data, "SSL connected"); + + return CURLE_OK; +} + +static void mbedtls_session_free(void *sessionid, size_t idsize) +{ + (void)idsize; + mbedtls_ssl_session_free(sessionid); + free(sessionid); +} + +static CURLcode +mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + CURLcode retcode = CURLE_OK; + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + + DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); + + if(ssl_config->primary.cache_session) { + int ret; + mbedtls_ssl_session *our_ssl_sessionid; + + our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session)); + if(!our_ssl_sessionid) + return CURLE_OUT_OF_MEMORY; + + mbedtls_ssl_session_init(our_ssl_sessionid); + + ret = mbedtls_ssl_get_session(&backend->ssl, our_ssl_sessionid); + if(ret) { + if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED) + mbedtls_ssl_session_free(our_ssl_sessionid); + free(our_ssl_sessionid); + failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); + return CURLE_SSL_CONNECT_ERROR; + } + + /* If there is already a matching session in the cache, delete it */ + Curl_ssl_sessionid_lock(data); + retcode = Curl_ssl_set_sessionid(cf, data, &connssl->peer, + our_ssl_sessionid, 0, + mbedtls_session_free); + Curl_ssl_sessionid_unlock(data); + if(retcode) + return retcode; + } + + connssl->connecting_state = ssl_connect_done; + + return CURLE_OK; +} + +static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *mem, size_t len, + CURLcode *curlcode) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + int ret = -1; + + (void)data; + DEBUGASSERT(backend); + ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); + + if(ret < 0) { + CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X", + len, -ret); + *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_WRITE) +#ifdef TLS13_SUPPORT + || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) +#endif + )? CURLE_AGAIN : CURLE_SEND_ERROR; + ret = -1; + } + + return ret; +} + +static void mbedtls_close_all(struct Curl_easy *data) +{ + (void)data; +} + +static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + unsigned char buf[1024]; + CURLcode result = CURLE_OK; + int ret; + size_t i; + + DEBUGASSERT(backend); + + if(!backend->initialized || cf->shutdown) { + *done = TRUE; + return CURLE_OK; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + + if(!backend->sent_shutdown) { + /* do this only once */ + backend->sent_shutdown = TRUE; + if(send_shutdown) { + ret = mbedtls_ssl_close_notify(&backend->ssl); + switch(ret) { + case 0: /* we sent it, receive from the server */ + break; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: /* server also closed */ + *done = TRUE; + goto out; + case MBEDTLS_ERR_SSL_WANT_READ: + connssl->io_need = CURL_SSL_IO_NEED_RECV; + goto out; + case MBEDTLS_ERR_SSL_WANT_WRITE: + connssl->io_need = CURL_SSL_IO_NEED_SEND; + goto out; + default: + CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret); + result = CURLE_RECV_ERROR; + goto out; + } + } + } + + /* SSL should now have started the shutdown from our side. Since it + * was not complete, we are lacking the close notify from the server. */ + for(i = 0; i < 10; ++i) { + ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf)); + /* This seems to be a bug in mbedTLS TLSv1.3 where it reports + * WANT_READ, but has not encountered an EAGAIN. */ + if(ret == MBEDTLS_ERR_SSL_WANT_READ) + ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf)); +#ifdef TLS13_SUPPORT + if(ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + continue; +#endif + if(ret <= 0) + break; + } + + if(ret > 0) { + /* still data coming in? */ + CURL_TRC_CF(data, cf, "mbedtls_shutdown, still getting data"); + } + else if(ret == 0 || (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)) { + /* We got the close notify alert and are done. */ + CURL_TRC_CF(data, cf, "mbedtls_shutdown done"); + *done = TRUE; + } + else if(ret == MBEDTLS_ERR_SSL_WANT_READ) { + CURL_TRC_CF(data, cf, "mbedtls_shutdown, need RECV"); + connssl->io_need = CURL_SSL_IO_NEED_RECV; + } + else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + CURL_TRC_CF(data, cf, "mbedtls_shutdown, need SEND"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; + } + else { + CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret); + result = CURLE_RECV_ERROR; + } + +out: + cf->shutdown = (result || *done); + return result; +} + +static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + + (void)data; + DEBUGASSERT(backend); + if(backend->initialized) { + mbedtls_pk_free(&backend->pk); + mbedtls_x509_crt_free(&backend->clicert); + mbedtls_x509_crt_free(&backend->cacert); +#ifdef MBEDTLS_X509_CRL_PARSE_C + mbedtls_x509_crl_free(&backend->crl); +#endif + Curl_safefree(backend->ciphersuites); + mbedtls_ssl_config_free(&backend->config); + mbedtls_ssl_free(&backend->ssl); + mbedtls_ctr_drbg_free(&backend->ctr_drbg); +#ifndef THREADING_SUPPORT + mbedtls_entropy_free(&backend->entropy); +#endif /* THREADING_SUPPORT */ + backend->initialized = FALSE; + } +} + +static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t buffersize, + CURLcode *curlcode) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + int ret = -1; + ssize_t len = -1; + + (void)data; + DEBUGASSERT(backend); + + ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, + buffersize); + if(ret <= 0) { + CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", + buffersize, -ret); + if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) + return 0; + *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_READ) +#ifdef TLS13_SUPPORT + || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) +#endif + ) ? CURLE_AGAIN : CURLE_RECV_ERROR; + if(*curlcode != CURLE_AGAIN) { + char errorbuf[128]; + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "ssl_read returned: (-0x%04X) %s", -ret, errorbuf); + } + return -1; + } + + len = ret; + + return len; +} + +static size_t mbedtls_version(char *buffer, size_t size) +{ +#ifdef MBEDTLS_VERSION_C + /* if mbedtls_version_get_number() is available it is better */ + unsigned int version = mbedtls_version_get_number(); + return msnprintf(buffer, size, "mbedTLS/%u.%u.%u", version>>24, + (version>>16)&0xff, (version>>8)&0xff); +#else + return msnprintf(buffer, size, "mbedTLS/%s", MBEDTLS_VERSION_STRING); +#endif +} + +static CURLcode mbedtls_random(struct Curl_easy *data, + unsigned char *entropy, size_t length) +{ +#if defined(MBEDTLS_CTR_DRBG_C) + int ret = -1; + char errorbuf[128]; + mbedtls_entropy_context ctr_entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_init(&ctr_entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + + ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, + &ctr_entropy, NULL, 0); + + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", + -ret, errorbuf); + } + else { + ret = mbedtls_ctr_drbg_random(&ctr_drbg, entropy, length); + + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "mbedtls_ctr_drbg_random returned (-0x%04X) %s", + -ret, errorbuf); + } + } + + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&ctr_entropy); + + return ret == 0 ? CURLE_OK : CURLE_FAILED_INIT; +#elif defined(MBEDTLS_HAVEGE_C) + mbedtls_havege_state hs; + mbedtls_havege_init(&hs); + mbedtls_havege_random(&hs, entropy, length); + mbedtls_havege_free(&hs); + return CURLE_OK; +#else + return CURLE_NOT_BUILT_IN; +#endif +} + +static CURLcode +mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, + bool nonblocking, + bool *done) +{ + CURLcode retcode; + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + timediff_t timeout_ms; + int what; + + /* check if the connection has already been established */ + if(ssl_connection_complete == connssl->state) { + *done = TRUE; + return CURLE_OK; + } + + if(ssl_connect_1 == connssl->connecting_state) { + /* Find out how much more time we are allowed */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + retcode = mbed_connect_step1(cf, data); + if(retcode) + return retcode; + } + + while(ssl_connect_2 == connssl->connecting_state) { + + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { + + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; + + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + nonblocking ? 0 : timeout_ms); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + return CURLE_SSL_CONNECT_ERROR; + } + else if(0 == what) { + if(nonblocking) { + *done = FALSE; + return CURLE_OK; + } + else { + /* timeout */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + } + /* socket is readable or writable */ + } + + /* Run transaction, and return to the caller if it failed or if + * this connection is part of a multi handle and this loop would + * execute again. This permits the owner of a multi handle to + * abort a connection attempt before step2 has completed while + * ensuring that a client using select() or epoll() will always + * have a valid fdset to wait on. + */ + connssl->io_need = CURL_SSL_IO_NEED_NONE; + retcode = mbed_connect_step2(cf, data); + if(retcode || + (nonblocking && (ssl_connect_2 == connssl->connecting_state))) + return retcode; + + } /* repeat step2 until all transactions are done. */ + + if(ssl_connect_3 == connssl->connecting_state) { + retcode = mbed_connect_step3(cf, data); + if(retcode) + return retcode; + } + + if(ssl_connect_done == connssl->connecting_state) { + connssl->state = ssl_connection_complete; + *done = TRUE; + } + else + *done = FALSE; + + /* Reset our connect state machine */ + connssl->connecting_state = ssl_connect_1; + + return CURLE_OK; +} + +static CURLcode mbedtls_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + return mbed_connect_common(cf, data, TRUE, done); +} + + +static CURLcode mbedtls_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode retcode; + bool done = FALSE; + + retcode = mbed_connect_common(cf, data, FALSE, &done); + if(retcode) + return retcode; + + DEBUGASSERT(done); + + return CURLE_OK; +} + +/* + * return 0 error initializing SSL + * return 1 SSL initialized successfully + */ +static int mbedtls_init(void) +{ + if(!Curl_mbedtlsthreadlock_thread_setup()) + return 0; +#ifdef THREADING_SUPPORT + entropy_init_mutex(&ts_entropy); +#endif + return 1; +} + +static void mbedtls_cleanup(void) +{ +#ifdef THREADING_SUPPORT + entropy_cleanup_mutex(&ts_entropy); +#endif + (void)Curl_mbedtlsthreadlock_thread_cleanup(); +} + +static bool mbedtls_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct ssl_connect_data *ctx = cf->ctx; + struct mbed_ssl_backend_data *backend; + + (void)data; + DEBUGASSERT(ctx && ctx->backend); + backend = (struct mbed_ssl_backend_data *)ctx->backend; + return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0; +} + +static CURLcode mbedtls_sha256sum(const unsigned char *input, + size_t inputlen, + unsigned char *sha256sum, + size_t sha256len UNUSED_PARAM) +{ + /* TODO: explain this for different mbedtls 2.x vs 3 version */ + (void)sha256len; +#if MBEDTLS_VERSION_NUMBER < 0x02070000 + mbedtls_sha256(input, inputlen, sha256sum, 0); +#else + /* returns 0 on success, otherwise failure */ +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + if(mbedtls_sha256(input, inputlen, sha256sum, 0) != 0) +#else + if(mbedtls_sha256_ret(input, inputlen, sha256sum, 0) != 0) +#endif + return CURLE_BAD_FUNCTION_ARGUMENT; +#endif + return CURLE_OK; +} + +static void *mbedtls_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) +{ + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + (void)info; + DEBUGASSERT(backend); + return &backend->ssl; +} + +const struct Curl_ssl Curl_ssl_mbedtls = { + { CURLSSLBACKEND_MBEDTLS, "mbedtls" }, /* info */ + + SSLSUPP_CA_PATH | + SSLSUPP_CAINFO_BLOB | + SSLSUPP_CERTINFO | + SSLSUPP_PINNEDPUBKEY | + SSLSUPP_SSL_CTX | + SSLSUPP_HTTPS_PROXY, + + sizeof(struct mbed_ssl_backend_data), + + mbedtls_init, /* init */ + mbedtls_cleanup, /* cleanup */ + mbedtls_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + mbedtls_shutdown, /* shutdown */ + mbedtls_data_pending, /* data_pending */ + mbedtls_random, /* random */ + Curl_none_cert_status_request, /* cert_status_request */ + mbedtls_connect, /* connect */ + mbedtls_connect_nonblocking, /* connect_nonblocking */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ + mbedtls_get_internals, /* get_internals */ + mbedtls_close, /* close_one */ + mbedtls_close_all, /* close_all */ + Curl_none_set_engine, /* set_engine */ + Curl_none_set_engine_default, /* set_engine_default */ + Curl_none_engines_list, /* engines_list */ + Curl_none_false_start, /* false_start */ + mbedtls_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ + NULL, /* disassociate_connection */ + mbed_recv, /* recv decrypted data */ + mbed_send, /* send data to encrypt */ +}; + +#endif /* USE_MBEDTLS */ diff --git a/third_party/curl/lib/vtls/mbedtls.h b/third_party/curl/lib/vtls/mbedtls.h new file mode 100644 index 0000000000..d8a0a06eb6 --- /dev/null +++ b/third_party/curl/lib/vtls/mbedtls.h @@ -0,0 +1,34 @@ +#ifndef HEADER_CURL_MBEDTLS_H +#define HEADER_CURL_MBEDTLS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Hoi-Ho Chan, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_MBEDTLS + +extern const struct Curl_ssl Curl_ssl_mbedtls; + +#endif /* USE_MBEDTLS */ +#endif /* HEADER_CURL_MBEDTLS_H */ diff --git a/third_party/curl/lib/vtls/mbedtls_threadlock.c b/third_party/curl/lib/vtls/mbedtls_threadlock.c new file mode 100644 index 0000000000..b96a904fcb --- /dev/null +++ b/third_party/curl/lib/vtls/mbedtls_threadlock.c @@ -0,0 +1,134 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Hoi-Ho Chan, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if defined(USE_MBEDTLS) && \ + ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ + defined(_WIN32)) + +#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) +# include +# define MBEDTLS_MUTEX_T pthread_mutex_t +#elif defined(_WIN32) +# define MBEDTLS_MUTEX_T HANDLE +#endif + +#include "mbedtls_threadlock.h" +#include "curl_printf.h" +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* number of thread locks */ +#define NUMT 2 + +/* This array will store all of the mutexes available to Mbedtls. */ +static MBEDTLS_MUTEX_T *mutex_buf = NULL; + +int Curl_mbedtlsthreadlock_thread_setup(void) +{ + int i; + + mutex_buf = calloc(1, NUMT * sizeof(MBEDTLS_MUTEX_T)); + if(!mutex_buf) + return 0; /* error, no number of threads defined */ + + for(i = 0; i < NUMT; i++) { +#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) + if(pthread_mutex_init(&mutex_buf[i], NULL)) + return 0; /* pthread_mutex_init failed */ +#elif defined(_WIN32) + mutex_buf[i] = CreateMutex(0, FALSE, 0); + if(mutex_buf[i] == 0) + return 0; /* CreateMutex failed */ +#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ + } + + return 1; /* OK */ +} + +int Curl_mbedtlsthreadlock_thread_cleanup(void) +{ + int i; + + if(!mutex_buf) + return 0; /* error, no threads locks defined */ + + for(i = 0; i < NUMT; i++) { +#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) + if(pthread_mutex_destroy(&mutex_buf[i])) + return 0; /* pthread_mutex_destroy failed */ +#elif defined(_WIN32) + if(!CloseHandle(mutex_buf[i])) + return 0; /* CloseHandle failed */ +#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ + } + free(mutex_buf); + mutex_buf = NULL; + + return 1; /* OK */ +} + +int Curl_mbedtlsthreadlock_lock_function(int n) +{ + if(n < NUMT) { +#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) + if(pthread_mutex_lock(&mutex_buf[n])) { + DEBUGF(fprintf(stderr, + "Error: mbedtlsthreadlock_lock_function failed\n")); + return 0; /* pthread_mutex_lock failed */ + } +#elif defined(_WIN32) + if(WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED) { + DEBUGF(fprintf(stderr, + "Error: mbedtlsthreadlock_lock_function failed\n")); + return 0; /* pthread_mutex_lock failed */ + } +#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ + } + return 1; /* OK */ +} + +int Curl_mbedtlsthreadlock_unlock_function(int n) +{ + if(n < NUMT) { +#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) + if(pthread_mutex_unlock(&mutex_buf[n])) { + DEBUGF(fprintf(stderr, + "Error: mbedtlsthreadlock_unlock_function failed\n")); + return 0; /* pthread_mutex_unlock failed */ + } +#elif defined(_WIN32) + if(!ReleaseMutex(mutex_buf[n])) { + DEBUGF(fprintf(stderr, + "Error: mbedtlsthreadlock_unlock_function failed\n")); + return 0; /* pthread_mutex_lock failed */ + } +#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ + } + return 1; /* OK */ +} + +#endif /* USE_MBEDTLS */ diff --git a/third_party/curl/lib/vtls/mbedtls_threadlock.h b/third_party/curl/lib/vtls/mbedtls_threadlock.h new file mode 100644 index 0000000000..484626852f --- /dev/null +++ b/third_party/curl/lib/vtls/mbedtls_threadlock.h @@ -0,0 +1,50 @@ +#ifndef HEADER_CURL_MBEDTLS_THREADLOCK_H +#define HEADER_CURL_MBEDTLS_THREADLOCK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Hoi-Ho Chan, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_MBEDTLS + +#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ + defined(_WIN32) + +int Curl_mbedtlsthreadlock_thread_setup(void); +int Curl_mbedtlsthreadlock_thread_cleanup(void); +int Curl_mbedtlsthreadlock_lock_function(int n); +int Curl_mbedtlsthreadlock_unlock_function(int n); + +#else + +#define Curl_mbedtlsthreadlock_thread_setup() 1 +#define Curl_mbedtlsthreadlock_thread_cleanup() 1 +#define Curl_mbedtlsthreadlock_lock_function(x) 1 +#define Curl_mbedtlsthreadlock_unlock_function(x) 1 + +#endif /* (USE_THREADS_POSIX && HAVE_PTHREAD_H) || _WIN32 */ + +#endif /* USE_MBEDTLS */ + +#endif /* HEADER_CURL_MBEDTLS_THREADLOCK_H */ diff --git a/third_party/curl/lib/vtls/openssl.c b/third_party/curl/lib/vtls/openssl.c new file mode 100644 index 0000000000..8a65491b22 --- /dev/null +++ b/third_party/curl/lib/vtls/openssl.c @@ -0,0 +1,5225 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code + * but vtls.c should ever call or use these functions. + */ + +#include "curl_setup.h" + +#if defined(USE_QUICHE) || defined(USE_OPENSSL) + +#include + +/* Wincrypt must be included before anything that could include OpenSSL. */ +#if defined(USE_WIN32_CRYPTO) +#include +/* Undefine wincrypt conflicting symbols for BoringSSL. */ +#undef X509_NAME +#undef X509_EXTENSIONS +#undef PKCS7_ISSUER_AND_SERIAL +#undef PKCS7_SIGNER_INFO +#undef OCSP_REQUEST +#undef OCSP_RESPONSE +#endif + +#include "urldata.h" +#include "sendf.h" +#include "formdata.h" /* for the boundary function */ +#include "url.h" /* for the ssl config check function */ +#include "inet_pton.h" +#include "openssl.h" +#include "connect.h" +#include "slist.h" +#include "select.h" +#include "vtls.h" +#include "vtls_int.h" +#include "vauth/vauth.h" +#include "keylog.h" +#include "strcase.h" +#include "hostcheck.h" +#include "multiif.h" +#include "strerror.h" +#include "curl_printf.h" + +#include +#include +#include +#ifndef OPENSSL_NO_DSA +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_ECH +# ifndef OPENSSL_IS_BORINGSSL +# include +# endif +# include "curl_base64.h" +# define ECH_ENABLED(__data__) \ + (__data__->set.tls_ech && \ + !(__data__->set.tls_ech & CURLECH_DISABLE)\ + ) +#endif /* USE_ECH */ + +#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP) +#include +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) && /* 0.9.7 or later */ \ + !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE) +#define USE_OPENSSL_ENGINE +#include +#endif + +#include "warnless.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS + renegotiations when built with BoringSSL. Renegotiating is non-compliant + with HTTP/2 and "an extremely dangerous protocol feature". Beware. + +#define ALLOW_RENEG 1 + */ + +#ifndef OPENSSL_VERSION_NUMBER +#error "OPENSSL_VERSION_NUMBER not defined" +#endif + +#ifdef USE_OPENSSL_ENGINE +#include +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x00909000L +#define SSL_METHOD_QUAL const +#else +#define SSL_METHOD_QUAL +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) +#define HAVE_ERR_REMOVE_THREAD_STATE 1 +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && /* OpenSSL 1.1.0+ */ \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) +#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER +#define HAVE_X509_GET0_EXTENSIONS 1 /* added in 1.1.0 -pre1 */ +#define HAVE_OPAQUE_EVP_PKEY 1 /* since 1.1.0 -pre3 */ +#define HAVE_OPAQUE_RSA_DSA_DH 1 /* since 1.1.0 -pre5 */ +#define CONST_EXTS const +#define HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED 1 + +/* funny typecast define due to difference in API */ +#ifdef LIBRESSL_VERSION_NUMBER +#define ARG2_X509_signature_print (X509_ALGOR *) +#else +#define ARG2_X509_signature_print +#endif + +#else +/* For OpenSSL before 1.1.0 */ +#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x) +#define X509_get0_notBefore(x) X509_get_notBefore(x) +#define X509_get0_notAfter(x) X509_get_notAfter(x) +#define CONST_EXTS /* nope */ +#ifndef LIBRESSL_VERSION_NUMBER +#define OpenSSL_version_num() SSLeay() +#endif +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* 1.0.2 or later */ \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) +#define HAVE_X509_GET0_SIGNATURE 1 +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */ +#define HAVE_SSL_GET_SHUTDOWN 1 +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x10002003L && \ + OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \ + !defined(OPENSSL_NO_COMP) +#define HAVE_SSL_COMP_FREE_COMPRESSION_METHODS 1 +#endif + +#if (OPENSSL_VERSION_NUMBER < 0x0090808fL) +/* not present in older OpenSSL */ +#define OPENSSL_load_builtin_modules(x) +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) +#define HAVE_EVP_PKEY_GET_PARAMS 1 +#endif + +#ifdef HAVE_EVP_PKEY_GET_PARAMS +#include +#define DECLARE_PKEY_PARAM_BIGNUM(name) BIGNUM *name = NULL +#define FREE_PKEY_PARAM_BIGNUM(name) BN_clear_free(name) +#else +#define DECLARE_PKEY_PARAM_BIGNUM(name) const BIGNUM *name +#define FREE_PKEY_PARAM_BIGNUM(name) +#endif + +/* + * Whether SSL_CTX_set_keylog_callback is available. + * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 + * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) + * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing. + */ +#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ + !defined(LIBRESSL_VERSION_NUMBER)) || \ + defined(OPENSSL_IS_BORINGSSL) +#define HAVE_KEYLOG_CALLBACK +#endif + +/* Whether SSL_CTX_set_ciphersuites is available. + * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) + * BoringSSL: no + * LibreSSL: supported since 3.4.1 (released 2021-10-14) + */ +#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L && \ + !defined(LIBRESSL_VERSION_NUMBER)) || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER >= 0x3040100fL)) && \ + !defined(OPENSSL_IS_BORINGSSL) + #define HAVE_SSL_CTX_SET_CIPHERSUITES + #if !defined(OPENSSL_IS_AWSLC) + #define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH + #endif +#endif + +/* + * Whether SSL_CTX_set1_curves_list is available. + * OpenSSL: supported since 1.0.2, see + * https://docs.openssl.org/master/man3/SSL_CTX_set1_curves/ + * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30) + * LibreSSL: since 2.5.3 (April 12, 2017) + */ +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) || \ + defined(OPENSSL_IS_BORINGSSL) +#define HAVE_SSL_CTX_SET_EC_CURVES +#endif + +#if defined(LIBRESSL_VERSION_NUMBER) +#define OSSL_PACKAGE "LibreSSL" +#elif defined(OPENSSL_IS_BORINGSSL) +#define OSSL_PACKAGE "BoringSSL" +#elif defined(OPENSSL_IS_AWSLC) +#define OSSL_PACKAGE "AWS-LC" +#else +# if defined(USE_NGTCP2) && defined(USE_NGHTTP3) +# define OSSL_PACKAGE "quictls" +# else +# define OSSL_PACKAGE "OpenSSL" +#endif +#endif + +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +typedef size_t numcert_t; +#else +typedef int numcert_t; +#endif +#define ossl_valsize_t numcert_t + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) +/* up2date versions of OpenSSL maintain reasonably secure defaults without + * breaking compatibility, so it is better not to override the defaults in curl + */ +#define DEFAULT_CIPHER_SELECTION NULL +#else +/* not the case with old versions of OpenSSL */ +#define DEFAULT_CIPHER_SELECTION \ + "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH" +#endif + +#ifdef HAVE_OPENSSL_SRP +/* the function exists */ +#ifdef USE_TLS_SRP +/* the functionality is not disabled */ +#define USE_OPENSSL_SRP +#endif +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) +#define HAVE_RANDOM_INIT_BY_DEFAULT 1 +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x2070100fL) && \ + !defined(OPENSSL_IS_BORINGSSL) && \ + !defined(OPENSSL_IS_AWSLC) +#define HAVE_OPENSSL_VERSION +#endif + +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +typedef uint32_t sslerr_t; +#else +typedef unsigned long sslerr_t; +#endif + +/* + * Whether the OpenSSL version has the API needed to support sharing an + * X509_STORE between connections. The API is: + * * `X509_STORE_up_ref` -- Introduced: OpenSSL 1.1.0. + */ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* OpenSSL >= 1.1.0 */ +#define HAVE_SSL_X509_STORE_SHARE +#endif + +/* What API version do we use? */ +#if defined(LIBRESSL_VERSION_NUMBER) +#define USE_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f) +#else /* !LIBRESSL_VERSION_NUMBER */ +#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L) +#endif /* !LIBRESSL_VERSION_NUMBER */ + +#define push_certinfo(_label, _num) \ +do { \ + long info_len = BIO_get_mem_data(mem, &ptr); \ + Curl_ssl_push_certinfo_len(data, _num, _label, ptr, info_len); \ + if(1 != BIO_reset(mem)) \ + break; \ +} while(0) + +static void pubkey_show(struct Curl_easy *data, + BIO *mem, + int num, + const char *type, + const char *name, + const BIGNUM *bn) +{ + char *ptr; + char namebuf[32]; + + msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name); + + if(bn) + BN_print(mem, bn); + push_certinfo(namebuf, num); +} + +#ifdef HAVE_OPAQUE_RSA_DSA_DH +#define print_pubkey_BN(_type, _name, _num) \ + pubkey_show(data, mem, _num, #_type, #_name, _name) + +#else +#define print_pubkey_BN(_type, _name, _num) \ +do { \ + if(_type->_name) { \ + pubkey_show(data, mem, _num, #_type, #_name, _type->_name); \ + } \ +} while(0) +#endif + +static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len) +{ + int i, ilen; + + ilen = (int)len; + if(ilen < 0) + return 1; /* buffer too big */ + + i = i2t_ASN1_OBJECT(buf, ilen, a); + + if(i >= ilen) + return 1; /* buffer too small */ + + return 0; +} + +static void X509V3_ext(struct Curl_easy *data, + int certnum, + CONST_EXTS STACK_OF(X509_EXTENSION) *exts) +{ + int i; + + if((int)sk_X509_EXTENSION_num(exts) <= 0) + /* no extensions, bail out */ + return; + + for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) { + ASN1_OBJECT *obj; + X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, (ossl_valsize_t)i); + BUF_MEM *biomem; + char namebuf[128]; + BIO *bio_out = BIO_new(BIO_s_mem()); + + if(!bio_out) + return; + + obj = X509_EXTENSION_get_object(ext); + + asn1_object_dump(obj, namebuf, sizeof(namebuf)); + + if(!X509V3_EXT_print(bio_out, ext, 0, 0)) + ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext)); + + BIO_get_mem_ptr(bio_out, &biomem); + Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data, + biomem->length); + BIO_free(bio_out); + } +} + +CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) +{ + CURLcode result; + STACK_OF(X509) *sk; + int i; + numcert_t numcerts; + BIO *mem; + + DEBUGASSERT(ssl); + + sk = SSL_get_peer_cert_chain(ssl); + if(!sk) { + return CURLE_OUT_OF_MEMORY; + } + + numcerts = sk_X509_num(sk); + + result = Curl_ssl_init_certinfo(data, (int)numcerts); + if(result) { + return result; + } + + mem = BIO_new(BIO_s_mem()); + if(!mem) { + return CURLE_OUT_OF_MEMORY; + } + + for(i = 0; i < (int)numcerts; i++) { + ASN1_INTEGER *num; + X509 *x = sk_X509_value(sk, (ossl_valsize_t)i); + EVP_PKEY *pubkey = NULL; + int j; + char *ptr; + const ASN1_BIT_STRING *psig = NULL; + + X509_NAME_print_ex(mem, X509_get_subject_name(x), 0, XN_FLAG_ONELINE); + push_certinfo("Subject", i); + + X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE); + push_certinfo("Issuer", i); + + BIO_printf(mem, "%lx", X509_get_version(x)); + push_certinfo("Version", i); + + num = X509_get_serialNumber(x); + if(num->type == V_ASN1_NEG_INTEGER) + BIO_puts(mem, "-"); + for(j = 0; j < num->length; j++) + BIO_printf(mem, "%02x", num->data[j]); + push_certinfo("Serial Number", i); + +#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS) + { + const X509_ALGOR *sigalg = NULL; + X509_PUBKEY *xpubkey = NULL; + ASN1_OBJECT *pubkeyoid = NULL; + + X509_get0_signature(&psig, &sigalg, x); + if(sigalg) { + const ASN1_OBJECT *sigalgoid = NULL; + X509_ALGOR_get0(&sigalgoid, NULL, NULL, sigalg); + i2a_ASN1_OBJECT(mem, sigalgoid); + push_certinfo("Signature Algorithm", i); + } + + xpubkey = X509_get_X509_PUBKEY(x); + if(xpubkey) { + X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey); + if(pubkeyoid) { + i2a_ASN1_OBJECT(mem, pubkeyoid); + push_certinfo("Public Key Algorithm", i); + } + } + + X509V3_ext(data, i, X509_get0_extensions(x)); + } +#else + { + /* before OpenSSL 1.0.2 */ + X509_CINF *cinf = x->cert_info; + + i2a_ASN1_OBJECT(mem, cinf->signature->algorithm); + push_certinfo("Signature Algorithm", i); + + i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm); + push_certinfo("Public Key Algorithm", i); + + X509V3_ext(data, i, cinf->extensions); + + psig = x->signature; + } +#endif + + ASN1_TIME_print(mem, X509_get0_notBefore(x)); + push_certinfo("Start date", i); + + ASN1_TIME_print(mem, X509_get0_notAfter(x)); + push_certinfo("Expire date", i); + + pubkey = X509_get_pubkey(x); + if(!pubkey) + infof(data, " Unable to load public key"); + else { + int pktype; +#ifdef HAVE_OPAQUE_EVP_PKEY + pktype = EVP_PKEY_id(pubkey); +#else + pktype = pubkey->type; +#endif + switch(pktype) { + case EVP_PKEY_RSA: + { +#ifndef HAVE_EVP_PKEY_GET_PARAMS + RSA *rsa; +#ifdef HAVE_OPAQUE_EVP_PKEY + rsa = EVP_PKEY_get0_RSA(pubkey); +#else + rsa = pubkey->pkey.rsa; +#endif /* HAVE_OPAQUE_EVP_PKEY */ +#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ + + { +#ifdef HAVE_OPAQUE_RSA_DSA_DH + DECLARE_PKEY_PARAM_BIGNUM(n); + DECLARE_PKEY_PARAM_BIGNUM(e); +#ifdef HAVE_EVP_PKEY_GET_PARAMS + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e); +#else + RSA_get0_key(rsa, &n, &e, NULL); +#endif /* HAVE_EVP_PKEY_GET_PARAMS */ + BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0); +#else + BIO_printf(mem, "%d", rsa->n ? BN_num_bits(rsa->n) : 0); +#endif /* HAVE_OPAQUE_RSA_DSA_DH */ + push_certinfo("RSA Public Key", i); + print_pubkey_BN(rsa, n, i); + print_pubkey_BN(rsa, e, i); + FREE_PKEY_PARAM_BIGNUM(n); + FREE_PKEY_PARAM_BIGNUM(e); + } + + break; + } + case EVP_PKEY_DSA: + { +#ifndef OPENSSL_NO_DSA +#ifndef HAVE_EVP_PKEY_GET_PARAMS + DSA *dsa; +#ifdef HAVE_OPAQUE_EVP_PKEY + dsa = EVP_PKEY_get0_DSA(pubkey); +#else + dsa = pubkey->pkey.dsa; +#endif /* HAVE_OPAQUE_EVP_PKEY */ +#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ + { +#ifdef HAVE_OPAQUE_RSA_DSA_DH + DECLARE_PKEY_PARAM_BIGNUM(p); + DECLARE_PKEY_PARAM_BIGNUM(q); + DECLARE_PKEY_PARAM_BIGNUM(g); + DECLARE_PKEY_PARAM_BIGNUM(pub_key); +#ifdef HAVE_EVP_PKEY_GET_PARAMS + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key); +#else + DSA_get0_pqg(dsa, &p, &q, &g); + DSA_get0_key(dsa, &pub_key, NULL); +#endif /* HAVE_EVP_PKEY_GET_PARAMS */ +#endif /* HAVE_OPAQUE_RSA_DSA_DH */ + print_pubkey_BN(dsa, p, i); + print_pubkey_BN(dsa, q, i); + print_pubkey_BN(dsa, g, i); + print_pubkey_BN(dsa, pub_key, i); + FREE_PKEY_PARAM_BIGNUM(p); + FREE_PKEY_PARAM_BIGNUM(q); + FREE_PKEY_PARAM_BIGNUM(g); + FREE_PKEY_PARAM_BIGNUM(pub_key); + } +#endif /* !OPENSSL_NO_DSA */ + break; + } + case EVP_PKEY_DH: + { +#ifndef HAVE_EVP_PKEY_GET_PARAMS + DH *dh; +#ifdef HAVE_OPAQUE_EVP_PKEY + dh = EVP_PKEY_get0_DH(pubkey); +#else + dh = pubkey->pkey.dh; +#endif /* HAVE_OPAQUE_EVP_PKEY */ +#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ + { +#ifdef HAVE_OPAQUE_RSA_DSA_DH + DECLARE_PKEY_PARAM_BIGNUM(p); + DECLARE_PKEY_PARAM_BIGNUM(q); + DECLARE_PKEY_PARAM_BIGNUM(g); + DECLARE_PKEY_PARAM_BIGNUM(pub_key); +#ifdef HAVE_EVP_PKEY_GET_PARAMS + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key); +#else + DH_get0_pqg(dh, &p, &q, &g); + DH_get0_key(dh, &pub_key, NULL); +#endif /* HAVE_EVP_PKEY_GET_PARAMS */ + print_pubkey_BN(dh, p, i); + print_pubkey_BN(dh, q, i); + print_pubkey_BN(dh, g, i); +#else + print_pubkey_BN(dh, p, i); + print_pubkey_BN(dh, g, i); +#endif /* HAVE_OPAQUE_RSA_DSA_DH */ + print_pubkey_BN(dh, pub_key, i); + FREE_PKEY_PARAM_BIGNUM(p); + FREE_PKEY_PARAM_BIGNUM(q); + FREE_PKEY_PARAM_BIGNUM(g); + FREE_PKEY_PARAM_BIGNUM(pub_key); + } + break; + } + } + EVP_PKEY_free(pubkey); + } + + if(psig) { + for(j = 0; j < psig->length; j++) + BIO_printf(mem, "%02x:", psig->data[j]); + push_certinfo("Signature", i); + } + + PEM_write_bio_X509(mem, x); + push_certinfo("Cert", i); + } + + BIO_free(mem); + + return CURLE_OK; +} + +#endif /* quiche or OpenSSL */ + +#ifdef USE_OPENSSL + +#if USE_PRE_1_1_API +#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL +#define BIO_set_init(x,v) ((x)->init=(v)) +#define BIO_get_data(x) ((x)->ptr) +#define BIO_set_data(x,v) ((x)->ptr=(v)) +#endif +#define BIO_get_shutdown(x) ((x)->shutdown) +#define BIO_set_shutdown(x,v) ((x)->shutdown=(v)) +#endif /* USE_PRE_1_1_API */ + +static int ossl_bio_cf_create(BIO *bio) +{ + BIO_set_shutdown(bio, 1); + BIO_set_init(bio, 1); +#if USE_PRE_1_1_API + bio->num = -1; +#endif + BIO_set_data(bio, NULL); + return 1; +} + +static int ossl_bio_cf_destroy(BIO *bio) +{ + if(!bio) + return 0; + return 1; +} + +static long ossl_bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + struct Curl_cfilter *cf = BIO_get_data(bio); + long ret = 1; + + (void)cf; + (void)ptr; + switch(cmd) { + case BIO_CTRL_GET_CLOSE: + ret = (long)BIO_get_shutdown(bio); + break; + case BIO_CTRL_SET_CLOSE: + BIO_set_shutdown(bio, (int)num); + break; + case BIO_CTRL_FLUSH: + /* we do no delayed writes, but if we ever would, this + * needs to trigger it. */ + ret = 1; + break; + case BIO_CTRL_DUP: + ret = 1; + break; +#ifdef BIO_CTRL_EOF + case BIO_CTRL_EOF: + /* EOF has been reached on input? */ + return (!cf->next || !cf->next->connected); +#endif + default: + ret = 0; + break; + } + return ret; +} + +static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen) +{ + struct Curl_cfilter *cf = BIO_get_data(bio); + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nwritten; + CURLcode result = CURLE_SEND_ERROR; + + DEBUGASSERT(data); + if(blen < 0) + return 0; + + nwritten = Curl_conn_cf_send(cf->next, data, buf, (size_t)blen, &result); + CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, err=%d", + blen, (int)nwritten, result); + BIO_clear_retry_flags(bio); + octx->io_result = result; + if(nwritten < 0) { + if(CURLE_AGAIN == result) + BIO_set_retry_write(bio); + } + return (int)nwritten; +} + +static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) +{ + struct Curl_cfilter *cf = BIO_get_data(bio); + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nread; + CURLcode result = CURLE_RECV_ERROR; + + DEBUGASSERT(data); + /* OpenSSL catches this case, so should we. */ + if(!buf) + return 0; + if(blen < 0) + return 0; + + nread = Curl_conn_cf_recv(cf->next, data, buf, (size_t)blen, &result); + CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, err=%d", + blen, (int)nread, result); + BIO_clear_retry_flags(bio); + octx->io_result = result; + if(nread < 0) { + if(CURLE_AGAIN == result) + BIO_set_retry_read(bio); + } + else if(nread == 0) { + connssl->peer_closed = TRUE; + } + + /* Before returning server replies to the SSL instance, we need + * to have setup the x509 store or verification will fail. */ + if(!octx->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); + if(result) { + octx->io_result = result; + return -1; + } + octx->x509_store_setup = TRUE; + } + + return (int)nread; +} + +#if USE_PRE_1_1_API + +static BIO_METHOD ossl_bio_cf_meth_1_0 = { + BIO_TYPE_MEM, + "OpenSSL CF BIO", + ossl_bio_cf_out_write, + ossl_bio_cf_in_read, + NULL, /* puts is never called */ + NULL, /* gets is never called */ + ossl_bio_cf_ctrl, + ossl_bio_cf_create, + ossl_bio_cf_destroy, + NULL +}; + +static BIO_METHOD *ossl_bio_cf_method_create(void) +{ + return &ossl_bio_cf_meth_1_0; +} + +#define ossl_bio_cf_method_free(m) Curl_nop_stmt + +#else + +static BIO_METHOD *ossl_bio_cf_method_create(void) +{ + BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO"); + if(m) { + BIO_meth_set_write(m, &ossl_bio_cf_out_write); + BIO_meth_set_read(m, &ossl_bio_cf_in_read); + BIO_meth_set_ctrl(m, &ossl_bio_cf_ctrl); + BIO_meth_set_create(m, &ossl_bio_cf_create); + BIO_meth_set_destroy(m, &ossl_bio_cf_destroy); + } + return m; +} + +static void ossl_bio_cf_method_free(BIO_METHOD *m) +{ + if(m) + BIO_meth_free(m); +} + +#endif + + +/* + * Number of bytes to read from the random number seed file. This must be + * a finite value (because some entropy "files" like /dev/urandom have + * an infinite length), but must be large enough to provide enough + * entropy to properly seed OpenSSL's PRNG. + */ +#define RAND_LOAD_LENGTH 1024 + +#ifdef HAVE_KEYLOG_CALLBACK +static void ossl_keylog_callback(const SSL *ssl, const char *line) +{ + (void)ssl; + + Curl_tls_keylog_write_line(line); +} +#else +/* + * ossl_log_tls12_secret is called by libcurl to make the CLIENT_RANDOMs if the + * OpenSSL being used does not have native support for doing that. + */ +static void +ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) +{ + const SSL_SESSION *session = SSL_get_session(ssl); + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; + int master_key_length = 0; + + if(!session || *keylog_done) + return; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) + /* ssl->s3 is not checked in OpenSSL 1.1.0-pre6, but let's assume that + * we have a valid SSL context if we have a non-NULL session. */ + SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE); + master_key_length = (int) + SSL_SESSION_get_master_key(session, master_key, SSL_MAX_MASTER_KEY_LENGTH); +#else + if(ssl->s3 && session->master_key_length > 0) { + master_key_length = session->master_key_length; + memcpy(master_key, session->master_key, session->master_key_length); + memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE); + } +#endif + + /* The handshake has not progressed sufficiently yet, or this is a TLS 1.3 + * session (when curl was built with older OpenSSL headers and running with + * newer OpenSSL runtime libraries). */ + if(master_key_length <= 0) + return; + + *keylog_done = true; + Curl_tls_keylog_write("CLIENT_RANDOM", client_random, + master_key, master_key_length); +} +#endif /* !HAVE_KEYLOG_CALLBACK */ + +static const char *SSL_ERROR_to_str(int err) +{ + switch(err) { + case SSL_ERROR_NONE: + return "SSL_ERROR_NONE"; + case SSL_ERROR_SSL: + return "SSL_ERROR_SSL"; + case SSL_ERROR_WANT_READ: + return "SSL_ERROR_WANT_READ"; + case SSL_ERROR_WANT_WRITE: + return "SSL_ERROR_WANT_WRITE"; + case SSL_ERROR_WANT_X509_LOOKUP: + return "SSL_ERROR_WANT_X509_LOOKUP"; + case SSL_ERROR_SYSCALL: + return "SSL_ERROR_SYSCALL"; + case SSL_ERROR_ZERO_RETURN: + return "SSL_ERROR_ZERO_RETURN"; + case SSL_ERROR_WANT_CONNECT: + return "SSL_ERROR_WANT_CONNECT"; + case SSL_ERROR_WANT_ACCEPT: + return "SSL_ERROR_WANT_ACCEPT"; +#if defined(SSL_ERROR_WANT_ASYNC) + case SSL_ERROR_WANT_ASYNC: + return "SSL_ERROR_WANT_ASYNC"; +#endif +#if defined(SSL_ERROR_WANT_ASYNC_JOB) + case SSL_ERROR_WANT_ASYNC_JOB: + return "SSL_ERROR_WANT_ASYNC_JOB"; +#endif +#if defined(SSL_ERROR_WANT_EARLY) + case SSL_ERROR_WANT_EARLY: + return "SSL_ERROR_WANT_EARLY"; +#endif + default: + return "SSL_ERROR unknown"; + } +} + +static size_t ossl_version(char *buffer, size_t size); + +/* Return error string for last OpenSSL error + */ +static char *ossl_strerror(unsigned long error, char *buf, size_t size) +{ + size_t len; + DEBUGASSERT(size); + *buf = '\0'; + + len = ossl_version(buf, size); + DEBUGASSERT(len < (size - 2)); + if(len < (size - 2)) { + buf += len; + size -= (len + 2); + *buf++ = ':'; + *buf++ = ' '; + *buf = '\0'; + } + +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) + ERR_error_string_n((uint32_t)error, buf, size); +#else + ERR_error_string_n(error, buf, size); +#endif + + if(!*buf) { + const char *msg = error ? "Unknown error" : "No error"; + if(strlen(msg) < size) + strcpy(buf, msg); + } + + return buf; +} + +static int passwd_callback(char *buf, int num, int encrypting, + void *global_passwd) +{ + DEBUGASSERT(0 == encrypting); + + if(!encrypting && num >= 0) { + int klen = curlx_uztosi(strlen((char *)global_passwd)); + if(num > klen) { + memcpy(buf, global_passwd, klen + 1); + return klen; + } + } + return 0; +} + +/* + * rand_enough() returns TRUE if we have seeded the random engine properly. + */ +static bool rand_enough(void) +{ + return (0 != RAND_status()) ? TRUE : FALSE; +} + +static CURLcode ossl_seed(struct Curl_easy *data) +{ + /* This might get called before it has been added to a multi handle */ + if(data->multi && data->multi->ssl_seeded) + return CURLE_OK; + + if(rand_enough()) { + /* OpenSSL 1.1.0+ should return here */ + if(data->multi) + data->multi->ssl_seeded = TRUE; + return CURLE_OK; + } +#ifdef HAVE_RANDOM_INIT_BY_DEFAULT + /* with OpenSSL 1.1.0+, a failed RAND_status is a showstopper */ + failf(data, "Insufficient randomness"); + return CURLE_SSL_CONNECT_ERROR; +#else + +#ifdef RANDOM_FILE + RAND_load_file(RANDOM_FILE, RAND_LOAD_LENGTH); + if(rand_enough()) + return CURLE_OK; +#endif + + /* fallback to a custom seeding of the PRNG using a hash based on a current + time */ + do { + unsigned char randb[64]; + size_t len = sizeof(randb); + size_t i, i_max; + for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) { + struct curltime tv = Curl_now(); + Curl_wait_ms(1); + tv.tv_sec *= (time_t)i + 1; + tv.tv_usec *= (int)i + 2; + tv.tv_sec ^= ((Curl_now().tv_sec + (time_t)Curl_now().tv_usec) * + (time_t)(i + 3)) << 8; + tv.tv_usec ^= (int) ((Curl_now().tv_sec + (time_t)Curl_now().tv_usec) * + (time_t)(i + 4)) << 16; + memcpy(&randb[i * sizeof(struct curltime)], &tv, + sizeof(struct curltime)); + } + RAND_add(randb, (int)len, (double)len/2); + } while(!rand_enough()); + + { + /* generates a default path for the random seed file */ + char fname[256]; + fname[0] = 0; /* blank it first */ + RAND_file_name(fname, sizeof(fname)); + if(fname[0]) { + /* we got a filename to try */ + RAND_load_file(fname, RAND_LOAD_LENGTH); + if(rand_enough()) + return CURLE_OK; + } + } + + infof(data, "libcurl is now using a weak random seed"); + return (rand_enough() ? CURLE_OK : + CURLE_SSL_CONNECT_ERROR /* confusing error code */); +#endif +} + +#ifndef SSL_FILETYPE_ENGINE +#define SSL_FILETYPE_ENGINE 42 +#endif +#ifndef SSL_FILETYPE_PKCS12 +#define SSL_FILETYPE_PKCS12 43 +#endif +static int do_file_type(const char *type) +{ + if(!type || !type[0]) + return SSL_FILETYPE_PEM; + if(strcasecompare(type, "PEM")) + return SSL_FILETYPE_PEM; + if(strcasecompare(type, "DER")) + return SSL_FILETYPE_ASN1; + if(strcasecompare(type, "ENG")) + return SSL_FILETYPE_ENGINE; + if(strcasecompare(type, "P12")) + return SSL_FILETYPE_PKCS12; + return -1; +} + +#ifdef USE_OPENSSL_ENGINE +/* + * Supply default password to the engine user interface conversation. + * The password is passed by OpenSSL engine from ENGINE_load_private_key() + * last argument to the ui and can be obtained by UI_get0_user_data(ui) here. + */ +static int ssl_ui_reader(UI *ui, UI_STRING *uis) +{ + const char *password; + switch(UI_get_string_type(uis)) { + case UIT_PROMPT: + case UIT_VERIFY: + password = (const char *)UI_get0_user_data(ui); + if(password && (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) { + UI_set_result(ui, uis, password); + return 1; + } + FALLTHROUGH(); + default: + break; + } + return (UI_method_get_reader(UI_OpenSSL()))(ui, uis); +} + +/* + * Suppress interactive request for a default password if available. + */ +static int ssl_ui_writer(UI *ui, UI_STRING *uis) +{ + switch(UI_get_string_type(uis)) { + case UIT_PROMPT: + case UIT_VERIFY: + if(UI_get0_user_data(ui) && + (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) { + return 1; + } + FALLTHROUGH(); + default: + break; + } + return (UI_method_get_writer(UI_OpenSSL()))(ui, uis); +} + +/* + * Check if a given string is a PKCS#11 URI + */ +static bool is_pkcs11_uri(const char *string) +{ + return (string && strncasecompare(string, "pkcs11:", 7)); +} + +#endif + +static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine); + +static int +SSL_CTX_use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob, + int type, const char *key_passwd) +{ + int ret = 0; + X509 *x = NULL; + /* the typecast of blob->len is fine since it is guaranteed to never be + larger than CURL_MAX_INPUT_LENGTH */ + BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len)); + if(!in) + return CURLE_OUT_OF_MEMORY; + + if(type == SSL_FILETYPE_ASN1) { + /* j = ERR_R_ASN1_LIB; */ + x = d2i_X509_bio(in, NULL); + } + else if(type == SSL_FILETYPE_PEM) { + /* ERR_R_PEM_LIB; */ + x = PEM_read_bio_X509(in, NULL, + passwd_callback, (void *)key_passwd); + } + else { + ret = 0; + goto end; + } + + if(!x) { + ret = 0; + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); +end: + X509_free(x); + BIO_free(in); + return ret; +} + +static int +SSL_CTX_use_PrivateKey_blob(SSL_CTX *ctx, const struct curl_blob *blob, + int type, const char *key_passwd) +{ + int ret = 0; + EVP_PKEY *pkey = NULL; + BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len)); + if(!in) + return CURLE_OUT_OF_MEMORY; + + if(type == SSL_FILETYPE_PEM) + pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback, + (void *)key_passwd); + else if(type == SSL_FILETYPE_ASN1) + pkey = d2i_PrivateKey_bio(in, NULL); + else { + ret = 0; + goto end; + } + if(!pkey) { + ret = 0; + goto end; + } + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + EVP_PKEY_free(pkey); +end: + BIO_free(in); + return ret; +} + +static int +SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob, + const char *key_passwd) +{ +/* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */ +#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* OpenSSL 1.0.2 or later */ \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + (LIBRESSL_VERSION_NUMBER < 0x2090100fL)) /* LibreSSL 2.9.1 or later */ + int ret = 0; + X509 *x = NULL; + void *passwd_callback_userdata = (void *)key_passwd; + BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len)); + if(!in) + return CURLE_OUT_OF_MEMORY; + + ERR_clear_error(); + + x = PEM_read_bio_X509_AUX(in, NULL, + passwd_callback, (void *)key_passwd); + + if(!x) { + ret = 0; + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + + if(ERR_peek_error() != 0) + ret = 0; + + if(ret) { + X509 *ca; + sslerr_t err; + + if(!SSL_CTX_clear_chain_certs(ctx)) { + ret = 0; + goto end; + } + + while((ca = PEM_read_bio_X509(in, NULL, passwd_callback, + passwd_callback_userdata)) + != NULL) { + + if(!SSL_CTX_add0_chain_cert(ctx, ca)) { + X509_free(ca); + ret = 0; + goto end; + } + } + + err = ERR_peek_last_error(); + if((ERR_GET_LIB(err) == ERR_LIB_PEM) && + (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) + ERR_clear_error(); + else + ret = 0; + } + +end: + X509_free(x); + BIO_free(in); + return ret; +#else + (void)ctx; /* unused */ + (void)blob; /* unused */ + (void)key_passwd; /* unused */ + return 0; +#endif +} + +static +int cert_stuff(struct Curl_easy *data, + SSL_CTX* ctx, + char *cert_file, + const struct curl_blob *cert_blob, + const char *cert_type, + char *key_file, + const struct curl_blob *key_blob, + const char *key_type, + char *key_passwd) +{ + char error_buffer[256]; + bool check_privkey = TRUE; + + int file_type = do_file_type(cert_type); + + if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE)) { + SSL *ssl; + X509 *x509; + int cert_done = 0; + int cert_use_result; + + if(key_passwd) { + /* set the password in the callback userdata */ + SSL_CTX_set_default_passwd_cb_userdata(ctx, key_passwd); + /* Set passwd callback: */ + SSL_CTX_set_default_passwd_cb(ctx, passwd_callback); + } + + + switch(file_type) { + case SSL_FILETYPE_PEM: + /* SSL_CTX_use_certificate_chain_file() only works on PEM files */ + cert_use_result = cert_blob ? + SSL_CTX_use_certificate_chain_blob(ctx, cert_blob, key_passwd) : + SSL_CTX_use_certificate_chain_file(ctx, cert_file); + if(cert_use_result != 1) { + failf(data, + "could not load PEM client certificate from %s, " OSSL_PACKAGE + " error %s, " + "(no key found, wrong pass phrase, or wrong file format?)", + (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file), + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + return 0; + } + break; + + case SSL_FILETYPE_ASN1: + /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but + we use the case above for PEM so this can only be performed with + ASN1 files. */ + + cert_use_result = cert_blob ? + SSL_CTX_use_certificate_blob(ctx, cert_blob, + file_type, key_passwd) : + SSL_CTX_use_certificate_file(ctx, cert_file, file_type); + if(cert_use_result != 1) { + failf(data, + "could not load ASN1 client certificate from %s, " OSSL_PACKAGE + " error %s, " + "(no key found, wrong pass phrase, or wrong file format?)", + (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file), + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + return 0; + } + break; + case SSL_FILETYPE_ENGINE: +#if defined(USE_OPENSSL_ENGINE) && defined(ENGINE_CTRL_GET_CMD_FROM_NAME) + { + /* Implicitly use pkcs11 engine if none was provided and the + * cert_file is a PKCS#11 URI */ + if(!data->state.engine) { + if(is_pkcs11_uri(cert_file)) { + if(ossl_set_engine(data, "pkcs11") != CURLE_OK) { + return 0; + } + } + } + + if(data->state.engine) { + const char *cmd_name = "LOAD_CERT_CTRL"; + struct { + const char *cert_id; + X509 *cert; + } params; + + params.cert_id = cert_file; + params.cert = NULL; + + /* Does the engine supports LOAD_CERT_CTRL ? */ + if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME, + 0, (void *)cmd_name, NULL)) { + failf(data, "ssl engine does not support loading certificates"); + return 0; + } + + /* Load the certificate from the engine */ + if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name, + 0, ¶ms, NULL, 1)) { + failf(data, "ssl engine cannot load client cert with id" + " '%s' [%s]", cert_file, + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return 0; + } + + if(!params.cert) { + failf(data, "ssl engine did not initialized the certificate " + "properly."); + return 0; + } + + if(SSL_CTX_use_certificate(ctx, params.cert) != 1) { + failf(data, "unable to set client certificate [%s]", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return 0; + } + X509_free(params.cert); /* we do not need the handle any more... */ + } + else { + failf(data, "crypto engine not set, cannot load certificate"); + return 0; + } + } + break; +#else + failf(data, "file type ENG for certificate not implemented"); + return 0; +#endif + + case SSL_FILETYPE_PKCS12: + { + BIO *cert_bio = NULL; + PKCS12 *p12 = NULL; + EVP_PKEY *pri; + STACK_OF(X509) *ca = NULL; + if(cert_blob) { + cert_bio = BIO_new_mem_buf(cert_blob->data, (int)(cert_blob->len)); + if(!cert_bio) { + failf(data, + "BIO_new_mem_buf NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + return 0; + } + } + else { + cert_bio = BIO_new(BIO_s_file()); + if(!cert_bio) { + failf(data, + "BIO_new return NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + return 0; + } + + if(BIO_read_filename(cert_bio, cert_file) <= 0) { + failf(data, "could not open PKCS12 file '%s'", cert_file); + BIO_free(cert_bio); + return 0; + } + } + + p12 = d2i_PKCS12_bio(cert_bio, NULL); + BIO_free(cert_bio); + + if(!p12) { + failf(data, "error reading PKCS12 file '%s'", + cert_blob ? "(memory blob)" : cert_file); + return 0; + } + + PKCS12_PBE_add(); + + if(!PKCS12_parse(p12, key_passwd, &pri, &x509, + &ca)) { + failf(data, + "could not parse PKCS12 file, check password, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + PKCS12_free(p12); + return 0; + } + + PKCS12_free(p12); + + if(SSL_CTX_use_certificate(ctx, x509) != 1) { + failf(data, + "could not load PKCS12 client certificate, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + goto fail; + } + + if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) { + failf(data, "unable to use private key from PKCS12 file '%s'", + cert_file); + goto fail; + } + + if(!SSL_CTX_check_private_key (ctx)) { + failf(data, "private key from PKCS12 file '%s' " + "does not match certificate in same file", cert_file); + goto fail; + } + /* Set Certificate Verification chain */ + if(ca) { + while(sk_X509_num(ca)) { + /* + * Note that sk_X509_pop() is used below to make sure the cert is + * removed from the stack properly before getting passed to + * SSL_CTX_add_extra_chain_cert(), which takes ownership. Previously + * we used sk_X509_value() instead, but then we would clean it in the + * subsequent sk_X509_pop_free() call. + */ + X509 *x = sk_X509_pop(ca); + if(!SSL_CTX_add_client_CA(ctx, x)) { + X509_free(x); + failf(data, "cannot add certificate to client CA list"); + goto fail; + } + if(!SSL_CTX_add_extra_chain_cert(ctx, x)) { + X509_free(x); + failf(data, "cannot add certificate to certificate chain"); + goto fail; + } + } + } + + cert_done = 1; +fail: + EVP_PKEY_free(pri); + X509_free(x509); + sk_X509_pop_free(ca, X509_free); + if(!cert_done) + return 0; /* failure! */ + break; + } + default: + failf(data, "not supported file type '%s' for certificate", cert_type); + return 0; + } + + if((!key_file) && (!key_blob)) { + key_file = cert_file; + key_blob = cert_blob; + } + else + file_type = do_file_type(key_type); + + switch(file_type) { + case SSL_FILETYPE_PEM: + if(cert_done) + break; + FALLTHROUGH(); + case SSL_FILETYPE_ASN1: + cert_use_result = key_blob ? + SSL_CTX_use_PrivateKey_blob(ctx, key_blob, file_type, key_passwd) : + SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type); + if(cert_use_result != 1) { + failf(data, "unable to set private key file: '%s' type %s", + key_file?key_file:"(memory blob)", key_type?key_type:"PEM"); + return 0; + } + break; + case SSL_FILETYPE_ENGINE: +#ifdef USE_OPENSSL_ENGINE + { + EVP_PKEY *priv_key = NULL; + + /* Implicitly use pkcs11 engine if none was provided and the + * key_file is a PKCS#11 URI */ + if(!data->state.engine) { + if(is_pkcs11_uri(key_file)) { + if(ossl_set_engine(data, "pkcs11") != CURLE_OK) { + return 0; + } + } + } + + if(data->state.engine) { + UI_METHOD *ui_method = + UI_create_method((char *)"curl user interface"); + if(!ui_method) { + failf(data, "unable do create " OSSL_PACKAGE + " user-interface method"); + return 0; + } + UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); + UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL())); + UI_method_set_reader(ui_method, ssl_ui_reader); + UI_method_set_writer(ui_method, ssl_ui_writer); + priv_key = ENGINE_load_private_key(data->state.engine, key_file, + ui_method, + key_passwd); + UI_destroy_method(ui_method); + if(!priv_key) { + failf(data, "failed to load private key from crypto engine"); + return 0; + } + if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) { + failf(data, "unable to set private key"); + EVP_PKEY_free(priv_key); + return 0; + } + EVP_PKEY_free(priv_key); /* we do not need the handle any more... */ + } + else { + failf(data, "crypto engine not set, cannot load private key"); + return 0; + } + } + break; +#else + failf(data, "file type ENG for private key not supported"); + return 0; +#endif + case SSL_FILETYPE_PKCS12: + if(!cert_done) { + failf(data, "file type P12 for private key not supported"); + return 0; + } + break; + default: + failf(data, "not supported file type for private key"); + return 0; + } + + ssl = SSL_new(ctx); + if(!ssl) { + failf(data, "unable to create an SSL structure"); + return 0; + } + + x509 = SSL_get_certificate(ssl); + + /* This version was provided by Evan Jordan and is supposed to not + leak memory as the previous version: */ + if(x509) { + EVP_PKEY *pktmp = X509_get_pubkey(x509); + EVP_PKEY_copy_parameters(pktmp, SSL_get_privatekey(ssl)); + EVP_PKEY_free(pktmp); + } + +#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL) && \ + !defined(OPENSSL_NO_DEPRECATED_3_0) + { + /* If RSA is used, do not check the private key if its flags indicate + * it does not support it. */ + EVP_PKEY *priv_key = SSL_get_privatekey(ssl); + int pktype; +#ifdef HAVE_OPAQUE_EVP_PKEY + pktype = EVP_PKEY_id(priv_key); +#else + pktype = priv_key->type; +#endif + if(pktype == EVP_PKEY_RSA) { + RSA *rsa = EVP_PKEY_get1_RSA(priv_key); + if(RSA_flags(rsa) & RSA_METHOD_FLAG_NO_CHECK) + check_privkey = FALSE; + RSA_free(rsa); /* Decrement reference count */ + } + } +#endif + + SSL_free(ssl); + + /* If we are using DSA, we can copy the parameters from + * the private key */ + + if(check_privkey == TRUE) { + /* Now we know that a key and cert have been set against + * the SSL context */ + if(!SSL_CTX_check_private_key(ctx)) { + failf(data, "Private key does not match the certificate public key"); + return 0; + } + } + } + return 1; +} + +CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, SSL_CTX *ctx, + char *cert_file, + const struct curl_blob *cert_blob, + const char *cert_type, char *key_file, + const struct curl_blob *key_blob, + const char *key_type, char *key_passwd) +{ + int rv = cert_stuff(data, ctx, cert_file, cert_blob, cert_type, key_file, + key_blob, key_type, key_passwd); + if(rv != 1) { + return CURLE_SSL_CERTPROBLEM; + } + + return CURLE_OK; +} + +/* returns non-zero on failure */ +static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) +{ + BIO *bio_out = BIO_new(BIO_s_mem()); + BUF_MEM *biomem; + int rc; + + if(!bio_out) + return 1; /* alloc failed! */ + + rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC); + BIO_get_mem_ptr(bio_out, &biomem); + + if((size_t)biomem->length < size) + size = biomem->length; + else + size--; /* do not overwrite the buffer end */ + + memcpy(buf, biomem->data, size); + buf[size] = 0; + + BIO_free(bio_out); + + return !rc; +} + +/** + * Global SSL init + * + * @retval 0 error initializing SSL + * @retval 1 SSL initialized successfully + */ +static int ossl_init(void) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ + (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL) + const uint64_t flags = +#ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN + /* not present in BoringSSL */ + OPENSSL_INIT_ENGINE_ALL_BUILTIN | +#endif +#ifdef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG + OPENSSL_INIT_NO_LOAD_CONFIG | +#else + OPENSSL_INIT_LOAD_CONFIG | +#endif + 0; + OPENSSL_init_ssl(flags, NULL); +#else + OPENSSL_load_builtin_modules(); + +#ifdef USE_OPENSSL_ENGINE + ENGINE_load_builtin_engines(); +#endif + +/* CONF_MFLAGS_DEFAULT_SECTION was introduced some time between 0.9.8b and + 0.9.8e */ +#ifndef CONF_MFLAGS_DEFAULT_SECTION +#define CONF_MFLAGS_DEFAULT_SECTION 0x0 +#endif + +#ifndef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG + CONF_modules_load_file(NULL, NULL, + CONF_MFLAGS_DEFAULT_SECTION| + CONF_MFLAGS_IGNORE_MISSING_FILE); +#endif + + /* Let's get nice error messages */ + SSL_load_error_strings(); + + /* Init the global ciphers and digests */ + if(!SSLeay_add_ssl_algorithms()) + return 0; + + OpenSSL_add_all_algorithms(); +#endif + + Curl_tls_keylog_open(); + + return 1; +} + +/* Global cleanup */ +static void ossl_cleanup(void) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ + (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL) + /* OpenSSL 1.1 deprecates all these cleanup functions and + turns them into no-ops in OpenSSL 1.0 compatibility mode */ +#else + /* Free ciphers and digests lists */ + EVP_cleanup(); + +#ifdef USE_OPENSSL_ENGINE + /* Free engine list */ + ENGINE_cleanup(); +#endif + + /* Free OpenSSL error strings */ + ERR_free_strings(); + + /* Free thread local error state, destroying hash upon zero refcount */ +#ifdef HAVE_ERR_REMOVE_THREAD_STATE + ERR_remove_thread_state(NULL); +#else + ERR_remove_state(0); +#endif + + /* Free all memory allocated by all configuration modules */ + CONF_modules_free(); + +#ifdef HAVE_SSL_COMP_FREE_COMPRESSION_METHODS + SSL_COMP_free_compression_methods(); +#endif +#endif + + Curl_tls_keylog_close(); +} + +/* Selects an OpenSSL crypto engine + */ +static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine) +{ +#ifdef USE_OPENSSL_ENGINE + ENGINE *e; + +#if OPENSSL_VERSION_NUMBER >= 0x00909000L + e = ENGINE_by_id(engine); +#else + /* avoid memory leak */ + for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) { + const char *e_id = ENGINE_get_id(e); + if(!strcmp(engine, e_id)) + break; + } +#endif + + if(!e) { + failf(data, "SSL Engine '%s' not found", engine); + return CURLE_SSL_ENGINE_NOTFOUND; + } + + if(data->state.engine) { + ENGINE_finish(data->state.engine); + ENGINE_free(data->state.engine); + data->state.engine = NULL; + } + if(!ENGINE_init(e)) { + char buf[256]; + + ENGINE_free(e); + failf(data, "Failed to initialise SSL Engine '%s': %s", + engine, ossl_strerror(ERR_get_error(), buf, sizeof(buf))); + return CURLE_SSL_ENGINE_INITFAILED; + } + data->state.engine = e; + return CURLE_OK; +#else + (void)engine; + failf(data, "SSL Engine not supported"); + return CURLE_SSL_ENGINE_NOTFOUND; +#endif +} + +/* Sets engine as default for all SSL operations + */ +static CURLcode ossl_set_engine_default(struct Curl_easy *data) +{ +#ifdef USE_OPENSSL_ENGINE + if(data->state.engine) { + if(ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) { + infof(data, "set default crypto engine '%s'", + ENGINE_get_id(data->state.engine)); + } + else { + failf(data, "set default crypto engine '%s' failed", + ENGINE_get_id(data->state.engine)); + return CURLE_SSL_ENGINE_SETFAILED; + } + } +#else + (void) data; +#endif + return CURLE_OK; +} + +/* Return list of OpenSSL crypto engine names. + */ +static struct curl_slist *ossl_engines_list(struct Curl_easy *data) +{ + struct curl_slist *list = NULL; +#ifdef USE_OPENSSL_ENGINE + struct curl_slist *beg; + ENGINE *e; + + for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) { + beg = curl_slist_append(list, ENGINE_get_id(e)); + if(!beg) { + curl_slist_free_all(list); + return NULL; + } + list = beg; + } +#endif + (void) data; + return list; +} + +static CURLcode ossl_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + CURLcode result = CURLE_OK; + char buf[1024]; + int nread, err; + unsigned long sslerr; + size_t i; + + DEBUGASSERT(octx); + if(!octx->ssl || cf->shutdown) { + *done = TRUE; + goto out; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + if(!(SSL_get_shutdown(octx->ssl) & SSL_SENT_SHUTDOWN)) { + /* We have not started the shutdown from our side yet. Check + * if the server already sent us one. */ + ERR_clear_error(); + for(i = 0; i < 10; ++i) { + nread = SSL_read(octx->ssl, buf, (int)sizeof(buf)); + CURL_TRC_CF(data, cf, "SSL shutdown not sent, read -> %d", nread); + if(nread <= 0) + break; + } + err = SSL_get_error(octx->ssl, nread); + if(!nread && err == SSL_ERROR_ZERO_RETURN) { + bool input_pending; + /* Yes, it did. */ + if(!send_shutdown) { + CURL_TRC_CF(data, cf, "SSL shutdown received, not sending"); + *done = TRUE; + goto out; + } + else if(!cf->next->cft->is_alive(cf->next, data, &input_pending)) { + /* Server closed the connection after its closy notify. It + * seems not interested to see our close notify, so do not + * send it. We are done. */ + connssl->peer_closed = TRUE; + CURL_TRC_CF(data, cf, "peer closed connection"); + *done = TRUE; + goto out; + } + } + if(send_shutdown && SSL_shutdown(octx->ssl) == 1) { + CURL_TRC_CF(data, cf, "SSL shutdown finished"); + *done = TRUE; + goto out; + } + } + + /* SSL should now have started the shutdown from our side. Since it + * was not complete, we are lacking the close notify from the server. */ + for(i = 0; i < 10; ++i) { + ERR_clear_error(); + nread = SSL_read(octx->ssl, buf, (int)sizeof(buf)); + CURL_TRC_CF(data, cf, "SSL shutdown read -> %d", nread); + if(nread <= 0) + break; + } + if(SSL_get_shutdown(octx->ssl) & SSL_RECEIVED_SHUTDOWN) { + CURL_TRC_CF(data, cf, "SSL shutdown received, finished"); + *done = TRUE; + goto out; + } + err = SSL_get_error(octx->ssl, nread); + switch(err) { + case SSL_ERROR_ZERO_RETURN: /* no more data */ + CURL_TRC_CF(data, cf, "SSL shutdown not received, but closed"); + *done = TRUE; + break; + case SSL_ERROR_NONE: /* just did not get anything */ + case SSL_ERROR_WANT_READ: + /* SSL has send its notify and now wants to read the reply + * from the server. We are not really interested in that. */ + CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive"); + connssl->io_need = CURL_SSL_IO_NEED_RECV; + break; + case SSL_ERROR_WANT_WRITE: + CURL_TRC_CF(data, cf, "SSL shutdown send blocked"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; + break; + default: + /* Server seems to have closed the connection without sending us + * a close notify. */ + sslerr = ERR_get_error(); + CURL_TRC_CF(data, cf, "SSL shutdown, ignore recv error: '%s', errno %d", + (sslerr ? + ossl_strerror(sslerr, buf, sizeof(buf)) : + SSL_ERROR_to_str(err)), + SOCKERRNO); + *done = TRUE; + result = CURLE_OK; + break; + } + +out: + cf->shutdown = (result || *done); + return result; +} + +static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + + (void)data; + DEBUGASSERT(octx); + + if(octx->ssl) { + SSL_free(octx->ssl); + octx->ssl = NULL; + } + if(octx->ssl_ctx) { + SSL_CTX_free(octx->ssl_ctx); + octx->ssl_ctx = NULL; + octx->x509_store_setup = FALSE; + } + if(octx->bio_method) { + ossl_bio_cf_method_free(octx->bio_method); + octx->bio_method = NULL; + } +} + +static void ossl_session_free(void *sessionid, size_t idsize) +{ + /* free the ID */ + (void)idsize; + SSL_SESSION_free(sessionid); +} + +/* + * This function is called when the 'data' struct is going away. Close + * down everything and free all resources! + */ +static void ossl_close_all(struct Curl_easy *data) +{ +#ifdef USE_OPENSSL_ENGINE + if(data->state.engine) { + ENGINE_finish(data->state.engine); + ENGINE_free(data->state.engine); + data->state.engine = NULL; + } +#else + (void)data; +#endif +#if !defined(HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED) && \ + defined(HAVE_ERR_REMOVE_THREAD_STATE) + /* OpenSSL 1.0.1 and 1.0.2 build an error queue that is stored per-thread + so we need to clean it here in case the thread will be killed. All OpenSSL + code should extract the error in association with the error so clearing + this queue here should be harmless at worst. */ + ERR_remove_thread_state(NULL); +#endif +} + +/* ====================================================== */ + +/* + * Match subjectAltName against the hostname. + */ +static bool subj_alt_hostcheck(struct Curl_easy *data, + const char *match_pattern, + size_t matchlen, + const char *hostname, + size_t hostlen, + const char *dispname) +{ +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)dispname; + (void)data; +#endif + if(Curl_cert_hostcheck(match_pattern, matchlen, hostname, hostlen)) { + infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"", + dispname, match_pattern); + return TRUE; + } + return FALSE; +} + +/* Quote from RFC2818 section 3.1 "Server Identity" + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. + + Matching is performed using the matching rules specified by + [RFC2459]. If more than one identity of a given type is present in + the certificate (e.g., more than one dNSName name, a match in any one + of the set is considered acceptable.) Names may contain the wildcard + character * which is considered to match any single domain name + component or component fragment. E.g., *.a.com matches foo.a.com but + not bar.foo.a.com. f*.com matches foo.com but not bar.com. + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. + + This function is now used from ngtcp2 (QUIC) as well. +*/ +CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + struct ssl_peer *peer, X509 *server_cert) +{ + bool matched = FALSE; + int target; /* target type, GEN_DNS or GEN_IPADD */ + size_t addrlen = 0; + STACK_OF(GENERAL_NAME) *altnames; +#ifdef USE_IPV6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + CURLcode result = CURLE_OK; + bool dNSName = FALSE; /* if a dNSName field exists in the cert */ + bool iPAddress = FALSE; /* if an iPAddress field exists in the cert */ + size_t hostlen; + + (void)conn; + hostlen = strlen(peer->hostname); + switch(peer->type) { + case CURL_SSL_PEER_IPV4: + if(!Curl_inet_pton(AF_INET, peer->hostname, &addr)) + return CURLE_PEER_FAILED_VERIFICATION; + target = GEN_IPADD; + addrlen = sizeof(struct in_addr); + break; +#ifdef USE_IPV6 + case CURL_SSL_PEER_IPV6: + if(!Curl_inet_pton(AF_INET6, peer->hostname, &addr)) + return CURLE_PEER_FAILED_VERIFICATION; + target = GEN_IPADD; + addrlen = sizeof(struct in6_addr); + break; +#endif + case CURL_SSL_PEER_DNS: + target = GEN_DNS; + break; + default: + DEBUGASSERT(0); + failf(data, "unexpected ssl peer type: %d", peer->type); + return CURLE_PEER_FAILED_VERIFICATION; + } + + /* get a "list" of alternative names */ + altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL); + + if(altnames) { +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) + size_t numalts; + size_t i; +#else + int numalts; + int i; +#endif + bool dnsmatched = FALSE; + bool ipmatched = FALSE; + + /* get amount of alternatives, RFC2459 claims there MUST be at least + one, but we do not depend on it... */ + numalts = sk_GENERAL_NAME_num(altnames); + + /* loop through all alternatives - until a dnsmatch */ + for(i = 0; (i < numalts) && !dnsmatched; i++) { + /* get a handle to alternative name number i */ + const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i); + + if(check->type == GEN_DNS) + dNSName = TRUE; + else if(check->type == GEN_IPADD) + iPAddress = TRUE; + + /* only check alternatives of the same type the target is */ + if(check->type == target) { + /* get data and length */ + const char *altptr = (char *)ASN1_STRING_get0_data(check->d.ia5); + size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5); + + switch(target) { + case GEN_DNS: /* name/pattern comparison */ + /* The OpenSSL manpage explicitly says: "In general it cannot be + assumed that the data returned by ASN1_STRING_data() is null + terminated or does not contain embedded nulls." But also that + "The actual format of the data will depend on the actual string + type itself: for example for an IA5String the data will be ASCII" + + It has been however verified that in 0.9.6 and 0.9.7, IA5String + is always null-terminated. + */ + if((altlen == strlen(altptr)) && + /* if this is not true, there was an embedded zero in the name + string and we cannot match it. */ + subj_alt_hostcheck(data, altptr, altlen, + peer->hostname, hostlen, + peer->dispname)) { + dnsmatched = TRUE; + } + break; + + case GEN_IPADD: /* IP address comparison */ + /* compare alternative IP address if the data chunk is the same size + our server IP address is */ + if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) { + ipmatched = TRUE; + infof(data, + " subjectAltName: host \"%s\" matched cert's IP address!", + peer->dispname); + } + break; + } + } + } + GENERAL_NAMES_free(altnames); + + if(dnsmatched || ipmatched) + matched = TRUE; + } + + if(matched) + /* an alternative name matched */ + ; + else if(dNSName || iPAddress) { + const char *tname = (peer->type == CURL_SSL_PEER_DNS) ? "hostname" : + (peer->type == CURL_SSL_PEER_IPV4) ? + "ipv4 address" : "ipv6 address"; + infof(data, " subjectAltName does not match %s %s", tname, peer->dispname); + failf(data, "SSL: no alternative certificate subject name matches " + "target %s '%s'", tname, peer->dispname); + result = CURLE_PEER_FAILED_VERIFICATION; + } + else { + /* we have to look to the last occurrence of a commonName in the + distinguished one to get the most significant one. */ + int i = -1; + unsigned char *peer_CN = NULL; + int peerlen = 0; + + /* The following is done because of a bug in 0.9.6b */ + X509_NAME *name = X509_get_subject_name(server_cert); + if(name) { + int j; + while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) + i = j; + } + + /* we have the name entry and we will now convert this to a string + that we can use for comparison. Doing this we support BMPstring, + UTF8, etc. */ + + if(i >= 0) { + ASN1_STRING *tmp = + X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); + + /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input + is already UTF-8 encoded. We check for this case and copy the raw + string manually to avoid the problem. This code can be made + conditional in the future when OpenSSL has been fixed. */ + if(tmp) { + if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { + peerlen = ASN1_STRING_length(tmp); + if(peerlen >= 0) { + peer_CN = OPENSSL_malloc(peerlen + 1); + if(peer_CN) { + memcpy(peer_CN, ASN1_STRING_get0_data(tmp), peerlen); + peer_CN[peerlen] = '\0'; + } + else + result = CURLE_OUT_OF_MEMORY; + } + } + else /* not a UTF8 name */ + peerlen = ASN1_STRING_to_UTF8(&peer_CN, tmp); + + if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != peerlen)) { + /* there was a terminating zero before the end of string, this + cannot match and we return failure! */ + failf(data, "SSL: illegal cert name field"); + result = CURLE_PEER_FAILED_VERIFICATION; + } + } + } + + if(result) + /* error already detected, pass through */ + ; + else if(!peer_CN) { + failf(data, + "SSL: unable to obtain common name from peer certificate"); + result = CURLE_PEER_FAILED_VERIFICATION; + } + else if(!Curl_cert_hostcheck((const char *)peer_CN, + peerlen, peer->hostname, hostlen)) { + failf(data, "SSL: certificate subject name '%s' does not match " + "target hostname '%s'", peer_CN, peer->dispname); + result = CURLE_PEER_FAILED_VERIFICATION; + } + else { + infof(data, " common name: %s (matched)", peer_CN); + } + if(peer_CN) + OPENSSL_free(peer_CN); + } + + return result; +} + +#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ + !defined(OPENSSL_NO_OCSP) +static CURLcode verifystatus(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ossl_ctx *octx) +{ + int i, ocsp_status; +#if defined(OPENSSL_IS_AWSLC) + const uint8_t *status; +#else + unsigned char *status; +#endif + const unsigned char *p; + CURLcode result = CURLE_OK; + OCSP_RESPONSE *rsp = NULL; + OCSP_BASICRESP *br = NULL; + X509_STORE *st = NULL; + STACK_OF(X509) *ch = NULL; + X509 *cert; + OCSP_CERTID *id = NULL; + int cert_status, crl_reason; + ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; + int ret; + long len; + + (void)cf; + DEBUGASSERT(octx); + + len = (long)SSL_get_tlsext_status_ocsp_resp(octx->ssl, &status); + + if(!status) { + failf(data, "No OCSP response received"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + p = status; + rsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if(!rsp) { + failf(data, "Invalid OCSP response"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + ocsp_status = OCSP_response_status(rsp); + if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + failf(data, "Invalid OCSP response status: %s (%d)", + OCSP_response_status_str(ocsp_status), ocsp_status); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + br = OCSP_response_get1_basic(rsp); + if(!br) { + failf(data, "Invalid OCSP response"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + ch = SSL_get_peer_cert_chain(octx->ssl); + if(!ch) { + failf(data, "Could not get peer certificate chain"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + st = SSL_CTX_get_cert_store(octx->ssl_ctx); + +#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER <= 0x2040200fL)) + /* The authorized responder cert in the OCSP response MUST be signed by the + peer cert's issuer (see RFC6960 section 4.2.2.2). If that is a root cert, + no problem, but if it is an intermediate cert OpenSSL has a bug where it + expects this issuer to be present in the chain embedded in the OCSP + response. So we add it if necessary. */ + + /* First make sure the peer cert chain includes both a peer and an issuer, + and the OCSP response contains a responder cert. */ + if(sk_X509_num(ch) >= 2 && sk_X509_num(br->certs) >= 1) { + X509 *responder = sk_X509_value(br->certs, sk_X509_num(br->certs) - 1); + + /* Find issuer of responder cert and add it to the OCSP response chain */ + for(i = 0; i < sk_X509_num(ch); i++) { + X509 *issuer = sk_X509_value(ch, i); + if(X509_check_issued(issuer, responder) == X509_V_OK) { + if(!OCSP_basic_add1_cert(br, issuer)) { + failf(data, "Could not add issuer cert to OCSP response"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + } + } + } +#endif + + if(OCSP_basic_verify(br, ch, st, 0) <= 0) { + failf(data, "OCSP response verification failed"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + /* Compute the certificate's ID */ + cert = SSL_get1_peer_certificate(octx->ssl); + if(!cert) { + failf(data, "Error getting peer certificate"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + for(i = 0; i < (int)sk_X509_num(ch); i++) { + X509 *issuer = sk_X509_value(ch, (ossl_valsize_t)i); + if(X509_check_issued(issuer, cert) == X509_V_OK) { + id = OCSP_cert_to_id(EVP_sha1(), cert, issuer); + break; + } + } + X509_free(cert); + + if(!id) { + failf(data, "Error computing OCSP ID"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + /* Find the single OCSP response corresponding to the certificate ID */ + ret = OCSP_resp_find_status(br, id, &cert_status, &crl_reason, &rev, + &thisupd, &nextupd); + OCSP_CERTID_free(id); + if(ret != 1) { + failf(data, "Could not find certificate ID in OCSP response"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + /* Validate the corresponding single OCSP response */ + if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) { + failf(data, "OCSP response has expired"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + infof(data, "SSL certificate status: %s (%d)", + OCSP_cert_status_str(cert_status), cert_status); + + switch(cert_status) { + case V_OCSP_CERTSTATUS_GOOD: + break; + + case V_OCSP_CERTSTATUS_REVOKED: + result = CURLE_SSL_INVALIDCERTSTATUS; + failf(data, "SSL certificate revocation reason: %s (%d)", + OCSP_crl_reason_str(crl_reason), crl_reason); + goto end; + + case V_OCSP_CERTSTATUS_UNKNOWN: + default: + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + +end: + if(br) + OCSP_BASICRESP_free(br); + OCSP_RESPONSE_free(rsp); + + return result; +} +#endif + +#endif /* USE_OPENSSL */ + +/* The SSL_CTRL_SET_MSG_CALLBACK does not exist in ancient OpenSSL versions + and thus this cannot be done there. */ +#ifdef SSL_CTRL_SET_MSG_CALLBACK + +static const char *ssl_msg_type(int ssl_ver, int msg) +{ +#ifdef SSL2_VERSION_MAJOR + if(ssl_ver == SSL2_VERSION_MAJOR) { + switch(msg) { + case SSL2_MT_ERROR: + return "Error"; + case SSL2_MT_CLIENT_HELLO: + return "Client hello"; + case SSL2_MT_CLIENT_MASTER_KEY: + return "Client key"; + case SSL2_MT_CLIENT_FINISHED: + return "Client finished"; + case SSL2_MT_SERVER_HELLO: + return "Server hello"; + case SSL2_MT_SERVER_VERIFY: + return "Server verify"; + case SSL2_MT_SERVER_FINISHED: + return "Server finished"; + case SSL2_MT_REQUEST_CERTIFICATE: + return "Request CERT"; + case SSL2_MT_CLIENT_CERTIFICATE: + return "Client CERT"; + } + } + else +#endif + if(ssl_ver == SSL3_VERSION_MAJOR) { + switch(msg) { + case SSL3_MT_HELLO_REQUEST: + return "Hello request"; + case SSL3_MT_CLIENT_HELLO: + return "Client hello"; + case SSL3_MT_SERVER_HELLO: + return "Server hello"; +#ifdef SSL3_MT_NEWSESSION_TICKET + case SSL3_MT_NEWSESSION_TICKET: + return "Newsession Ticket"; +#endif + case SSL3_MT_CERTIFICATE: + return "Certificate"; + case SSL3_MT_SERVER_KEY_EXCHANGE: + return "Server key exchange"; + case SSL3_MT_CLIENT_KEY_EXCHANGE: + return "Client key exchange"; + case SSL3_MT_CERTIFICATE_REQUEST: + return "Request CERT"; + case SSL3_MT_SERVER_DONE: + return "Server finished"; + case SSL3_MT_CERTIFICATE_VERIFY: + return "CERT verify"; + case SSL3_MT_FINISHED: + return "Finished"; +#ifdef SSL3_MT_CERTIFICATE_STATUS + case SSL3_MT_CERTIFICATE_STATUS: + return "Certificate Status"; +#endif +#ifdef SSL3_MT_ENCRYPTED_EXTENSIONS + case SSL3_MT_ENCRYPTED_EXTENSIONS: + return "Encrypted Extensions"; +#endif +#ifdef SSL3_MT_SUPPLEMENTAL_DATA + case SSL3_MT_SUPPLEMENTAL_DATA: + return "Supplemental data"; +#endif +#ifdef SSL3_MT_END_OF_EARLY_DATA + case SSL3_MT_END_OF_EARLY_DATA: + return "End of early data"; +#endif +#ifdef SSL3_MT_KEY_UPDATE + case SSL3_MT_KEY_UPDATE: + return "Key update"; +#endif +#ifdef SSL3_MT_NEXT_PROTO + case SSL3_MT_NEXT_PROTO: + return "Next protocol"; +#endif +#ifdef SSL3_MT_MESSAGE_HASH + case SSL3_MT_MESSAGE_HASH: + return "Message hash"; +#endif + } + } + return "Unknown"; +} + +static const char *tls_rt_type(int type) +{ + switch(type) { +#ifdef SSL3_RT_HEADER + case SSL3_RT_HEADER: + return "TLS header"; +#endif + case SSL3_RT_CHANGE_CIPHER_SPEC: + return "TLS change cipher"; + case SSL3_RT_ALERT: + return "TLS alert"; + case SSL3_RT_HANDSHAKE: + return "TLS handshake"; + case SSL3_RT_APPLICATION_DATA: + return "TLS app data"; + default: + return "TLS Unknown"; + } +} + +/* + * Our callback from the SSL/TLS layers. + */ +static void ossl_trace(int direction, int ssl_ver, int content_type, + const void *buf, size_t len, SSL *ssl, + void *userp) +{ + const char *verstr = "???"; + struct Curl_cfilter *cf = userp; + struct Curl_easy *data = NULL; + char unknown[32]; + + if(!cf) + return; + data = CF_DATA_CURRENT(cf); + if(!data || !data->set.fdebug || (direction && direction != 1)) + return; + + switch(ssl_ver) { +#ifdef SSL2_VERSION /* removed in recent versions */ + case SSL2_VERSION: + verstr = "SSLv2"; + break; +#endif +#ifdef SSL3_VERSION + case SSL3_VERSION: + verstr = "SSLv3"; + break; +#endif + case TLS1_VERSION: + verstr = "TLSv1.0"; + break; +#ifdef TLS1_1_VERSION + case TLS1_1_VERSION: + verstr = "TLSv1.1"; + break; +#endif +#ifdef TLS1_2_VERSION + case TLS1_2_VERSION: + verstr = "TLSv1.2"; + break; +#endif +#ifdef TLS1_3_VERSION + case TLS1_3_VERSION: + verstr = "TLSv1.3"; + break; +#endif + case 0: + break; + default: + msnprintf(unknown, sizeof(unknown), "(%x)", ssl_ver); + verstr = unknown; + break; + } + + /* Log progress for interesting records only (like Handshake or Alert), skip + * all raw record headers (content_type == SSL3_RT_HEADER or ssl_ver == 0). + * For TLS 1.3, skip notification of the decrypted inner Content-Type. + */ + if(ssl_ver +#ifdef SSL3_RT_HEADER + && content_type != SSL3_RT_HEADER +#endif +#ifdef SSL3_RT_INNER_CONTENT_TYPE + && content_type != SSL3_RT_INNER_CONTENT_TYPE +#endif + ) { + const char *msg_name, *tls_rt_name; + char ssl_buf[1024]; + int msg_type, txt_len; + + /* the info given when the version is zero is not that useful for us */ + + ssl_ver >>= 8; /* check the upper 8 bits only below */ + + /* SSLv2 does not seem to have TLS record-type headers, so OpenSSL + * always pass-up content-type as 0. But the interesting message-type + * is at 'buf[0]'. + */ + if(ssl_ver == SSL3_VERSION_MAJOR && content_type) + tls_rt_name = tls_rt_type(content_type); + else + tls_rt_name = ""; + + if(content_type == SSL3_RT_CHANGE_CIPHER_SPEC) { + msg_type = *(char *)buf; + msg_name = "Change cipher spec"; + } + else if(content_type == SSL3_RT_ALERT) { + msg_type = (((char *)buf)[0] << 8) + ((char *)buf)[1]; + msg_name = SSL_alert_desc_string_long(msg_type); + } + else { + msg_type = *(char *)buf; + msg_name = ssl_msg_type(ssl_ver, msg_type); + } + + txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), + "%s (%s), %s, %s (%d):\n", + verstr, direction?"OUT":"IN", + tls_rt_name, msg_name, msg_type); + if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) { + Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len); + } + } + + Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT : + CURLINFO_SSL_DATA_IN, (char *)buf, len); + (void) ssl; +} +#endif + +#ifdef USE_OPENSSL +/* ====================================================== */ + +/* Check for OpenSSL 1.0.2 which has ALPN support. */ +#undef HAS_ALPN +#if OPENSSL_VERSION_NUMBER >= 0x10002000L \ + && !defined(OPENSSL_NO_TLSEXT) +# define HAS_ALPN 1 +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ +static CURLcode +ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + /* first, TLS min version... */ + long curl_ssl_version_min = conn_config->version; + long curl_ssl_version_max; + + /* convert curl min SSL version option to OpenSSL constant */ +#if (defined(OPENSSL_IS_BORINGSSL) || \ + defined(OPENSSL_IS_AWSLC) || \ + defined(LIBRESSL_VERSION_NUMBER)) + uint16_t ossl_ssl_version_min = 0; + uint16_t ossl_ssl_version_max = 0; +#else + long ossl_ssl_version_min = 0; + long ossl_ssl_version_max = 0; +#endif + switch(curl_ssl_version_min) { + case CURL_SSLVERSION_TLSv1: /* TLS 1.x */ + case CURL_SSLVERSION_TLSv1_0: + ossl_ssl_version_min = TLS1_VERSION; + break; + case CURL_SSLVERSION_TLSv1_1: + ossl_ssl_version_min = TLS1_1_VERSION; + break; + case CURL_SSLVERSION_TLSv1_2: + ossl_ssl_version_min = TLS1_2_VERSION; + break; + case CURL_SSLVERSION_TLSv1_3: +#ifdef TLS1_3_VERSION + ossl_ssl_version_min = TLS1_3_VERSION; + break; +#else + return CURLE_NOT_BUILT_IN; +#endif + } + + /* CURL_SSLVERSION_DEFAULT means that no option was selected. + We do not want to pass 0 to SSL_CTX_set_min_proto_version as + it would enable all versions down to the lowest supported by + the library. + So we skip this, and stay with the library default + */ + if(curl_ssl_version_min != CURL_SSLVERSION_DEFAULT) { + if(!SSL_CTX_set_min_proto_version(ctx, ossl_ssl_version_min)) { + return CURLE_SSL_CONNECT_ERROR; + } + } + + /* ... then, TLS max version */ + curl_ssl_version_max = (long)conn_config->version_max; + + /* convert curl max SSL version option to OpenSSL constant */ + switch(curl_ssl_version_max) { + case CURL_SSLVERSION_MAX_TLSv1_0: + ossl_ssl_version_max = TLS1_VERSION; + break; + case CURL_SSLVERSION_MAX_TLSv1_1: + ossl_ssl_version_max = TLS1_1_VERSION; + break; + case CURL_SSLVERSION_MAX_TLSv1_2: + ossl_ssl_version_max = TLS1_2_VERSION; + break; +#ifdef TLS1_3_VERSION + case CURL_SSLVERSION_MAX_TLSv1_3: + ossl_ssl_version_max = TLS1_3_VERSION; + break; +#endif + case CURL_SSLVERSION_MAX_NONE: /* none selected */ + case CURL_SSLVERSION_MAX_DEFAULT: /* max selected */ + default: + /* SSL_CTX_set_max_proto_version states that: + setting the maximum to 0 will enable + protocol versions up to the highest version + supported by the library */ + ossl_ssl_version_max = 0; + break; + } + + if(!SSL_CTX_set_max_proto_version(ctx, ossl_ssl_version_max)) { + return CURLE_SSL_CONNECT_ERROR; + } + + return CURLE_OK; +} +#endif + +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +typedef uint32_t ctx_option_t; +#elif OPENSSL_VERSION_NUMBER >= 0x30000000L +typedef uint64_t ctx_option_t; +#elif OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) +typedef unsigned long ctx_option_t; +#else +typedef long ctx_option_t; +#endif + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */ +static CURLcode +ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + long ssl_version = conn_config->version; + long ssl_version_max = conn_config->version_max; + + (void) data; /* In case it is unused. */ + + switch(ssl_version) { + case CURL_SSLVERSION_TLSv1_3: +#ifdef TLS1_3_VERSION + { + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + DEBUGASSERT(octx); + SSL_CTX_set_max_proto_version(octx->ssl_ctx, TLS1_3_VERSION); + *ctx_options |= SSL_OP_NO_TLSv1_2; + } +#else + (void)ctx_options; + failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); + return CURLE_NOT_BUILT_IN; +#endif + FALLTHROUGH(); + case CURL_SSLVERSION_TLSv1_2: +#if OPENSSL_VERSION_NUMBER >= 0x1000100FL + *ctx_options |= SSL_OP_NO_TLSv1_1; +#else + failf(data, OSSL_PACKAGE " was built without TLS 1.2 support"); + return CURLE_NOT_BUILT_IN; +#endif + FALLTHROUGH(); + case CURL_SSLVERSION_TLSv1_1: +#if OPENSSL_VERSION_NUMBER >= 0x1000100FL + *ctx_options |= SSL_OP_NO_TLSv1; +#else + failf(data, OSSL_PACKAGE " was built without TLS 1.1 support"); + return CURLE_NOT_BUILT_IN; +#endif + FALLTHROUGH(); + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1: + break; + } + + switch(ssl_version_max) { + case CURL_SSLVERSION_MAX_TLSv1_0: +#if OPENSSL_VERSION_NUMBER >= 0x1000100FL + *ctx_options |= SSL_OP_NO_TLSv1_1; +#endif + FALLTHROUGH(); + case CURL_SSLVERSION_MAX_TLSv1_1: +#if OPENSSL_VERSION_NUMBER >= 0x1000100FL + *ctx_options |= SSL_OP_NO_TLSv1_2; +#endif + FALLTHROUGH(); + case CURL_SSLVERSION_MAX_TLSv1_2: +#ifdef TLS1_3_VERSION + *ctx_options |= SSL_OP_NO_TLSv1_3; +#endif + break; + case CURL_SSLVERSION_MAX_TLSv1_3: +#ifdef TLS1_3_VERSION + break; +#else + failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); + return CURLE_NOT_BUILT_IN; +#endif + } + return CURLE_OK; +} +#endif + +CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + SSL_SESSION *session) +{ + const struct ssl_config_data *config; + CURLcode result = CURLE_OK; + + if(!cf || !data) + goto out; + + config = Curl_ssl_cf_get_config(cf, data); + if(config->primary.cache_session) { + + Curl_ssl_sessionid_lock(data); + result = Curl_ssl_set_sessionid(cf, data, peer, session, 0, + ossl_session_free); + session = NULL; /* call has taken ownership */ + Curl_ssl_sessionid_unlock(data); + } + +out: + if(session) + ossl_session_free(session, 0); + return result; +} + +/* The "new session" callback must return zero if the session can be removed + * or non-zero if the session has been put into the session cache. + */ +static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) +{ + struct Curl_cfilter *cf; + struct Curl_easy *data; + struct ssl_connect_data *connssl; + + cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); + connssl = cf? cf->ctx : NULL; + data = connssl? CF_DATA_CURRENT(cf) : NULL; + Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid); + return 1; +} + +static CURLcode load_cacert_from_memory(X509_STORE *store, + const struct curl_blob *ca_info_blob) +{ + /* these need to be freed at the end */ + BIO *cbio = NULL; + STACK_OF(X509_INFO) *inf = NULL; + + /* everything else is just a reference */ + int i, count = 0; + X509_INFO *itmp = NULL; + + if(ca_info_blob->len > (size_t)INT_MAX) + return CURLE_SSL_CACERT_BADFILE; + + cbio = BIO_new_mem_buf(ca_info_blob->data, (int)ca_info_blob->len); + if(!cbio) + return CURLE_OUT_OF_MEMORY; + + inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL); + if(!inf) { + BIO_free(cbio); + return CURLE_SSL_CACERT_BADFILE; + } + + /* add each entry from PEM file to x509_store */ + for(i = 0; i < (int)sk_X509_INFO_num(inf); ++i) { + itmp = sk_X509_INFO_value(inf, (ossl_valsize_t)i); + if(itmp->x509) { + if(X509_STORE_add_cert(store, itmp->x509)) { + ++count; + } + else { + /* set count to 0 to return an error */ + count = 0; + break; + } + } + if(itmp->crl) { + if(X509_STORE_add_crl(store, itmp->crl)) { + ++count; + } + else { + /* set count to 0 to return an error */ + count = 0; + break; + } + } + } + + sk_X509_INFO_pop_free(inf, X509_INFO_free); + BIO_free(cbio); + + /* if we did not end up importing anything, treat that as an error */ + return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE; +} + +#if defined(USE_WIN32_CRYPTO) +static CURLcode import_windows_cert_store(struct Curl_easy *data, + const char *name, + X509_STORE *store, + bool *imported) +{ + CURLcode result = CURLE_OK; + HCERTSTORE hStore; + + *imported = false; + + hStore = CertOpenSystemStoreA(0, name); + if(hStore) { + PCCERT_CONTEXT pContext = NULL; + /* The array of enhanced key usage OIDs will vary per certificate and + is declared outside of the loop so that rather than malloc/free each + iteration we can grow it with realloc, when necessary. */ + CERT_ENHKEY_USAGE *enhkey_usage = NULL; + DWORD enhkey_usage_size = 0; + + /* This loop makes a best effort to import all valid certificates from + the MS root store. If a certificate cannot be imported it is + skipped. 'result' is used to store only hard-fail conditions (such + as out of memory) that cause an early break. */ + result = CURLE_OK; + for(;;) { + X509 *x509; + FILETIME now; + BYTE key_usage[2]; + DWORD req_size; + const unsigned char *encoded_cert; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + char cert_name[256]; +#endif + + pContext = CertEnumCertificatesInStore(hStore, pContext); + if(!pContext) + break; + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, + NULL, cert_name, sizeof(cert_name))) { + strcpy(cert_name, "Unknown"); + } + infof(data, "SSL: Checking cert \"%s\"", cert_name); +#endif + encoded_cert = (const unsigned char *)pContext->pbCertEncoded; + if(!encoded_cert) + continue; + + GetSystemTimeAsFileTime(&now); + if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || + CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) + continue; + + /* If key usage exists check for signing attribute */ + if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, + pContext->pCertInfo, + key_usage, sizeof(key_usage))) { + if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) + continue; + } + else if(GetLastError()) + continue; + + /* If enhanced key usage exists check for server auth attribute. + * + * Note "In a Microsoft environment, a certificate might also have + * EKU extended properties that specify valid uses for the + * certificate." The call below checks both, and behavior varies + * depending on what is found. For more details see + * CertGetEnhancedKeyUsage doc. + */ + if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { + if(req_size && req_size > enhkey_usage_size) { + void *tmp = realloc(enhkey_usage, req_size); + + if(!tmp) { + failf(data, "SSL: Out of memory allocating for OID list"); + result = CURLE_OUT_OF_MEMORY; + break; + } + + enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; + enhkey_usage_size = req_size; + } + + if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { + if(!enhkey_usage->cUsageIdentifier) { + /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate + is good for all uses. If it returns zero, the certificate + has no valid uses." */ + if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) + continue; + } + else { + DWORD i; + bool found = false; + + for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { + if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, + enhkey_usage->rgpszUsageIdentifier[i])) { + found = true; + break; + } + } + + if(!found) + continue; + } + } + else + continue; + } + else + continue; + + x509 = d2i_X509(NULL, &encoded_cert, (long)pContext->cbCertEncoded); + if(!x509) + continue; + + /* Try to import the certificate. This may fail for legitimate + reasons such as duplicate certificate, which is allowed by MS but + not OpenSSL. */ + if(X509_STORE_add_cert(store, x509) == 1) { +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + infof(data, "SSL: Imported cert \"%s\"", cert_name); +#endif + *imported = true; + } + X509_free(x509); + } + + free(enhkey_usage); + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + if(result) + return result; + } + + return result; +} +#endif + +static CURLcode populate_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + X509_STORE *store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_OK; + X509_LOOKUP *lookup = NULL; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : conn_config->CAfile); + const char * const ssl_capath = conn_config->CApath; + const char * const ssl_crlfile = ssl_config->primary.CRLfile; + const bool verifypeer = conn_config->verifypeer; + bool imported_native_ca = false; + bool imported_ca_info_blob = false; + + CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d", + ssl_cafile? ssl_cafile : "none", !!ca_info_blob); + if(!store) + return CURLE_OUT_OF_MEMORY; + + if(verifypeer) { +#if defined(USE_WIN32_CRYPTO) + /* Import certificates from the Windows root certificate store if + requested. + https://stackoverflow.com/questions/9507184/ + https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 + https://datatracker.ietf.org/doc/html/rfc5280 */ + if(ssl_config->native_ca_store) { + const char *storeNames[] = { + "ROOT", /* Trusted Root Certification Authorities */ + "CA" /* Intermediate Certification Authorities */ + }; + size_t i; + for(i = 0; i < ARRAYSIZE(storeNames); ++i) { + bool imported = false; + result = import_windows_cert_store(data, storeNames[i], store, + &imported); + if(result) + return result; + if(imported) { + infof(data, "successfully imported Windows %s store", storeNames[i]); + imported_native_ca = true; + } + else + infof(data, "error importing Windows %s store, continuing anyway", + storeNames[i]); + } + } +#endif + if(ca_info_blob) { + result = load_cacert_from_memory(store, ca_info_blob); + if(result) { + failf(data, "error importing CA certificate blob"); + return result; + } + else { + imported_ca_info_blob = true; + infof(data, "successfully imported CA certificate blob"); + } + } + + if(ssl_cafile || ssl_capath) { +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) + /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ + if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate file: %s", ssl_cafile); + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "error setting certificate file, continuing anyway"); + } + if(ssl_capath && !X509_STORE_load_path(store, ssl_capath)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate path: %s", ssl_capath); + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "error setting certificate path, continuing anyway"); + } +#else + /* tell OpenSSL where to find CA certificates that are used to verify the + server's certificate. */ + if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + else { + infof(data, "error setting certificate verify locations," + " continuing anyway"); + } + } +#endif + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } + +#ifdef CURL_CA_FALLBACK + if(!ssl_cafile && !ssl_capath && + !imported_native_ca && !imported_ca_info_blob) { + /* verifying the peer without any CA certificates will not + work so use OpenSSL's built-in default as fallback */ + X509_STORE_set_default_paths(store); + } +#endif + } + + if(ssl_crlfile) { + /* tell OpenSSL where to find CRL file that is used to check certificate + * revocation */ + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if(!lookup || + (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { + failf(data, "error loading CRL file: %s", ssl_crlfile); + return CURLE_SSL_CRL_BADFILE; + } + /* Everything is fine. */ + infof(data, "successfully loaded CRL file:"); + X509_STORE_set_flags(store, + X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); + + infof(data, " CRLfile: %s", ssl_crlfile); + } + + if(verifypeer) { + /* Try building a chain using issuers in the trusted store first to avoid + problems with server-sent legacy intermediates. Newer versions of + OpenSSL do alternate chain checking by default but we do not know how to + determine that in a reliable manner. + https://web.archive.org/web/20190422050538/ + rt.openssl.org/Ticket/Display.html?id=3621 + */ +#if defined(X509_V_FLAG_TRUSTED_FIRST) + X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); +#endif +#ifdef X509_V_FLAG_PARTIAL_CHAIN + if(!ssl_config->no_partialchain && !ssl_crlfile) { + /* Have intermediate certificates in the trust store be treated as + trust-anchors, in the same way as self-signed root CA certificates + are. This allows users to verify servers using the intermediate cert + only, instead of needing the whole chain. + + Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we + cannot do partial chains with a CRL check. + */ + X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN); + } +#endif + } + + return result; +} + +#if defined(HAVE_SSL_X509_STORE_SHARE) + +/* key to use at `multi->proto_hash` */ +#define MPROTO_OSSL_X509_KEY "tls:ossl:x509:share" + +struct ossl_x509_share { + char *CAfile; /* CAfile path used to generate X509 store */ + X509_STORE *store; /* cached X509 store or NULL if none */ + struct curltime time; /* when the cached store was created */ +}; + +static void oss_x509_share_free(void *key, size_t key_len, void *p) +{ + struct ossl_x509_share *share = p; + DEBUGASSERT(key_len == (sizeof(MPROTO_OSSL_X509_KEY)-1)); + DEBUGASSERT(!memcmp(MPROTO_OSSL_X509_KEY, key, key_len)); + (void)key; + (void)key_len; + if(share->store) { + X509_STORE_free(share->store); + } + free(share->CAfile); + free(share); +} + +static bool +cached_x509_store_expired(const struct Curl_easy *data, + const struct ossl_x509_share *mb) +{ + const struct ssl_general_config *cfg = &data->set.general_ssl; + if(cfg->ca_cache_timeout < 0) + return FALSE; + else { + struct curltime now = Curl_now(); + timediff_t elapsed_ms = Curl_timediff(now, mb->time); + timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + + return elapsed_ms >= timeout_ms; + } +} + +static bool +cached_x509_store_different(struct Curl_cfilter *cf, + const struct ossl_x509_share *mb) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!mb->CAfile || !conn_config->CAfile) + return mb->CAfile != conn_config->CAfile; + + return strcmp(mb->CAfile, conn_config->CAfile); +} + +static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct Curl_multi *multi = data->multi; + struct ossl_x509_share *share; + X509_STORE *store = NULL; + + DEBUGASSERT(multi); + share = multi? Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_OSSL_X509_KEY, + sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL; + if(share && share->store && + !cached_x509_store_expired(data, share) && + !cached_x509_store_different(cf, share)) { + store = share->store; + } + + return store; +} + +static void set_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + X509_STORE *store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi; + struct ossl_x509_share *share; + + DEBUGASSERT(multi); + if(!multi) + return; + share = Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_OSSL_X509_KEY, + sizeof(MPROTO_OSSL_X509_KEY)-1); + + if(!share) { + share = calloc(1, sizeof(*share)); + if(!share) + return; + if(!Curl_hash_add2(&multi->proto_hash, + (void *)MPROTO_OSSL_X509_KEY, + sizeof(MPROTO_OSSL_X509_KEY)-1, + share, oss_x509_share_free)) { + free(share); + return; + } + } + + if(X509_STORE_up_ref(store)) { + char *CAfile = NULL; + + if(conn_config->CAfile) { + CAfile = strdup(conn_config->CAfile); + if(!CAfile) { + X509_STORE_free(store); + return; + } + } + + if(share->store) { + X509_STORE_free(share->store); + free(share->CAfile); + } + + share->time = Curl_now(); + share->store = store; + share->CAfile = CAfile; + } +} + +CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + SSL_CTX *ssl_ctx) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_OK; + X509_STORE *cached_store; + bool cache_criteria_met; + + /* Consider the X509 store cacheable if it comes exclusively from a CAfile, + or no source is provided and we are falling back to OpenSSL's built-in + default. */ + cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) && + conn_config->verifypeer && + !conn_config->CApath && + !conn_config->ca_info_blob && + !ssl_config->primary.CRLfile && + !ssl_config->native_ca_store; + + cached_store = get_cached_x509_store(cf, data); + if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) { + SSL_CTX_set_cert_store(ssl_ctx, cached_store); + } + else { + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); + + result = populate_x509_store(cf, data, store); + if(result == CURLE_OK && cache_criteria_met) { + set_cached_x509_store(cf, data, store); + } + } + + return result; +} +#else /* HAVE_SSL_X509_STORE_SHARE */ +CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + SSL_CTX *ssl_ctx) +{ + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); + + return populate_x509_store(cf, data, store); +} +#endif /* HAVE_SSL_X509_STORE_SHARE */ + +CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + int transport, /* TCP or QUIC */ + const unsigned char *alpn, size_t alpn_len, + Curl_ossl_ctx_setup_cb *cb_setup, + void *cb_user_data, + Curl_ossl_new_session_cb *cb_new_session, + void *ssl_user_data) +{ + CURLcode result = CURLE_OK; + const char *ciphers; + SSL_METHOD_QUAL SSL_METHOD *req_method = NULL; + ctx_option_t ctx_options = 0; + void *ssl_sessionid = NULL; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + const long int ssl_version_min = conn_config->version; + char * const ssl_cert = ssl_config->primary.clientcert; + const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; + const char * const ssl_cert_type = ssl_config->cert_type; + const bool verifypeer = conn_config->verifypeer; + char error_buffer[256]; + + /* Make funny stuff to get random input */ + result = ossl_seed(data); + if(result) + return result; + + ssl_config->certverifyresult = !X509_V_OK; + + switch(transport) { + case TRNSPRT_TCP: + /* check to see if we have been told to use an explicit SSL/TLS version */ + switch(ssl_version_min) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + case CURL_SSLVERSION_TLSv1_3: + /* it will be handled later with the context options */ + #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + req_method = TLS_client_method(); + #else + req_method = SSLv23_client_method(); + #endif + break; + case CURL_SSLVERSION_SSLv2: + failf(data, "No SSLv2 support"); + return CURLE_NOT_BUILT_IN; + case CURL_SSLVERSION_SSLv3: + failf(data, "No SSLv3 support"); + return CURLE_NOT_BUILT_IN; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + break; + case TRNSPRT_QUIC: + if(conn_config->version_max && + (conn_config->version_max != CURL_SSLVERSION_MAX_TLSv1_3)) { + failf(data, "QUIC needs at least TLS version 1.3"); + return CURLE_SSL_CONNECT_ERROR; + } + +#ifdef USE_OPENSSL_QUIC + req_method = OSSL_QUIC_client_method(); +#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L) + req_method = TLS_method(); +#else + req_method = SSLv23_client_method(); +#endif + break; + default: + failf(data, "unsupported transport %d in SSL init", transport); + return CURLE_SSL_CONNECT_ERROR; + } + + + DEBUGASSERT(!octx->ssl_ctx); + octx->ssl_ctx = SSL_CTX_new(req_method); + + if(!octx->ssl_ctx) { + failf(data, "SSL: could not create a context: %s", + ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer))); + return CURLE_OUT_OF_MEMORY; + } + + if(cb_setup) { + result = cb_setup(cf, data, cb_user_data); + if(result) + return result; + } + +#ifdef SSL_CTRL_SET_MSG_CALLBACK + if(data->set.fdebug && data->set.verbose) { + /* the SSL trace callback is only used for verbose logging */ + SSL_CTX_set_msg_callback(octx->ssl_ctx, ossl_trace); + SSL_CTX_set_msg_callback_arg(octx->ssl_ctx, cf); + } +#endif + + /* OpenSSL contains code to work around lots of bugs and flaws in various + SSL-implementations. SSL_CTX_set_options() is used to enabled those + work-arounds. The manpage for this option states that SSL_OP_ALL enables + all the work-arounds and that "It is usually safe to use SSL_OP_ALL to + enable the bug workaround options if compatibility with somewhat broken + implementations is desired." + + The "-no_ticket" option was introduced in OpenSSL 0.9.8j. it is a flag to + disable "rfc4507bis session ticket support". rfc4507bis was later turned + into the proper RFC5077: https://datatracker.ietf.org/doc/html/rfc5077 + + The enabled extension concerns the session management. I wonder how often + libcurl stops a connection and then resumes a TLS session. Also, sending + the session data is some overhead. I suggest that you just use your + proposed patch (which explicitly disables TICKET). + + If someone writes an application with libcurl and OpenSSL who wants to + enable the feature, one can do this in the SSL callback. + + SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG option enabling allowed proper + interoperability with web server Netscape Enterprise Server 2.0.1 which + was released back in 1996. + + Due to CVE-2010-4180, option SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG has + become ineffective as of OpenSSL 0.9.8q and 1.0.0c. In order to mitigate + CVE-2010-4180 when using previous OpenSSL versions we no longer enable + this option regardless of OpenSSL version and SSL_OP_ALL definition. + + OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability: + https://web.archive.org/web/20240114184648/openssl.org/~bodo/tls-cbc.txt. + In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that work-around + despite the fact that SSL_OP_ALL is documented to do "rather harmless" + workarounds. In order to keep the secure work-around, the + SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit must not be set. + */ + + ctx_options = SSL_OP_ALL; + +#ifdef SSL_OP_NO_TICKET + ctx_options |= SSL_OP_NO_TICKET; +#endif + +#ifdef SSL_OP_NO_COMPRESSION + ctx_options |= SSL_OP_NO_COMPRESSION; +#endif + +#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG + /* mitigate CVE-2010-4180 */ + ctx_options &= ~(ctx_option_t)SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG; +#endif + +#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + /* unless the user explicitly asks to allow the protocol vulnerability we + use the work-around */ + if(!ssl_config->enable_beast) + ctx_options &= ~(ctx_option_t)SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; +#endif + + switch(ssl_version_min) { + case CURL_SSLVERSION_SSLv2: + case CURL_SSLVERSION_SSLv3: + return CURLE_NOT_BUILT_IN; + + /* "--tlsv" options mean TLS >= version */ + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: /* TLS >= version 1.0 */ + case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */ + case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */ + case CURL_SSLVERSION_TLSv1_2: /* TLS >= version 1.2 */ + case CURL_SSLVERSION_TLSv1_3: /* TLS >= version 1.3 */ + /* asking for any TLS version as the minimum, means no SSL versions + allowed */ + ctx_options |= SSL_OP_NO_SSLv2; + ctx_options |= SSL_OP_NO_SSLv3; + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ + result = ossl_set_ssl_version_min_max(cf, octx->ssl_ctx); +#else + result = ossl_set_ssl_version_min_max_legacy(&ctx_options, cf, data); +#endif + if(result != CURLE_OK) + return result; + break; + + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + + SSL_CTX_set_options(octx->ssl_ctx, ctx_options); + +#ifdef HAS_ALPN + if(alpn && alpn_len) { + if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) { + failf(data, "Error setting ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + } +#endif + + if(ssl_cert || ssl_cert_blob || ssl_cert_type) { + if(!result && + !cert_stuff(data, octx->ssl_ctx, + ssl_cert, ssl_cert_blob, ssl_cert_type, + ssl_config->key, ssl_config->key_blob, + ssl_config->key_type, ssl_config->key_passwd)) + result = CURLE_SSL_CERTPROBLEM; + if(result) + /* failf() is already done in cert_stuff() */ + return result; + } + + ciphers = conn_config->cipher_list; + if(!ciphers && (peer->transport != TRNSPRT_QUIC)) + ciphers = DEFAULT_CIPHER_SELECTION; + if(ciphers) { + if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, ciphers)) { + failf(data, "failed setting cipher list: %s", ciphers); + return CURLE_SSL_CIPHER; + } + infof(data, "Cipher selection: %s", ciphers); + } + +#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES + { + const char *ciphers13 = conn_config->cipher_list13; + if(ciphers13) { + if(!SSL_CTX_set_ciphersuites(octx->ssl_ctx, ciphers13)) { + failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13); + return CURLE_SSL_CIPHER; + } + infof(data, "TLS 1.3 cipher selection: %s", ciphers13); + } + } +#endif + +#ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH + /* OpenSSL 1.1.1 requires clients to opt-in for PHA */ + SSL_CTX_set_post_handshake_auth(octx->ssl_ctx, 1); +#endif + +#ifdef HAVE_SSL_CTX_SET_EC_CURVES + { + const char *curves = conn_config->curves; + if(curves) { + if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, curves)) { + failf(data, "failed setting curves list: '%s'", curves); + return CURLE_SSL_CIPHER; + } + } + } +#endif + +#ifdef USE_OPENSSL_SRP + if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) { + char * const ssl_username = ssl_config->primary.username; + char * const ssl_password = ssl_config->primary.password; + infof(data, "Using TLS-SRP username: %s", ssl_username); + + if(!SSL_CTX_set_srp_username(octx->ssl_ctx, ssl_username)) { + failf(data, "Unable to set SRP username"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if(!SSL_CTX_set_srp_password(octx->ssl_ctx, ssl_password)) { + failf(data, "failed setting SRP password"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if(!conn_config->cipher_list) { + infof(data, "Setting cipher list SRP"); + + if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, "SRP")) { + failf(data, "failed setting SRP cipher list"); + return CURLE_SSL_CIPHER; + } + } + } +#endif + + /* OpenSSL always tries to verify the peer, this only says whether it should + * fail to connect if the verification fails, or if it should continue + * anyway. In the latter case the result of the verification is checked with + * SSL_get_verify_result() below. */ + SSL_CTX_set_verify(octx->ssl_ctx, + verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); + + /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */ +#ifdef HAVE_KEYLOG_CALLBACK + if(Curl_tls_keylog_enabled()) { + SSL_CTX_set_keylog_callback(octx->ssl_ctx, ossl_keylog_callback); + } +#endif + + if(cb_new_session) { + /* Enable the session cache because it is a prerequisite for the + * "new session" callback. Use the "external storage" mode to prevent + * OpenSSL from creating an internal session cache. + */ + SSL_CTX_set_session_cache_mode(octx->ssl_ctx, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(octx->ssl_ctx, cb_new_session); + } + + /* give application a chance to interfere with SSL set up. */ + if(data->set.ssl.fsslctx) { + /* When a user callback is installed to modify the SSL_CTX, + * we need to do the full initialization before calling it. + * See: #11800 */ + if(!octx->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); + if(result) + return result; + octx->x509_store_setup = TRUE; + } + Curl_set_in_callback(data, true); + result = (*data->set.ssl.fsslctx)(data, octx->ssl_ctx, + data->set.ssl.fsslctxp); + Curl_set_in_callback(data, false); + if(result) { + failf(data, "error signaled by ssl ctx callback"); + return result; + } + } + + /* Let's make an SSL structure */ + if(octx->ssl) + SSL_free(octx->ssl); + octx->ssl = SSL_new(octx->ssl_ctx); + if(!octx->ssl) { + failf(data, "SSL: could not create a context (handle)"); + return CURLE_OUT_OF_MEMORY; + } + + SSL_set_app_data(octx->ssl, ssl_user_data); + +#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ + !defined(OPENSSL_NO_OCSP) + if(conn_config->verifystatus) + SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp); +#endif + +#if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \ + defined(ALLOW_RENEG) + SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely); +#endif + + SSL_set_connect_state(octx->ssl); + + octx->server_cert = 0x0; +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + if(peer->sni) { + if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) { + failf(data, "Failed set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + } + +#ifdef USE_ECH + if(ECH_ENABLED(data)) { + unsigned char *ech_config = NULL; + size_t ech_config_len = 0; + char *outername = data->set.str[STRING_ECH_PUBLIC]; + int trying_ech_now = 0; + + if(data->set.tls_ech & CURLECH_GREASE) { + infof(data, "ECH: will GREASE ClientHello"); +# ifdef OPENSSL_IS_BORINGSSL + SSL_set_enable_ech_grease(octx->ssl, 1); +# else + SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE); +# endif + } + else if(data->set.tls_ech & CURLECH_CLA_CFG) { +# ifdef OPENSSL_IS_BORINGSSL + /* have to do base64 decode here for boring */ + const char *b64 = data->set.str[STRING_ECH_CONFIG]; + + if(!b64) { + infof(data, "ECH: ECHConfig from command line empty"); + return CURLE_SSL_CONNECT_ERROR; + } + ech_config_len = 2 * strlen(b64); + result = Curl_base64_decode(b64, &ech_config, &ech_config_len); + if(result || !ech_config) { + infof(data, "ECH: cannot base64 decode ECHConfig from command line"); + if(data->set.tls_ech & CURLECH_HARD) + return result; + } + if(SSL_set1_ech_config_list(octx->ssl, ech_config, + ech_config_len) != 1) { + infof(data, "ECH: SSL_ECH_set1_echconfig failed"); + if(data->set.tls_ech & CURLECH_HARD) { + free(ech_config); + return CURLE_SSL_CONNECT_ERROR; + } + } + free(ech_config); + trying_ech_now = 1; +# else + ech_config = (unsigned char *) data->set.str[STRING_ECH_CONFIG]; + if(!ech_config) { + infof(data, "ECH: ECHConfig from command line empty"); + return CURLE_SSL_CONNECT_ERROR; + } + ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]); + if(SSL_ech_set1_echconfig(octx->ssl, ech_config, ech_config_len) != 1) { + infof(data, "ECH: SSL_ECH_set1_echconfig failed"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else + trying_ech_now = 1; +# endif + infof(data, "ECH: ECHConfig from command line"); + } + else { + struct Curl_dns_entry *dns = NULL; + + if(peer->hostname) + dns = Curl_fetch_addr(data, peer->hostname, peer->port); + if(!dns) { + infof(data, "ECH: requested but no DNS info available"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else { + struct Curl_https_rrinfo *rinfo = NULL; + + rinfo = dns->hinfo; + if(rinfo && rinfo->echconfiglist) { + unsigned char *ecl = rinfo->echconfiglist; + size_t elen = rinfo->echconfiglist_len; + + infof(data, "ECH: ECHConfig from DoH HTTPS RR"); +# ifndef OPENSSL_IS_BORINGSSL + if(SSL_ech_set1_echconfig(octx->ssl, ecl, elen) != 1) { + infof(data, "ECH: SSL_ECH_set1_echconfig failed"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } +# else + if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { + infof(data, "ECH: SSL_set1_ech_config_list failed (boring)"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } +# endif + else { + trying_ech_now = 1; + infof(data, "ECH: imported ECHConfigList of length %zu", elen); + } + } + else { + infof(data, "ECH: requested but no ECHConfig available"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + Curl_resolv_unlock(data, dns); + } + } +# ifdef OPENSSL_IS_BORINGSSL + if(trying_ech_now && outername) { + infof(data, "ECH: setting public_name not supported with BoringSSL"); + return CURLE_SSL_CONNECT_ERROR; + } +# else + if(trying_ech_now && outername) { + infof(data, "ECH: inner: '%s', outer: '%s'", + peer->hostname ? peer->hostname : "NULL", outername); + result = SSL_ech_set_server_names(octx->ssl, + peer->hostname, outername, + 0 /* do send outer */); + if(result != 1) { + infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result); + return CURLE_SSL_CONNECT_ERROR; + } + } +# endif /* not BORING */ + if(trying_ech_now + && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) { + infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); + return CURLE_SSL_CONNECT_ERROR; + } + } +#endif /* USE_ECH */ + +#endif + + octx->reused_session = FALSE; + if(ssl_config->primary.cache_session && transport == TRNSPRT_TCP) { + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, NULL)) { + /* we got a session id, use it! */ + if(!SSL_set_session(octx->ssl, ssl_sessionid)) { + Curl_ssl_sessionid_unlock(data); + failf(data, "SSL: SSL_set_session failed: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return CURLE_SSL_CONNECT_ERROR; + } + /* Informational message */ + infof(data, "SSL reusing session ID"); + octx->reused_session = TRUE; + } + Curl_ssl_sessionid_unlock(data); + } + + return CURLE_OK; +} + +static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + struct alpn_proto_buf proto; + BIO *bio; + CURLcode result; + + DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); + DEBUGASSERT(octx); + memset(&proto, 0, sizeof(proto)); +#ifdef HAS_ALPN + if(connssl->alpn) { + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result) { + failf(data, "Error determining ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + } +#endif + + result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP, + proto.data, proto.len, NULL, NULL, + ossl_new_session_cb, cf); + if(result) + return result; + + octx->bio_method = ossl_bio_cf_method_create(); + if(!octx->bio_method) + return CURLE_OUT_OF_MEMORY; + bio = BIO_new(octx->bio_method); + if(!bio) + return CURLE_OUT_OF_MEMORY; + + BIO_set_data(bio, cf); +#ifdef HAVE_SSL_SET0_WBIO + /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works + * without backward compat quirks. Every call takes one reference, so we + * up it and pass. SSL* then owns it and will free. + * We check on the function in configure, since LibreSSL and friends + * each have their own versions to add support for this. */ + BIO_up_ref(bio); + SSL_set0_rbio(octx->ssl, bio); + SSL_set0_wbio(octx->ssl, bio); +#else + SSL_set_bio(octx->ssl, bio, bio); +#endif + +#ifdef HAS_ALPN + if(connssl->alpn) { + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } +#endif + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; +} + +#ifdef USE_ECH +/* If we have retry configs, then trace those out */ +static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl, + int reason) +{ + CURLcode result = CURLE_OK; + size_t rcl = 0; + int rv = 1; +# ifndef OPENSSL_IS_BORINGSSL + char *inner = NULL; + unsigned char *rcs = NULL; + char *outer = NULL; +# else + const char *inner = NULL; + const uint8_t *rcs = NULL; + const char *outer = NULL; + size_t out_name_len = 0; + int servername_type = 0; +# endif + + /* nothing to trace if not doing ECH */ + if(!ECH_ENABLED(data)) + return; +# ifndef OPENSSL_IS_BORINGSSL + rv = SSL_ech_get_retry_config(ssl, &rcs, &rcl); +# else + SSL_get0_ech_retry_configs(ssl, &rcs, &rcl); + rv = (int)rcl; +# endif + + if(rv && rcs) { +# define HEXSTR_MAX 800 + char *b64str = NULL; + size_t blen = 0; + + result = Curl_base64_encode((const char *)rcs, rcl, + &b64str, &blen); + if(!result && b64str) + infof(data, "ECH: retry_configs %s", b64str); + free(b64str); +# ifndef OPENSSL_IS_BORINGSSL + rv = SSL_ech_get_status(ssl, &inner, &outer); + infof(data, "ECH: retry_configs for %s from %s, %d %d", + inner ? inner : "NULL", outer ? outer : "NULL", reason, rv); +#else + rv = SSL_ech_accepted(ssl); + servername_type = SSL_get_servername_type(ssl); + inner = SSL_get_servername(ssl, servername_type); + SSL_get0_ech_name_override(ssl, &outer, &out_name_len); + /* TODO: get the inner from boring */ + infof(data, "ECH: retry_configs for %s from %s, %d %d", + inner ? inner : "NULL", outer ? outer : "NULL", reason, rv); +#endif + } + else + infof(data, "ECH: no retry_configs (rv = %d)", rv); +# ifndef OPENSSL_IS_BORINGSSL + OPENSSL_free((void *)rcs); +# endif + return; +} + +#endif + +static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + int err; + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + DEBUGASSERT(ssl_connect_2 == connssl->connecting_state); + DEBUGASSERT(octx); + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + ERR_clear_error(); + + err = SSL_connect(octx->ssl); + + if(!octx->x509_store_setup) { + /* After having send off the ClientHello, we prepare the x509 + * store to verify the coming certificate from the server */ + CURLcode result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); + if(result) + return result; + octx->x509_store_setup = TRUE; + } + +#ifndef HAVE_KEYLOG_CALLBACK + /* If key logging is enabled, wait for the handshake to complete and then + * proceed with logging secrets (for TLS 1.2 or older). + */ + if(Curl_tls_keylog_enabled() && !octx->keylog_done) + ossl_log_tls12_secret(octx->ssl, &octx->keylog_done); +#endif + + /* 1 is fine + 0 is "not successful but was shut down controlled" + <0 is "handshake was not successful, because a fatal error occurred" */ + if(1 != err) { + int detail = SSL_get_error(octx->ssl, err); + + if(SSL_ERROR_WANT_READ == detail) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + return CURLE_OK; + } + if(SSL_ERROR_WANT_WRITE == detail) { + connssl->io_need = CURL_SSL_IO_NEED_SEND; + return CURLE_OK; + } +#ifdef SSL_ERROR_WANT_ASYNC + if(SSL_ERROR_WANT_ASYNC == detail) { + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; + } +#endif +#ifdef SSL_ERROR_WANT_RETRY_VERIFY + if(SSL_ERROR_WANT_RETRY_VERIFY == detail) { + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; + } +#endif + if(octx->io_result == CURLE_AGAIN) { + return CURLE_OK; + } + else { + /* untreated error */ + sslerr_t errdetail; + char error_buffer[256]=""; + CURLcode result; + long lerr; + int lib; + int reason; + + /* the connection failed, we are not waiting for anything else. */ + connssl->connecting_state = ssl_connect_2; + + /* Get the earliest error code from the thread's error queue and remove + the entry. */ + errdetail = ERR_get_error(); + + /* Extract which lib and reason */ + lib = ERR_GET_LIB(errdetail); + reason = ERR_GET_REASON(errdetail); + + if((lib == ERR_LIB_SSL) && + ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) || + (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { + result = CURLE_PEER_FAILED_VERIFICATION; + + lerr = SSL_get_verify_result(octx->ssl); + if(lerr != X509_V_OK) { + ssl_config->certverifyresult = lerr; + msnprintf(error_buffer, sizeof(error_buffer), + "SSL certificate problem: %s", + X509_verify_cert_error_string(lerr)); + } + else + /* strcpy() is fine here as long as the string fits within + error_buffer */ + strcpy(error_buffer, "SSL certificate verification failed"); + } +#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED) + /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on + OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */ + else if((lib == ERR_LIB_SSL) && + (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) { + /* If client certificate is required, communicate the + error to client */ + result = CURLE_SSL_CLIENTCERT; + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + } +#endif +#ifdef USE_ECH + else if((lib == ERR_LIB_SSL) && +# ifndef OPENSSL_IS_BORINGSSL + (reason == SSL_R_ECH_REQUIRED)) { +# else + (reason == SSL_R_ECH_REJECTED)) { +# endif + + /* trace retry_configs if we got some */ + ossl_trace_ech_retry_configs(data, octx->ssl, reason); + + result = CURLE_ECH_REQUIRED; + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + } +#endif + else { + result = CURLE_SSL_CONNECT_ERROR; + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + } + + /* detail is already set to the SSL error above */ + + /* If we e.g. use SSLv2 request-method and the server does not like us + * (RST connection, etc.), OpenSSL gives no explanation whatsoever and + * the SO_ERROR is also lost. + */ + if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { + char extramsg[80]=""; + int sockerr = SOCKERRNO; + + if(sockerr && detail == SSL_ERROR_SYSCALL) + Curl_strerror(sockerr, extramsg, sizeof(extramsg)); + failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", + extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), + connssl->peer.hostname, connssl->peer.port); + return result; + } + + /* Could be a CERT problem */ + failf(data, "%s", error_buffer); + + return result; + } + } + else { + int psigtype_nid = NID_undef; + const char *negotiated_group_name = NULL; + + /* we connected fine, we are not waiting for anything else. */ + connssl->connecting_state = ssl_connect_3; + +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) + SSL_get_peer_signature_type_nid(octx->ssl, &psigtype_nid); +#if (OPENSSL_VERSION_NUMBER >= 0x30200000L) + negotiated_group_name = SSL_get0_group_name(octx->ssl); +#else + negotiated_group_name = + OBJ_nid2sn(SSL_get_negotiated_group(octx->ssl) & 0x0000FFFF); +#endif +#endif + + /* Informational message */ + infof(data, "SSL connection using %s / %s / %s / %s", + SSL_get_version(octx->ssl), + SSL_get_cipher(octx->ssl), + negotiated_group_name? negotiated_group_name : "[blank]", + OBJ_nid2sn(psigtype_nid)); + +#ifdef USE_ECH +# ifndef OPENSSL_IS_BORINGSSL + if(ECH_ENABLED(data)) { + char *inner = NULL, *outer = NULL; + const char *status = NULL; + int rv; + + rv = SSL_ech_get_status(octx->ssl, &inner, &outer); + switch(rv) { + case SSL_ECH_STATUS_SUCCESS: + status = "succeeded"; + break; + case SSL_ECH_STATUS_GREASE_ECH: + status = "sent GREASE, got retry-configs"; + break; + case SSL_ECH_STATUS_GREASE: + status = "sent GREASE"; + break; + case SSL_ECH_STATUS_NOT_TRIED: + status = "not attempted"; + break; + case SSL_ECH_STATUS_NOT_CONFIGURED: + status = "not configured"; + break; + case SSL_ECH_STATUS_BACKEND: + status = "backend (unexpected)"; + break; + case SSL_ECH_STATUS_FAILED: + status = "failed"; + break; + case SSL_ECH_STATUS_BAD_CALL: + status = "bad call (unexpected)"; + break; + case SSL_ECH_STATUS_BAD_NAME: + status = "bad name (unexpected)"; + break; + default: + status = "unexpected status"; + infof(data, "ECH: unexpected status %d",rv); + } + infof(data, "ECH: result: status is %s, inner is %s, outer is %s", + (status?status:"NULL"), + (inner?inner:"NULL"), + (outer?outer:"NULL")); + OPENSSL_free(inner); + OPENSSL_free(outer); + if(rv == SSL_ECH_STATUS_GREASE_ECH) { + /* trace retry_configs if we got some */ + ossl_trace_ech_retry_configs(data, octx->ssl, 0); + } + if(rv != SSL_ECH_STATUS_SUCCESS + && data->set.tls_ech & CURLECH_HARD) { + infof(data, "ECH: ech-hard failed"); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { + infof(data, "ECH: result: status is not attempted"); + } +# endif /* BORING */ +#endif /* USE_ECH */ + +#ifdef HAS_ALPN + /* Sets data and len to negotiated protocol, len is 0 if no protocol was + * negotiated + */ + if(connssl->alpn) { + const unsigned char *neg_protocol; + unsigned int len; + SSL_get0_alpn_selected(octx->ssl, &neg_protocol, &len); + + return Curl_alpn_set_negotiated(cf, data, neg_protocol, len); + } +#endif + + return CURLE_OK; + } +} + +/* + * Heavily modified from: + * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL + */ +static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, + const char *pinnedpubkey) +{ + /* Scratch */ + int len1 = 0, len2 = 0; + unsigned char *buff1 = NULL, *temp = NULL; + + /* Result is returned to caller */ + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + + /* if a path was not specified, do not pin */ + if(!pinnedpubkey) + return CURLE_OK; + + if(!cert) + return result; + + do { + /* Begin Gyrations to get the subjectPublicKeyInfo */ + /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */ + + /* https://groups.google.com/group/mailing.openssl.users/browse_thread + /thread/d61858dae102c6c7 */ + len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); + if(len1 < 1) + break; /* failed */ + + buff1 = temp = malloc(len1); + if(!buff1) + break; /* failed */ + + /* https://docs.openssl.org/master/man3/d2i_X509/ */ + len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp); + + /* + * These checks are verifying we got back the same values as when we + * sized the buffer. it is pretty weak since they should always be the + * same. But it gives us something to test. + */ + if((len1 != len2) || !temp || ((temp - buff1) != len1)) + break; /* failed */ + + /* End Gyrations */ + + /* The one good exit point */ + result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1); + } while(0); + + if(buff1) + free(buff1); + + return result; +} + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x3060000fL) && \ + !defined(OPENSSL_IS_BORINGSSL) && \ + !defined(OPENSSL_IS_AWSLC) && \ + !defined(CURL_DISABLE_VERBOSE_STRINGS) +static void infof_certstack(struct Curl_easy *data, const SSL *ssl) +{ + STACK_OF(X509) *certstack; + long verify_result; + int num_cert_levels; + int cert_level; + + verify_result = SSL_get_verify_result(ssl); + if(verify_result != X509_V_OK) + certstack = SSL_get_peer_cert_chain(ssl); + else + certstack = SSL_get0_verified_chain(ssl); + num_cert_levels = sk_X509_num(certstack); + + for(cert_level = 0; cert_level < num_cert_levels; cert_level++) { + char cert_algorithm[80] = ""; + char group_name_final[80] = ""; + const X509_ALGOR *palg_cert = NULL; + const ASN1_OBJECT *paobj_cert = NULL; + X509 *current_cert; + EVP_PKEY *current_pkey; + int key_bits; + int key_sec_bits; + int get_group_name; + const char *type_name; + + current_cert = sk_X509_value(certstack, cert_level); + + X509_get0_signature(NULL, &palg_cert, current_cert); + X509_ALGOR_get0(&paobj_cert, NULL, NULL, palg_cert); + OBJ_obj2txt(cert_algorithm, sizeof(cert_algorithm), paobj_cert, 0); + + current_pkey = X509_get0_pubkey(current_cert); + key_bits = EVP_PKEY_bits(current_pkey); +#if (OPENSSL_VERSION_NUMBER < 0x30000000L) +#define EVP_PKEY_get_security_bits EVP_PKEY_security_bits +#endif + key_sec_bits = EVP_PKEY_get_security_bits(current_pkey); +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) + { + char group_name[80] = ""; + get_group_name = EVP_PKEY_get_group_name(current_pkey, group_name, + sizeof(group_name), NULL); + msnprintf(group_name_final, sizeof(group_name_final), "/%s", group_name); + } + type_name = EVP_PKEY_get0_type_name(current_pkey); +#else + get_group_name = 0; + type_name = NULL; +#endif + + infof(data, + " Certificate level %d: " + "Public key type %s%s (%d/%d Bits/secBits), signed using %s", + cert_level, type_name ? type_name : "?", + get_group_name == 0 ? "" : group_name_final, + key_bits, key_sec_bits, cert_algorithm); + } +} +#else +#define infof_certstack(data, ssl) +#endif + +CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ossl_ctx *octx, + struct ssl_peer *peer) +{ + struct connectdata *conn = cf->conn; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + CURLcode result = CURLE_OK; + int rc; + long lerr; + X509 *issuer; + BIO *fp = NULL; + char error_buffer[256]=""; + char buffer[2048]; + const char *ptr; + BIO *mem = BIO_new(BIO_s_mem()); + bool strict = (conn_config->verifypeer || conn_config->verifyhost); + + DEBUGASSERT(octx); + + if(!mem) { + failf(data, + "BIO_new return NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + return CURLE_OUT_OF_MEMORY; + } + + if(data->set.ssl.certinfo) + /* asked to gather certificate info */ + (void)Curl_ossl_certchain(data, octx->ssl); + + octx->server_cert = SSL_get1_peer_certificate(octx->ssl); + if(!octx->server_cert) { + BIO_free(mem); + if(!strict) + return CURLE_OK; + + failf(data, "SSL: could not get peer certificate"); + return CURLE_PEER_FAILED_VERIFICATION; + } + + infof(data, "%s certificate:", + Curl_ssl_cf_is_proxy(cf)? "Proxy" : "Server"); + + rc = x509_name_oneline(X509_get_subject_name(octx->server_cert), + buffer, sizeof(buffer)); + infof(data, " subject: %s", rc?"[NONE]":buffer); + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + { + long len; + ASN1_TIME_print(mem, X509_get0_notBefore(octx->server_cert)); + len = BIO_get_mem_data(mem, (char **) &ptr); + infof(data, " start date: %.*s", (int)len, ptr); + (void)BIO_reset(mem); + + ASN1_TIME_print(mem, X509_get0_notAfter(octx->server_cert)); + len = BIO_get_mem_data(mem, (char **) &ptr); + infof(data, " expire date: %.*s", (int)len, ptr); + (void)BIO_reset(mem); + } +#endif + + BIO_free(mem); + + if(conn_config->verifyhost) { + result = Curl_ossl_verifyhost(data, conn, peer, octx->server_cert); + if(result) { + X509_free(octx->server_cert); + octx->server_cert = NULL; + return result; + } + } + + rc = x509_name_oneline(X509_get_issuer_name(octx->server_cert), + buffer, sizeof(buffer)); + if(rc) { + if(strict) + failf(data, "SSL: could not get X509-issuer name"); + result = CURLE_PEER_FAILED_VERIFICATION; + } + else { + infof(data, " issuer: %s", buffer); + + /* We could do all sorts of certificate verification stuff here before + deallocating the certificate. */ + + /* e.g. match issuer name with provided issuer certificate */ + if(conn_config->issuercert || conn_config->issuercert_blob) { + if(conn_config->issuercert_blob) { + fp = BIO_new_mem_buf(conn_config->issuercert_blob->data, + (int)conn_config->issuercert_blob->len); + if(!fp) { + failf(data, + "BIO_new_mem_buf NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + X509_free(octx->server_cert); + octx->server_cert = NULL; + return CURLE_OUT_OF_MEMORY; + } + } + else { + fp = BIO_new(BIO_s_file()); + if(!fp) { + failf(data, + "BIO_new return NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + X509_free(octx->server_cert); + octx->server_cert = NULL; + return CURLE_OUT_OF_MEMORY; + } + + if(BIO_read_filename(fp, conn_config->issuercert) <= 0) { + if(strict) + failf(data, "SSL: Unable to open issuer cert (%s)", + conn_config->issuercert); + BIO_free(fp); + X509_free(octx->server_cert); + octx->server_cert = NULL; + return CURLE_SSL_ISSUER_ERROR; + } + } + + issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL); + if(!issuer) { + if(strict) + failf(data, "SSL: Unable to read issuer cert (%s)", + conn_config->issuercert); + BIO_free(fp); + X509_free(issuer); + X509_free(octx->server_cert); + octx->server_cert = NULL; + return CURLE_SSL_ISSUER_ERROR; + } + + if(X509_check_issued(issuer, octx->server_cert) != X509_V_OK) { + if(strict) + failf(data, "SSL: Certificate issuer check failed (%s)", + conn_config->issuercert); + BIO_free(fp); + X509_free(issuer); + X509_free(octx->server_cert); + octx->server_cert = NULL; + return CURLE_SSL_ISSUER_ERROR; + } + + infof(data, " SSL certificate issuer check ok (%s)", + conn_config->issuercert); + BIO_free(fp); + X509_free(issuer); + } + + lerr = SSL_get_verify_result(octx->ssl); + ssl_config->certverifyresult = lerr; + if(lerr != X509_V_OK) { + if(conn_config->verifypeer) { + /* We probably never reach this, because SSL_connect() will fail + and we return earlier if verifypeer is set? */ + if(strict) + failf(data, "SSL certificate verify result: %s (%ld)", + X509_verify_cert_error_string(lerr), lerr); + result = CURLE_PEER_FAILED_VERIFICATION; + } + else + infof(data, " SSL certificate verify result: %s (%ld)," + " continuing anyway.", + X509_verify_cert_error_string(lerr), lerr); + } + else + infof(data, " SSL certificate verify ok."); + } + + infof_certstack(data, octx->ssl); + +#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ + !defined(OPENSSL_NO_OCSP) + if(conn_config->verifystatus && !octx->reused_session) { + /* do not do this after Session ID reuse */ + result = verifystatus(cf, data, octx); + if(result) { + /* when verifystatus failed, remove the session id from the cache again + if present */ + if(!Curl_ssl_cf_is_proxy(cf)) { + void *old_ssl_sessionid = NULL; + bool incache; + Curl_ssl_sessionid_lock(data); + incache = !(Curl_ssl_getsessionid(cf, data, peer, + &old_ssl_sessionid, NULL)); + if(incache) { + infof(data, "Remove session ID again from cache"); + Curl_ssl_delsessionid(data, old_ssl_sessionid); + } + Curl_ssl_sessionid_unlock(data); + } + + X509_free(octx->server_cert); + octx->server_cert = NULL; + return result; + } + } +#endif + + if(!strict) + /* when not strict, we do not bother about the verify cert problems */ + result = CURLE_OK; + +#ifndef CURL_DISABLE_PROXY + ptr = Curl_ssl_cf_is_proxy(cf)? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: + data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif + if(!result && ptr) { + result = ossl_pkp_pin_peer_pubkey(data, octx->server_cert, ptr); + if(result) + failf(data, "SSL: public key does not match pinned public key"); + } + + X509_free(octx->server_cert); + octx->server_cert = NULL; + + return result; +} + +static CURLcode ossl_connect_step3(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + + DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + + /* + * We check certificates to authenticate the server; otherwise we risk + * man-in-the-middle attack; NEVERTHELESS, if we are told explicitly not to + * verify the peer, ignore faults and failures from the server cert + * operations. + */ + + result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer); + if(!result) + connssl->connecting_state = ssl_connect_done; + + return result; +} + +static CURLcode ossl_connect_common(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool nonblocking, + bool *done) +{ + CURLcode result = CURLE_OK; + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + int what; + + /* check if the connection has already been established */ + if(ssl_connection_complete == connssl->state) { + *done = TRUE; + return CURLE_OK; + } + + if(ssl_connect_1 == connssl->connecting_state) { + /* Find out how much more time we are allowed */ + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time is already up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + result = ossl_connect_step1(cf, data); + if(result) + goto out; + } + + while(ssl_connect_2 == connssl->connecting_state) { + + /* check allowed time left */ + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + + /* if ssl is expecting something, check if it is available. */ + if(!nonblocking && connssl->io_need) { + + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; + + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + timeout_ms); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } + if(0 == what) { + /* timeout */ + failf(data, "SSL connection timeout"); + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + /* socket is readable or writable */ + } + + /* Run transaction, and return to the caller if it failed or if this + * connection is done nonblocking and this loop would execute again. This + * permits the owner of a multi handle to abort a connection attempt + * before step2 has completed while ensuring that a client using select() + * or epoll() will always have a valid fdset to wait on. + */ + result = ossl_connect_step2(cf, data); + if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) + goto out; + + } /* repeat step2 until all transactions are done. */ + + if(ssl_connect_3 == connssl->connecting_state) { + result = ossl_connect_step3(cf, data); + if(result) + goto out; + } + + if(ssl_connect_done == connssl->connecting_state) { + connssl->state = ssl_connection_complete; + *done = TRUE; + } + else + *done = FALSE; + + /* Reset our connect state machine */ + connssl->connecting_state = ssl_connect_1; + +out: + return result; +} + +static CURLcode ossl_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + return ossl_connect_common(cf, data, TRUE, done); +} + +static CURLcode ossl_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode result; + bool done = FALSE; + + result = ossl_connect_common(cf, data, FALSE, &done); + if(result) + return result; + + DEBUGASSERT(done); + + return CURLE_OK; +} + +static bool ossl_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + + (void)data; + DEBUGASSERT(connssl && octx); + if(octx->ssl && SSL_pending(octx->ssl)) + return TRUE; + return FALSE; +} + +static ssize_t ossl_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, + size_t len, + CURLcode *curlcode) +{ + /* SSL_write() is said to return 'int' while write() and send() returns + 'size_t' */ + int err; + char error_buffer[256]; + sslerr_t sslerror; + int memlen; + int rc; + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + + (void)data; + DEBUGASSERT(octx); + + ERR_clear_error(); + + memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; + rc = SSL_write(octx->ssl, mem, memlen); + + if(rc <= 0) { + err = SSL_get_error(octx->ssl, rc); + + switch(err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* The operation did not complete; the same TLS/SSL I/O function + should be called again later. This is basically an EWOULDBLOCK + equivalent. */ + *curlcode = CURLE_AGAIN; + rc = -1; + goto out; + case SSL_ERROR_SYSCALL: + { + int sockerr = SOCKERRNO; + + if(octx->io_result == CURLE_AGAIN) { + *curlcode = CURLE_AGAIN; + rc = -1; + goto out; + } + sslerror = ERR_get_error(); + if(sslerror) + ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); + else if(sockerr) + Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + else + msnprintf(error_buffer, sizeof(error_buffer), "%s", + SSL_ERROR_to_str(err)); + + failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", + error_buffer, sockerr); + *curlcode = CURLE_SEND_ERROR; + rc = -1; + goto out; + } + case SSL_ERROR_SSL: { + /* A failure in the SSL library occurred, usually a protocol error. + The OpenSSL error queue contains more information on the error. */ + sslerror = ERR_get_error(); + failf(data, "SSL_write() error: %s", + ossl_strerror(sslerror, error_buffer, sizeof(error_buffer))); + *curlcode = CURLE_SEND_ERROR; + rc = -1; + goto out; + } + default: + /* a true error */ + failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", + SSL_ERROR_to_str(err), SOCKERRNO); + *curlcode = CURLE_SEND_ERROR; + rc = -1; + goto out; + } + } + *curlcode = CURLE_OK; + +out: + return (ssize_t)rc; /* number of bytes */ +} + +static ssize_t ossl_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, /* transfer */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + CURLcode *curlcode) +{ + char error_buffer[256]; + unsigned long sslerror; + ssize_t nread; + int buffsize; + struct connectdata *conn = cf->conn; + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + + (void)data; + DEBUGASSERT(octx); + + ERR_clear_error(); + + buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; + nread = (ssize_t)SSL_read(octx->ssl, buf, buffsize); + + if(nread <= 0) { + /* failed SSL_read */ + int err = SSL_get_error(octx->ssl, (int)nread); + + switch(err) { + case SSL_ERROR_NONE: /* this is not an error */ + break; + case SSL_ERROR_ZERO_RETURN: /* no more data */ + /* close_notify alert */ + if(cf->sockindex == FIRSTSOCKET) + /* mark the connection for close if it is indeed the control + connection */ + connclose(conn, "TLS close_notify"); + break; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* there is data pending, re-invoke SSL_read() */ + *curlcode = CURLE_AGAIN; + nread = -1; + goto out; + default: + /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return + value/errno" */ + /* https://docs.openssl.org/master/man3/ERR_get_error/ */ + if(octx->io_result == CURLE_AGAIN) { + *curlcode = CURLE_AGAIN; + nread = -1; + goto out; + } + sslerror = ERR_get_error(); + if((nread < 0) || sslerror) { + /* If the return code was negative or there actually is an error in the + queue */ + int sockerr = SOCKERRNO; + if(sslerror) + ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); + else if(sockerr && err == SSL_ERROR_SYSCALL) + Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + else + msnprintf(error_buffer, sizeof(error_buffer), "%s", + SSL_ERROR_to_str(err)); + failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d", + error_buffer, sockerr); + *curlcode = CURLE_RECV_ERROR; + nread = -1; + goto out; + } + /* For debug builds be a little stricter and error on any + SSL_ERROR_SYSCALL. For example a server may have closed the connection + abruptly without a close_notify alert. For compatibility with older + peers we do not do this by default. #4624 + + We can use this to gauge how many users may be affected, and + if it goes ok eventually transition to allow in dev and release with + the newest OpenSSL: #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) */ +#ifdef DEBUGBUILD + if(err == SSL_ERROR_SYSCALL) { + int sockerr = SOCKERRNO; + if(sockerr) + Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + else { + msnprintf(error_buffer, sizeof(error_buffer), + "Connection closed abruptly"); + } + failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d" + " (Fatal because this is a curl debug build)", + error_buffer, sockerr); + *curlcode = CURLE_RECV_ERROR; + nread = -1; + goto out; + } +#endif + } + } + +out: + return nread; +} + +static size_t ossl_version(char *buffer, size_t size) +{ +#ifdef LIBRESSL_VERSION_NUMBER +#ifdef HAVE_OPENSSL_VERSION + char *p; + size_t count; + const char *ver = OpenSSL_version(OPENSSL_VERSION); + const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */ + if(strncasecompare(ver, expected, sizeof(expected) - 1)) { + ver += sizeof(expected) - 1; + } + count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver); + for(p = buffer; *p; ++p) { + if(ISBLANK(*p)) + *p = '_'; + } + return count; +#else + return msnprintf(buffer, size, "%s/%lx.%lx.%lx", + OSSL_PACKAGE, + (LIBRESSL_VERSION_NUMBER>>28)&0xf, + (LIBRESSL_VERSION_NUMBER>>20)&0xff, + (LIBRESSL_VERSION_NUMBER>>12)&0xff); +#endif +#elif defined(OPENSSL_IS_BORINGSSL) +#ifdef CURL_BORINGSSL_VERSION + return msnprintf(buffer, size, "%s/%s", + OSSL_PACKAGE, + CURL_BORINGSSL_VERSION); +#else + return msnprintf(buffer, size, OSSL_PACKAGE); +#endif +#elif defined(OPENSSL_IS_AWSLC) + return msnprintf(buffer, size, "%s/%s", + OSSL_PACKAGE, + AWSLC_VERSION_NUMBER_STRING); +#elif defined(HAVE_OPENSSL_VERSION) && defined(OPENSSL_VERSION_STRING) + return msnprintf(buffer, size, "%s/%s", + OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING)); +#else + /* not LibreSSL, BoringSSL and not using OpenSSL_version */ + + char sub[3]; + unsigned long ssleay_value; + sub[2]='\0'; + sub[1]='\0'; + ssleay_value = OpenSSL_version_num(); + if(ssleay_value < 0x906000) { + ssleay_value = SSLEAY_VERSION_NUMBER; + sub[0]='\0'; + } + else { + if(ssleay_value&0xff0) { + int minor_ver = (ssleay_value >> 4) & 0xff; + if(minor_ver > 26) { + /* handle extended version introduced for 0.9.8za */ + sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1); + sub[0] = 'z'; + } + else { + sub[0] = (char) (minor_ver + 'a' - 1); + } + } + else + sub[0]='\0'; + } + + return msnprintf(buffer, size, "%s/%lx.%lx.%lx%s" +#ifdef OPENSSL_FIPS + "-fips" +#endif + , + OSSL_PACKAGE, + (ssleay_value>>28)&0xf, + (ssleay_value>>20)&0xff, + (ssleay_value>>12)&0xff, + sub); +#endif /* OPENSSL_IS_BORINGSSL */ +} + +/* can be called with data == NULL */ +static CURLcode ossl_random(struct Curl_easy *data, + unsigned char *entropy, size_t length) +{ + int rc; + if(data) { + if(ossl_seed(data)) /* Initiate the seed if not already done */ + return CURLE_FAILED_INIT; /* could not seed for some reason */ + } + else { + if(!rand_enough()) + return CURLE_FAILED_INIT; + } + /* RAND_bytes() returns 1 on success, 0 otherwise. */ + rc = RAND_bytes(entropy, (ossl_valsize_t)curlx_uztosi(length)); + return (rc == 1 ? CURLE_OK : CURLE_FAILED_INIT); +} + +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) +static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum /* output */, + size_t unused) +{ + EVP_MD_CTX *mdctx; + unsigned int len = 0; + (void) unused; + + mdctx = EVP_MD_CTX_create(); + if(!mdctx) + return CURLE_OUT_OF_MEMORY; + if(!EVP_DigestInit(mdctx, EVP_sha256())) { + EVP_MD_CTX_destroy(mdctx); + return CURLE_FAILED_INIT; + } + EVP_DigestUpdate(mdctx, tmp, tmplen); + EVP_DigestFinal_ex(mdctx, sha256sum, &len); + EVP_MD_CTX_destroy(mdctx); + return CURLE_OK; +} +#endif + +static bool ossl_cert_status_request(void) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ + !defined(OPENSSL_NO_OCSP) + return TRUE; +#else + return FALSE; +#endif +} + +static void *ossl_get_internals(struct ssl_connect_data *connssl, + CURLINFO info) +{ + /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */ + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + DEBUGASSERT(octx); + return info == CURLINFO_TLS_SESSION ? + (void *)octx->ssl_ctx : (void *)octx->ssl; +} + +const struct Curl_ssl Curl_ssl_openssl = { + { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */ + + SSLSUPP_CA_PATH | + SSLSUPP_CAINFO_BLOB | + SSLSUPP_CERTINFO | + SSLSUPP_PINNEDPUBKEY | + SSLSUPP_SSL_CTX | +#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES + SSLSUPP_TLS13_CIPHERSUITES | +#endif +#ifdef USE_ECH + SSLSUPP_ECH | +#endif + SSLSUPP_CA_CACHE | + SSLSUPP_HTTPS_PROXY, + + sizeof(struct ossl_ctx), + + ossl_init, /* init */ + ossl_cleanup, /* cleanup */ + ossl_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + ossl_shutdown, /* shutdown */ + ossl_data_pending, /* data_pending */ + ossl_random, /* random */ + ossl_cert_status_request, /* cert_status_request */ + ossl_connect, /* connect */ + ossl_connect_nonblocking, /* connect_nonblocking */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ + ossl_get_internals, /* get_internals */ + ossl_close, /* close_one */ + ossl_close_all, /* close_all */ + ossl_set_engine, /* set_engine */ + ossl_set_engine_default, /* set_engine_default */ + ossl_engines_list, /* engines_list */ + Curl_none_false_start, /* false_start */ +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) + ossl_sha256sum, /* sha256sum */ +#else + NULL, /* sha256sum */ +#endif + NULL, /* use of data in this connection */ + NULL, /* remote of data from this connection */ + ossl_recv, /* recv decrypted data */ + ossl_send, /* send data to encrypt */ +}; + +#endif /* USE_OPENSSL */ diff --git a/third_party/curl/lib/vtls/openssl.h b/third_party/curl/lib/vtls/openssl.h new file mode 100644 index 0000000000..b0d78478a7 --- /dev/null +++ b/third_party/curl/lib/vtls/openssl.h @@ -0,0 +1,122 @@ +#ifndef HEADER_CURL_SSLUSE_H +#define HEADER_CURL_SSLUSE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_OPENSSL +/* + * This header should only be needed to get included by vtls.c, openssl.c + * and ngtcp2.c + */ +#include +#include + +#include "urldata.h" + +/* Struct to hold a Curl OpenSSL instance */ +struct ossl_ctx { + /* these ones requires specific SSL-types */ + SSL_CTX* ssl_ctx; + SSL* ssl; + X509* server_cert; + BIO_METHOD *bio_method; + CURLcode io_result; /* result of last BIO cfilter operation */ +#ifndef HAVE_KEYLOG_CALLBACK + /* Set to true once a valid keylog entry has been created to avoid dupes. + This is a bool and not a bitfield because it is passed by address. */ + bool keylog_done; +#endif + BIT(x509_store_setup); /* x509 store has been set up */ + BIT(reused_session); /* session-ID was reused for this */ +}; + +typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data); + +typedef int Curl_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid); + +CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + int transport, /* TCP or QUIC */ + const unsigned char *alpn, size_t alpn_len, + Curl_ossl_ctx_setup_cb *cb_setup, + void *cb_user_data, + Curl_ossl_new_session_cb *cb_new_session, + void *ssl_user_data); + +#if (OPENSSL_VERSION_NUMBER < 0x30000000L) +#define SSL_get1_peer_certificate SSL_get_peer_certificate +#endif + +CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + struct ssl_peer *peer, X509 *server_cert); +extern const struct Curl_ssl Curl_ssl_openssl; + +CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, + SSL_CTX *ctx, char *cert_file, + const struct curl_blob *cert_blob, + const char *cert_type, char *key_file, + const struct curl_blob *key_blob, + const char *key_type, char *key_passwd); + +CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl); + +/** + * Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and + * easy handle `data`. Will allow reuse of a shared cache if suitable + * and configured. + */ +CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + SSL_CTX *ssl_ctx); + +CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf, + struct Curl_easy *data, + SSL_CTX *ssl_ctx); + +/* + * Add a new session to the cache. Takes ownership of the session. + */ +CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + SSL_SESSION *ssl_sessionid); + +/* + * Get the server cert, verify it and show it, etc., only call failf() if + * ssl config verifypeer or -host is set. Otherwise all this is for + * informational purposes only! + */ +CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ossl_ctx *octx, + struct ssl_peer *peer); + +#endif /* USE_OPENSSL */ +#endif /* HEADER_CURL_SSLUSE_H */ diff --git a/third_party/curl/lib/vtls/rustls.c b/third_party/curl/lib/vtls/rustls.c new file mode 100644 index 0000000000..bdcd08ab99 --- /dev/null +++ b/third_party/curl/lib/vtls/rustls.c @@ -0,0 +1,856 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Jacob Hoffman-Andrews, + * + * Copyright (C) kpcyrd, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_RUSTLS + +#include "curl_printf.h" + +#include +#include + +#include "inet_pton.h" +#include "urldata.h" +#include "sendf.h" +#include "vtls.h" +#include "vtls_int.h" +#include "select.h" +#include "strerror.h" +#include "multiif.h" +#include "connect.h" /* for the connect timeout */ + +struct rustls_ssl_backend_data +{ + const struct rustls_client_config *config; + struct rustls_connection *conn; + size_t plain_out_buffered; + BIT(data_in_pending); + BIT(sent_shutdown); +}; + +/* For a given rustls_result error code, return the best-matching CURLcode. */ +static CURLcode map_error(rustls_result r) +{ + if(rustls_result_is_cert_error(r)) { + return CURLE_PEER_FAILED_VERIFICATION; + } + switch(r) { + case RUSTLS_RESULT_OK: + return CURLE_OK; + case RUSTLS_RESULT_NULL_PARAMETER: + return CURLE_BAD_FUNCTION_ARGUMENT; + default: + return CURLE_RECV_ERROR; + } +} + +static bool +cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) +{ + struct ssl_connect_data *ctx = cf->ctx; + struct rustls_ssl_backend_data *backend; + + (void)data; + DEBUGASSERT(ctx && ctx->backend); + backend = (struct rustls_ssl_backend_data *)ctx->backend; + return backend->data_in_pending; +} + +struct io_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; +}; + +static int +read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) +{ + struct io_ctx *io_ctx = userdata; + struct ssl_connect_data *const connssl = io_ctx->cf->ctx; + CURLcode result; + int ret = 0; + ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data, + (char *)buf, len, &result); + if(nread < 0) { + nread = 0; + if(CURLE_AGAIN == result) + ret = EAGAIN; + else + ret = EINVAL; + } + else if(nread == 0) + connssl->peer_closed = TRUE; + *out_n = (uintptr_t)nread; + CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %zd, %d", + len, nread, result); + return ret; +} + +static int +write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) +{ + struct io_ctx *io_ctx = userdata; + CURLcode result; + int ret = 0; + ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data, + (const char *)buf, len, &result); + if(nwritten < 0) { + nwritten = 0; + if(CURLE_AGAIN == result) + ret = EAGAIN; + else + ret = EINVAL; + } + *out_n = (uintptr_t)nwritten; + CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", + len, nwritten, result); + return ret; +} + +static ssize_t tls_recv_more(struct Curl_cfilter *cf, + struct Curl_easy *data, CURLcode *err) +{ + struct ssl_connect_data *const connssl = cf->ctx; + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; + struct io_ctx io_ctx; + size_t tls_bytes_read = 0; + rustls_io_result io_error; + rustls_result rresult = 0; + + io_ctx.cf = cf; + io_ctx.data = data; + io_error = rustls_connection_read_tls(backend->conn, read_cb, &io_ctx, + &tls_bytes_read); + if(io_error == EAGAIN || io_error == EWOULDBLOCK) { + *err = CURLE_AGAIN; + return -1; + } + else if(io_error) { + char buffer[STRERROR_LEN]; + failf(data, "reading from socket: %s", + Curl_strerror(io_error, buffer, sizeof(buffer))); + *err = CURLE_RECV_ERROR; + return -1; + } + + rresult = rustls_connection_process_new_packets(backend->conn); + if(rresult != RUSTLS_RESULT_OK) { + char errorbuf[255]; + size_t errorlen; + rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); + failf(data, "rustls_connection_process_new_packets: %.*s", + (int)errorlen, errorbuf); + *err = map_error(rresult); + return -1; + } + + backend->data_in_pending = TRUE; + *err = CURLE_OK; + return (ssize_t)tls_bytes_read; +} + +/* + * On each run: + * - Read a chunk of bytes from the socket into rustls' TLS input buffer. + * - Tell rustls to process any new packets. + * - Read out as many plaintext bytes from rustls as possible, until hitting + * error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up. + * + * it is okay to call this function with plainbuf == NULL and plainlen == 0. In + * that case, it will copy bytes from the socket into rustls' TLS input + * buffer, and process packets, but will not consume bytes from rustls' + * plaintext output buffer. + */ +static ssize_t +cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *plainbuf, size_t plainlen, CURLcode *err) +{ + struct ssl_connect_data *const connssl = cf->ctx; + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; + struct rustls_connection *rconn = NULL; + size_t n = 0; + size_t plain_bytes_copied = 0; + rustls_result rresult = 0; + ssize_t nread; + bool eof = FALSE; + + DEBUGASSERT(backend); + rconn = backend->conn; + + while(plain_bytes_copied < plainlen) { + if(!backend->data_in_pending) { + if(tls_recv_more(cf, data, err) < 0) { + if(*err != CURLE_AGAIN) { + nread = -1; + goto out; + } + break; + } + } + + rresult = rustls_connection_read(rconn, + (uint8_t *)plainbuf + plain_bytes_copied, + plainlen - plain_bytes_copied, + &n); + if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) { + backend->data_in_pending = FALSE; + } + else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) { + failf(data, "rustls: peer closed TCP connection " + "without first closing TLS connection"); + *err = CURLE_RECV_ERROR; + nread = -1; + goto out; + } + else if(rresult != RUSTLS_RESULT_OK) { + /* n always equals 0 in this case, do not need to check it */ + char errorbuf[255]; + size_t errorlen; + rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); + failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf); + *err = CURLE_RECV_ERROR; + nread = -1; + goto out; + } + else if(n == 0) { + /* n == 0 indicates clean EOF, but we may have read some other + plaintext bytes before we reached this. Break out of the loop + so we can figure out whether to return success or EOF. */ + eof = TRUE; + break; + } + else { + plain_bytes_copied += n; + } + } + + if(plain_bytes_copied) { + *err = CURLE_OK; + nread = (ssize_t)plain_bytes_copied; + } + else if(eof) { + *err = CURLE_OK; + nread = 0; + } + else { + *err = CURLE_AGAIN; + nread = -1; + } + +out: + CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", + plainlen, nread, *err); + return nread; +} + +static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data, + struct rustls_connection *rconn) +{ + struct io_ctx io_ctx; + rustls_io_result io_error; + size_t tlswritten = 0; + size_t tlswritten_total = 0; + CURLcode result = CURLE_OK; + + io_ctx.cf = cf; + io_ctx.data = data; + + while(rustls_connection_wants_write(rconn)) { + io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, + &tlswritten); + if(io_error == EAGAIN || io_error == EWOULDBLOCK) { + CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes", + tlswritten_total); + return CURLE_AGAIN; + } + else if(io_error) { + char buffer[STRERROR_LEN]; + failf(data, "writing to socket: %s", + Curl_strerror(io_error, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + if(tlswritten == 0) { + failf(data, "EOF in swrite"); + return CURLE_SEND_ERROR; + } + CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten); + tlswritten_total += tlswritten; + } + return result; +} + +/* + * On each call: + * - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0). + * - Fully drain rustls' plaintext output buffer into the socket until + * we get either an error or EAGAIN/EWOULDBLOCK. + * + * it is okay to call this function with plainbuf == NULL and plainlen == 0. + * In that case, it will not read anything into rustls' plaintext input buffer. + * It will only drain rustls' plaintext output buffer into the socket. + */ +static ssize_t +cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *plainbuf, size_t plainlen, CURLcode *err) +{ + struct ssl_connect_data *const connssl = cf->ctx; + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; + struct rustls_connection *rconn = NULL; + size_t plainwritten = 0; + rustls_result rresult; + char errorbuf[256]; + size_t errorlen; + const unsigned char *buf = plainbuf; + size_t blen = plainlen; + ssize_t nwritten = 0; + + DEBUGASSERT(backend); + rconn = backend->conn; + DEBUGASSERT(rconn); + + CURL_TRC_CF(data, cf, "cf_send(len=%zu)", plainlen); + + /* If a previous send blocked, we already added its plain bytes + * to rustsls and must not do that again. Flush the TLS bytes and, + * if successful, deduct the previous plain bytes from the current + * send. */ + if(backend->plain_out_buffered) { + *err = cr_flush_out(cf, data, rconn); + CURL_TRC_CF(data, cf, "cf_send: flushing %zu previously added bytes -> %d", + backend->plain_out_buffered, *err); + if(*err) + return -1; + if(blen > backend->plain_out_buffered) { + blen -= backend->plain_out_buffered; + buf += backend->plain_out_buffered; + } + else + blen = 0; + nwritten += (ssize_t)backend->plain_out_buffered; + backend->plain_out_buffered = 0; + } + + if(blen > 0) { + CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to rustls", blen); + rresult = rustls_connection_write(rconn, buf, blen, &plainwritten); + if(rresult != RUSTLS_RESULT_OK) { + rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); + failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf); + *err = CURLE_WRITE_ERROR; + return -1; + } + else if(plainwritten == 0) { + failf(data, "rustls_connection_write: EOF"); + *err = CURLE_WRITE_ERROR; + return -1; + } + } + + *err = cr_flush_out(cf, data, rconn); + if(*err) { + if(CURLE_AGAIN == *err) { + /* The TLS bytes may have been partially written, but we fail the + * complete send() and remember how much we already added to rustls. */ + CURL_TRC_CF(data, cf, "cf_send: EAGAIN, remember we added %zu plain" + " bytes already to rustls", blen); + backend->plain_out_buffered = plainwritten; + if(nwritten) { + *err = CURLE_OK; + return (ssize_t)nwritten; + } + } + return -1; + } + else + nwritten += (ssize_t)plainwritten; + + CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %d, %zd", + plainlen, *err, nwritten); + return nwritten; +} + +/* A server certificate verify callback for rustls that always returns + RUSTLS_RESULT_OK, or in other words disable certificate verification. */ +static uint32_t +cr_verify_none(void *userdata UNUSED_PARAM, + const rustls_verify_server_cert_params *params UNUSED_PARAM) +{ + return RUSTLS_RESULT_OK; +} + +static bool +cr_hostname_is_ip(const char *hostname) +{ + struct in_addr in; +#ifdef USE_IPV6 + struct in6_addr in6; + if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) { + return true; + } +#endif /* USE_IPV6 */ + if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { + return true; + } + return false; +} + +static CURLcode +cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, + struct rustls_ssl_backend_data *const backend) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct rustls_connection *rconn = NULL; + struct rustls_client_config_builder *config_builder = NULL; + const struct rustls_root_cert_store *roots = NULL; + struct rustls_root_cert_store_builder *roots_builder = NULL; + struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL; + struct rustls_server_cert_verifier *server_cert_verifier = NULL; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : conn_config->CAfile); + const bool verifypeer = conn_config->verifypeer; + const char *hostname = connssl->peer.hostname; + char errorbuf[256]; + size_t errorlen; + rustls_result result; + + DEBUGASSERT(backend); + rconn = backend->conn; + + config_builder = rustls_client_config_builder_new(); + if(connssl->alpn) { + struct alpn_proto_buf proto; + rustls_slice_bytes alpn[ALPN_ENTRIES_MAX]; + size_t i; + + for(i = 0; i < connssl->alpn->count; ++i) { + alpn[i].data = (const uint8_t *)connssl->alpn->entries[i]; + alpn[i].len = strlen(connssl->alpn->entries[i]); + } + rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, + connssl->alpn->count); + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } + if(!verifypeer) { + rustls_client_config_builder_dangerous_set_certificate_verifier( + config_builder, cr_verify_none); + /* rustls does not support IP addresses (as of 0.19.0), and will reject + * connections created with an IP address, even when certificate + * verification is turned off. Set a placeholder hostname and disable + * SNI. */ + if(cr_hostname_is_ip(hostname)) { + rustls_client_config_builder_set_enable_sni(config_builder, false); + hostname = "example.invalid"; + } + } + else if(ca_info_blob || ssl_cafile) { + roots_builder = rustls_root_cert_store_builder_new(); + + if(ca_info_blob) { + /* Enable strict parsing only if verification is not disabled. */ + result = rustls_root_cert_store_builder_add_pem(roots_builder, + ca_info_blob->data, + ca_info_blob->len, + verifypeer); + if(result != RUSTLS_RESULT_OK) { + failf(data, "rustls: failed to parse trusted certificates from blob"); + rustls_root_cert_store_builder_free(roots_builder); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + } + else if(ssl_cafile) { + /* Enable strict parsing only if verification is not disabled. */ + result = rustls_root_cert_store_builder_load_roots_from_file( + roots_builder, ssl_cafile, verifypeer); + if(result != RUSTLS_RESULT_OK) { + failf(data, "rustls: failed to load trusted certificates"); + rustls_root_cert_store_builder_free(roots_builder); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + } + + result = rustls_root_cert_store_builder_build(roots_builder, &roots); + rustls_root_cert_store_builder_free(roots_builder); + if(result != RUSTLS_RESULT_OK) { + failf(data, "rustls: failed to load trusted certificates"); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + + verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots); + + result = rustls_web_pki_server_cert_verifier_builder_build( + verifier_builder, &server_cert_verifier); + rustls_web_pki_server_cert_verifier_builder_free(verifier_builder); + if(result != RUSTLS_RESULT_OK) { + failf(data, "rustls: failed to load trusted certificates"); + rustls_server_cert_verifier_free(server_cert_verifier); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + + rustls_client_config_builder_set_server_verifier(config_builder, + server_cert_verifier); + } + + backend->config = rustls_client_config_builder_build(config_builder); + DEBUGASSERT(rconn == NULL); + result = rustls_client_connection_new(backend->config, + connssl->peer.hostname, &rconn); + if(result != RUSTLS_RESULT_OK) { + rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); + failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf); + return CURLE_COULDNT_CONNECT; + } + DEBUGASSERT(rconn); + rustls_connection_set_userdata(rconn, backend); + backend->conn = rconn; + return CURLE_OK; +} + +static void +cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data, + const struct rustls_connection *rconn) +{ + const uint8_t *protocol = NULL; + size_t len = 0; + + rustls_connection_get_alpn_protocol(rconn, &protocol, &len); + Curl_alpn_set_negotiated(cf, data, protocol, len); +} + +/* Given an established network connection, do a TLS handshake. + * + * If `blocking` is true, this function will block until the handshake is + * complete. Otherwise it will return as soon as I/O would block. + * + * For the non-blocking I/O case, this function will set `*done` to true + * once the handshake is complete. This function never reads the value of + * `*done*`. + */ +static CURLcode +cr_connect_common(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, + bool *done) +{ + struct ssl_connect_data *const connssl = cf->ctx; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; + struct rustls_connection *rconn = NULL; + CURLcode tmperr = CURLE_OK; + int result; + int what; + bool wants_read; + bool wants_write; + curl_socket_t writefd; + curl_socket_t readfd; + timediff_t timeout_ms; + timediff_t socket_check_timeout; + + DEBUGASSERT(backend); + + CURL_TRC_CF(data, cf, "cr_connect_common, state=%d", connssl->state); + *done = FALSE; + if(!backend->conn) { + result = cr_init_backend(cf, data, + (struct rustls_ssl_backend_data *)connssl->backend); + CURL_TRC_CF(data, cf, "cr_connect_common, init backend -> %d", result); + if(result != CURLE_OK) { + return result; + } + connssl->state = ssl_connection_negotiating; + } + + rconn = backend->conn; + + /* Read/write data until the handshake is done or the socket would block. */ + for(;;) { + /* + * Connection has been established according to rustls. Set send/recv + * handlers, and update the state machine. + */ + connssl->io_need = CURL_SSL_IO_NEED_NONE; + if(!rustls_connection_is_handshaking(rconn)) { + infof(data, "Done handshaking"); + /* rustls claims it is no longer handshaking *before* it has + * send its FINISHED message off. We attempt to let it write + * one more time. Oh my. + */ + cr_set_negotiated_alpn(cf, data, rconn); + cr_send(cf, data, NULL, 0, &tmperr); + if(tmperr == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_SEND; + return CURLE_OK; + } + else if(tmperr != CURLE_OK) { + return tmperr; + } + /* REALLY Done with the handshake. */ + connssl->state = ssl_connection_complete; + *done = TRUE; + return CURLE_OK; + } + + connssl->connecting_state = ssl_connect_2; + wants_read = rustls_connection_wants_read(rconn); + wants_write = rustls_connection_wants_write(rconn) || + backend->plain_out_buffered; + DEBUGASSERT(wants_read || wants_write); + writefd = wants_write?sockfd:CURL_SOCKET_BAD; + readfd = wants_read?sockfd:CURL_SOCKET_BAD; + + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "rustls: operation timed out before socket check"); + return CURLE_OPERATION_TIMEDOUT; + } + + socket_check_timeout = blocking?timeout_ms:0; + + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + socket_check_timeout); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + return CURLE_SSL_CONNECT_ERROR; + } + if(blocking && 0 == what) { + failf(data, "rustls connection timeout after %" + CURL_FORMAT_TIMEDIFF_T " ms", socket_check_timeout); + return CURLE_OPERATION_TIMEDOUT; + } + if(0 == what) { + CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block", + wants_read&&wants_write ? "writing and reading" : + wants_write ? "writing" : "reading"); + if(wants_write) + connssl->io_need |= CURL_SSL_IO_NEED_SEND; + if(wants_read) + connssl->io_need |= CURL_SSL_IO_NEED_RECV; + return CURLE_OK; + } + /* socket is readable or writable */ + + if(wants_write) { + CURL_TRC_CF(data, cf, "rustls_connection wants us to write_tls."); + cr_send(cf, data, NULL, 0, &tmperr); + if(tmperr == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "writing would block"); + /* fall through */ + } + else if(tmperr != CURLE_OK) { + return tmperr; + } + } + + if(wants_read) { + CURL_TRC_CF(data, cf, "rustls_connection wants us to read_tls."); + if(tls_recv_more(cf, data, &tmperr) < 0) { + if(tmperr == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "reading would block"); + /* fall through */ + } + else if(tmperr == CURLE_RECV_ERROR) { + return CURLE_SSL_CONNECT_ERROR; + } + else { + return tmperr; + } + } + } + } + + /* We should never fall through the loop. We should return either because + the handshake is done or because we cannot read/write without blocking. */ + DEBUGASSERT(false); +} + +static CURLcode +cr_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + return cr_connect_common(cf, data, false, done); +} + +static CURLcode +cr_connect_blocking(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + bool done; /* unused */ + return cr_connect_common(cf, data, true, &done); +} + +static void * +cr_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) +{ + struct rustls_ssl_backend_data *backend = + (struct rustls_ssl_backend_data *)connssl->backend; + DEBUGASSERT(backend); + return &backend->conn; +} + +static CURLcode +cr_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct rustls_ssl_backend_data *backend = + (struct rustls_ssl_backend_data *)connssl->backend; + CURLcode result = CURLE_OK; + ssize_t nwritten, nread; + char buf[1024]; + size_t i; + + DEBUGASSERT(backend); + if(!backend->conn || cf->shutdown) { + *done = TRUE; + goto out; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + + if(!backend->sent_shutdown) { + /* do this only once */ + backend->sent_shutdown = TRUE; + if(send_shutdown) { + rustls_connection_send_close_notify(backend->conn); + } + } + + nwritten = cr_send(cf, data, NULL, 0, &result); + if(nwritten < 0) { + if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_SEND; + result = CURLE_OK; + goto out; + } + DEBUGASSERT(result); + CURL_TRC_CF(data, cf, "shutdown send failed: %d", result); + goto out; + } + + for(i = 0; i < 10; ++i) { + nread = cr_recv(cf, data, buf, (int)sizeof(buf), &result); + if(nread <= 0) + break; + } + + if(nread > 0) { + /* still data coming in? */ + } + else if(nread == 0) { + /* We got the close notify alert and are done. */ + *done = TRUE; + } + else if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + result = CURLE_OK; + } + else { + DEBUGASSERT(result); + CURL_TRC_CF(data, cf, "shutdown, error: %d", result); + } + +out: + cf->shutdown = (result || *done); + return result; +} + +static void +cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct rustls_ssl_backend_data *backend = + (struct rustls_ssl_backend_data *)connssl->backend; + + (void)data; + DEBUGASSERT(backend); + if(backend->conn) { + rustls_connection_free(backend->conn); + backend->conn = NULL; + } + if(backend->config) { + rustls_client_config_free(backend->config); + backend->config = NULL; + } +} + +static size_t cr_version(char *buffer, size_t size) +{ + struct rustls_str ver = rustls_version(); + return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data); +} + +const struct Curl_ssl Curl_ssl_rustls = { + { CURLSSLBACKEND_RUSTLS, "rustls" }, + SSLSUPP_CAINFO_BLOB | /* supports */ + SSLSUPP_HTTPS_PROXY, + sizeof(struct rustls_ssl_backend_data), + + Curl_none_init, /* init */ + Curl_none_cleanup, /* cleanup */ + cr_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + cr_shutdown, /* shutdown */ + cr_data_pending, /* data_pending */ + Curl_none_random, /* random */ + Curl_none_cert_status_request, /* cert_status_request */ + cr_connect_blocking, /* connect */ + cr_connect_nonblocking, /* connect_nonblocking */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ + cr_get_internals, /* get_internals */ + cr_close, /* close_one */ + Curl_none_close_all, /* close_all */ + Curl_none_set_engine, /* set_engine */ + Curl_none_set_engine_default, /* set_engine_default */ + Curl_none_engines_list, /* engines_list */ + Curl_none_false_start, /* false_start */ + NULL, /* sha256sum */ + NULL, /* associate_connection */ + NULL, /* disassociate_connection */ + cr_recv, /* recv decrypted data */ + cr_send, /* send data to encrypt */ +}; + +#endif /* USE_RUSTLS */ diff --git a/third_party/curl/lib/vtls/rustls.h b/third_party/curl/lib/vtls/rustls.h new file mode 100644 index 0000000000..bfbe23de3e --- /dev/null +++ b/third_party/curl/lib/vtls/rustls.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Jacob Hoffman-Andrews, + * + * + * 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 + * + ***************************************************************************/ +#ifndef HEADER_CURL_RUSTLS_H +#define HEADER_CURL_RUSTLS_H + +#include "curl_setup.h" + +#ifdef USE_RUSTLS + +extern const struct Curl_ssl Curl_ssl_rustls; + +#endif /* USE_RUSTLS */ +#endif /* HEADER_CURL_RUSTLS_H */ diff --git a/third_party/curl/lib/vtls/schannel.c b/third_party/curl/lib/vtls/schannel.c new file mode 100644 index 0000000000..f9bb2f8247 --- /dev/null +++ b/third_party/curl/lib/vtls/schannel.c @@ -0,0 +1,3001 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Marc Hoersken, + * Copyright (C) Mark Salisbury, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Source file for all Schannel-specific code for the TLS/SSL layer. No code + * but vtls.c should ever call or use these functions. + */ + +#include "curl_setup.h" + +#ifdef USE_SCHANNEL + +#ifndef USE_WINDOWS_SSPI +# error "cannot compile SCHANNEL support without SSPI." +#endif + +#include "schannel.h" +#include "schannel_int.h" +#include "vtls.h" +#include "vtls_int.h" +#include "strcase.h" +#include "sendf.h" +#include "connect.h" /* for the connect timeout */ +#include "strerror.h" +#include "select.h" /* for the socket readiness */ +#include "inet_pton.h" /* for IP addr SNI check */ +#include "curl_multibyte.h" +#include "warnless.h" +#include "x509asn1.h" +#include "curl_printf.h" +#include "multiif.h" +#include "version_win32.h" +#include "rand.h" + +/* The last #include file should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* ALPN requires version 8.1 of the Windows SDK, which was + shipped with Visual Studio 2013, aka _MSC_VER 1800: + + https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_) +# define HAS_ALPN 1 +#endif + +#ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM +#define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305" +#endif + +#ifndef BCRYPT_CHAIN_MODE_CCM +#define BCRYPT_CHAIN_MODE_CCM L"ChainingModeCCM" +#endif + +#ifndef BCRYPT_CHAIN_MODE_GCM +#define BCRYPT_CHAIN_MODE_GCM L"ChainingModeGCM" +#endif + +#ifndef BCRYPT_AES_ALGORITHM +#define BCRYPT_AES_ALGORITHM L"AES" +#endif + +#ifndef BCRYPT_SHA256_ALGORITHM +#define BCRYPT_SHA256_ALGORITHM L"SHA256" +#endif + +#ifndef BCRYPT_SHA384_ALGORITHM +#define BCRYPT_SHA384_ALGORITHM L"SHA384" +#endif + +#ifdef HAS_CLIENT_CERT_PATH +#ifdef UNICODE +#define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W +#else +#define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_A +#endif +#endif + +#ifndef SP_PROT_TLS1_0_CLIENT +#define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT +#endif + +#ifndef SP_PROT_TLS1_1_CLIENT +#define SP_PROT_TLS1_1_CLIENT 0x00000200 +#endif + +#ifndef SP_PROT_TLS1_2_CLIENT +#define SP_PROT_TLS1_2_CLIENT 0x00000800 +#endif + +#ifndef SP_PROT_TLS1_3_CLIENT +#define SP_PROT_TLS1_3_CLIENT 0x00002000 +#endif + +#ifndef SCH_USE_STRONG_CRYPTO +#define SCH_USE_STRONG_CRYPTO 0x00400000 +#endif + +#ifndef SECBUFFER_ALERT +#define SECBUFFER_ALERT 17 +#endif + +/* Both schannel buffer sizes must be > 0 */ +#define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096 +#define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024 + +#define CERT_THUMBPRINT_STR_LEN 40 +#define CERT_THUMBPRINT_DATA_LEN 20 + +/* Uncomment to force verbose output + * #define infof(x, y, ...) printf(y, __VA_ARGS__) + * #define failf(x, y, ...) printf(y, __VA_ARGS__) + */ + +#ifndef CALG_SHA_256 +# define CALG_SHA_256 0x0000800c +#endif + +#ifndef PKCS12_NO_PERSIST_KEY +#define PKCS12_NO_PERSIST_KEY 0x00008000 +#endif + +static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *pinnedpubkey); + +static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType, + void *BufDataPtr, unsigned long BufByteSize) +{ + buffer->cbBuffer = BufByteSize; + buffer->BufferType = BufType; + buffer->pvBuffer = BufDataPtr; +} + +static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr, + unsigned long NumArrElem) +{ + desc->ulVersion = SECBUFFER_VERSION; + desc->pBuffers = BufArr; + desc->cBuffers = NumArrElem; +} + +static CURLcode +schannel_set_ssl_version_min_max(DWORD *enabled_protocols, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + long ssl_version = conn_config->version; + long ssl_version_max = (long)conn_config->version_max; + long i = ssl_version; + + switch(ssl_version_max) { + case CURL_SSLVERSION_MAX_NONE: + case CURL_SSLVERSION_MAX_DEFAULT: + + /* Windows Server 2022 and newer (including Windows 11) support TLS 1.3 + built-in. Previous builds of Windows 10 had broken TLS 1.3 + implementations that could be enabled via registry. + */ + if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; + } + else /* Windows 10 and older */ + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; + + break; + } + + for(; i <= (ssl_version_max >> 16); ++i) { + switch(i) { + case CURL_SSLVERSION_TLSv1_0: + (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_1: + (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_2: + (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_3: + + /* Windows Server 2022 and newer */ + if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + (*enabled_protocols) |= SP_PROT_TLS1_3_CLIENT; + break; + } + else { /* Windows 10 and older */ + failf(data, "schannel: TLS 1.3 not supported on Windows prior to 11"); + return CURLE_SSL_CONNECT_ERROR; + } + } + } + return CURLE_OK; +} + +/* longest is 26, buffer is slightly bigger */ +#define LONGEST_ALG_ID 32 +#define CIPHEROPTION(x) {#x, x} + +struct algo { + const char *name; + int id; +}; + +static const struct algo algs[]= { + CIPHEROPTION(CALG_MD2), + CIPHEROPTION(CALG_MD4), + CIPHEROPTION(CALG_MD5), + CIPHEROPTION(CALG_SHA), + CIPHEROPTION(CALG_SHA1), + CIPHEROPTION(CALG_MAC), + CIPHEROPTION(CALG_RSA_SIGN), + CIPHEROPTION(CALG_DSS_SIGN), +/* ifdefs for the options that are defined conditionally in wincrypt.h */ +#ifdef CALG_NO_SIGN + CIPHEROPTION(CALG_NO_SIGN), +#endif + CIPHEROPTION(CALG_RSA_KEYX), + CIPHEROPTION(CALG_DES), +#ifdef CALG_3DES_112 + CIPHEROPTION(CALG_3DES_112), +#endif + CIPHEROPTION(CALG_3DES), + CIPHEROPTION(CALG_DESX), + CIPHEROPTION(CALG_RC2), + CIPHEROPTION(CALG_RC4), + CIPHEROPTION(CALG_SEAL), +#ifdef CALG_DH_SF + CIPHEROPTION(CALG_DH_SF), +#endif + CIPHEROPTION(CALG_DH_EPHEM), +#ifdef CALG_AGREEDKEY_ANY + CIPHEROPTION(CALG_AGREEDKEY_ANY), +#endif +#ifdef CALG_HUGHES_MD5 + CIPHEROPTION(CALG_HUGHES_MD5), +#endif + CIPHEROPTION(CALG_SKIPJACK), +#ifdef CALG_TEK + CIPHEROPTION(CALG_TEK), +#endif + CIPHEROPTION(CALG_CYLINK_MEK), + CIPHEROPTION(CALG_SSL3_SHAMD5), +#ifdef CALG_SSL3_MASTER + CIPHEROPTION(CALG_SSL3_MASTER), +#endif +#ifdef CALG_SCHANNEL_MASTER_HASH + CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH), +#endif +#ifdef CALG_SCHANNEL_MAC_KEY + CIPHEROPTION(CALG_SCHANNEL_MAC_KEY), +#endif +#ifdef CALG_SCHANNEL_ENC_KEY + CIPHEROPTION(CALG_SCHANNEL_ENC_KEY), +#endif +#ifdef CALG_PCT1_MASTER + CIPHEROPTION(CALG_PCT1_MASTER), +#endif +#ifdef CALG_SSL2_MASTER + CIPHEROPTION(CALG_SSL2_MASTER), +#endif +#ifdef CALG_TLS1_MASTER + CIPHEROPTION(CALG_TLS1_MASTER), +#endif +#ifdef CALG_RC5 + CIPHEROPTION(CALG_RC5), +#endif +#ifdef CALG_HMAC + CIPHEROPTION(CALG_HMAC), +#endif +#ifdef CALG_TLS1PRF + CIPHEROPTION(CALG_TLS1PRF), +#endif +#ifdef CALG_HASH_REPLACE_OWF + CIPHEROPTION(CALG_HASH_REPLACE_OWF), +#endif +#ifdef CALG_AES_128 + CIPHEROPTION(CALG_AES_128), +#endif +#ifdef CALG_AES_192 + CIPHEROPTION(CALG_AES_192), +#endif +#ifdef CALG_AES_256 + CIPHEROPTION(CALG_AES_256), +#endif +#ifdef CALG_AES + CIPHEROPTION(CALG_AES), +#endif +#ifdef CALG_SHA_256 + CIPHEROPTION(CALG_SHA_256), +#endif +#ifdef CALG_SHA_384 + CIPHEROPTION(CALG_SHA_384), +#endif +#ifdef CALG_SHA_512 + CIPHEROPTION(CALG_SHA_512), +#endif +#ifdef CALG_ECDH + CIPHEROPTION(CALG_ECDH), +#endif +#ifdef CALG_ECMQV + CIPHEROPTION(CALG_ECMQV), +#endif +#ifdef CALG_ECDSA + CIPHEROPTION(CALG_ECDSA), +#endif +#ifdef CALG_ECDH_EPHEM + CIPHEROPTION(CALG_ECDH_EPHEM), +#endif + {NULL, 0}, +}; + +static int +get_alg_id_by_name(char *name) +{ + char *nameEnd = strchr(name, ':'); + size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name); + int i; + + for(i = 0; algs[i].name; i++) { + if((n == strlen(algs[i].name) && !strncmp(algs[i].name, name, n))) + return algs[i].id; + } + return 0; /* not found */ +} + +#define NUM_CIPHERS 47 /* There are 47 options listed above */ + +static CURLcode +set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, + ALG_ID *algIds) +{ + char *startCur = ciphers; + int algCount = 0; + while(startCur && (0 != *startCur) && (algCount < NUM_CIPHERS)) { + long alg = strtol(startCur, 0, 0); + if(!alg) + alg = get_alg_id_by_name(startCur); + if(alg) + algIds[algCount++] = (ALG_ID)alg; + else if(!strncmp(startCur, "USE_STRONG_CRYPTO", + sizeof("USE_STRONG_CRYPTO") - 1) || + !strncmp(startCur, "SCH_USE_STRONG_CRYPTO", + sizeof("SCH_USE_STRONG_CRYPTO") - 1)) + schannel_cred->dwFlags |= SCH_USE_STRONG_CRYPTO; + else + return CURLE_SSL_CIPHER; + startCur = strchr(startCur, ':'); + if(startCur) + startCur++; + } + schannel_cred->palgSupportedAlgs = algIds; + schannel_cred->cSupportedAlgs = (DWORD)algCount; + return CURLE_OK; +} + +#ifdef HAS_CLIENT_CERT_PATH + +/* Function allocates memory for store_path only if CURLE_OK is returned */ +static CURLcode +get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, + TCHAR **thumbprint) +{ + TCHAR *sep; + TCHAR *store_path_start; + size_t store_name_len; + + sep = _tcschr(path, TEXT('\\')); + if(!sep) + return CURLE_SSL_CERTPROBLEM; + + store_name_len = sep - path; + + if(_tcsncmp(path, TEXT("CurrentUser"), store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_CURRENT_USER; + else if(_tcsncmp(path, TEXT("LocalMachine"), store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE; + else if(_tcsncmp(path, TEXT("CurrentService"), store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE; + else if(_tcsncmp(path, TEXT("Services"), store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_SERVICES; + else if(_tcsncmp(path, TEXT("Users"), store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_USERS; + else if(_tcsncmp(path, TEXT("CurrentUserGroupPolicy"), + store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY; + else if(_tcsncmp(path, TEXT("LocalMachineGroupPolicy"), + store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY; + else if(_tcsncmp(path, TEXT("LocalMachineEnterprise"), + store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; + else + return CURLE_SSL_CERTPROBLEM; + + store_path_start = sep + 1; + + sep = _tcschr(store_path_start, TEXT('\\')); + if(!sep) + return CURLE_SSL_CERTPROBLEM; + + *thumbprint = sep + 1; + if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN) + return CURLE_SSL_CERTPROBLEM; + + *sep = TEXT('\0'); + *store_path = _tcsdup(store_path_start); + *sep = TEXT('\\'); + if(!*store_path) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} +#endif + +static bool algo(const char *check, char *namep, size_t nlen) +{ + return (strlen(check) == nlen) && !strncmp(check, namep, nlen); +} + +static CURLcode +schannel_acquire_credential_handle(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + +#ifdef HAS_CLIENT_CERT_PATH + PCCERT_CONTEXT client_certs[1] = { NULL }; + HCERTSTORE client_cert_store = NULL; +#endif + SECURITY_STATUS sspi_status = SEC_E_OK; + CURLcode result; + + /* setup Schannel API options */ + DWORD flags = 0; + DWORD enabled_protocols = 0; + + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)(connssl->backend); + + DEBUGASSERT(backend); + + if(conn_config->verifypeer) { +#ifdef HAS_MANUAL_VERIFY_API + if(backend->use_manual_cred_validation) + flags = SCH_CRED_MANUAL_CRED_VALIDATION; + else +#endif + flags = SCH_CRED_AUTO_CRED_VALIDATION; + + if(ssl_config->no_revoke) { + flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE; + + DEBUGF(infof(data, "schannel: disabled server certificate revocation " + "checks")); + } + else if(ssl_config->revoke_best_effort) { + flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN; + + DEBUGF(infof(data, "schannel: ignore revocation offline errors")); + } + else { + flags |= SCH_CRED_REVOCATION_CHECK_CHAIN; + + DEBUGF(infof(data, + "schannel: checking server certificate revocation")); + } + } + else { + flags = SCH_CRED_MANUAL_CRED_VALIDATION | + SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE; + DEBUGF(infof(data, + "schannel: disabled server cert revocation checks")); + } + + if(!conn_config->verifyhost) { + flags |= SCH_CRED_NO_SERVERNAME_CHECK; + DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from " + "comparing the supplied target name with the subject " + "names in server certificates.")); + } + + if(!ssl_config->auto_client_cert) { + flags &= ~(DWORD)SCH_CRED_USE_DEFAULT_CREDS; + flags |= SCH_CRED_NO_DEFAULT_CREDS; + infof(data, "schannel: disabled automatic use of client certificate"); + } + else + infof(data, "schannel: enabled automatic use of client certificate"); + + switch(conn_config->version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + case CURL_SSLVERSION_TLSv1_3: + { + result = schannel_set_ssl_version_min_max(&enabled_protocols, cf, data); + if(result != CURLE_OK) + return result; + break; + } + case CURL_SSLVERSION_SSLv3: + case CURL_SSLVERSION_SSLv2: + failf(data, "SSL versions not supported"); + return CURLE_NOT_BUILT_IN; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + +#ifdef HAS_CLIENT_CERT_PATH + /* client certificate */ + if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { + DWORD cert_store_name = 0; + TCHAR *cert_store_path = NULL; + TCHAR *cert_thumbprint_str = NULL; + CRYPT_HASH_BLOB cert_thumbprint; + BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; + HCERTSTORE cert_store = NULL; + FILE *fInCert = NULL; + void *certdata = NULL; + size_t certsize = 0; + bool blob = data->set.ssl.primary.cert_blob != NULL; + TCHAR *cert_path = NULL; + if(blob) { + certdata = data->set.ssl.primary.cert_blob->data; + certsize = data->set.ssl.primary.cert_blob->len; + } + else { + cert_path = curlx_convert_UTF8_to_tchar( + data->set.ssl.primary.clientcert); + if(!cert_path) + return CURLE_OUT_OF_MEMORY; + + result = get_cert_location(cert_path, &cert_store_name, + &cert_store_path, &cert_thumbprint_str); + + if(result && (data->set.ssl.primary.clientcert[0]!='\0')) + fInCert = fopen(data->set.ssl.primary.clientcert, "rb"); + + if(result && !fInCert) { + failf(data, "schannel: Failed to get certificate location" + " or file for %s", + data->set.ssl.primary.clientcert); + curlx_unicodefree(cert_path); + return result; + } + } + + if((fInCert || blob) && (data->set.ssl.cert_type) && + (!strcasecompare(data->set.ssl.cert_type, "P12"))) { + failf(data, "schannel: certificate format compatibility error " + " for %s", + blob ? "(memory blob)" : data->set.ssl.primary.clientcert); + curlx_unicodefree(cert_path); + return CURLE_SSL_CERTPROBLEM; + } + + if(fInCert || blob) { + /* Reading a .P12 or .pfx file, like the example at bottom of + https://social.msdn.microsoft.com/Forums/windowsdesktop/ + en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5 + */ + CRYPT_DATA_BLOB datablob; + WCHAR* pszPassword; + size_t pwd_len = 0; + int str_w_len = 0; + const char *cert_showfilename_error = blob ? + "(memory blob)" : data->set.ssl.primary.clientcert; + curlx_unicodefree(cert_path); + if(fInCert) { + long cert_tell = 0; + bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; + if(continue_reading) + cert_tell = ftell(fInCert); + if(cert_tell < 0) + continue_reading = FALSE; + else + certsize = (size_t)cert_tell; + if(continue_reading) + continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; + if(continue_reading) + certdata = malloc(certsize + 1); + if((!certdata) || + ((int) fread(certdata, certsize, 1, fInCert) != 1)) + continue_reading = FALSE; + fclose(fInCert); + if(!continue_reading) { + failf(data, "schannel: Failed to read cert file %s", + data->set.ssl.primary.clientcert); + free(certdata); + return CURLE_SSL_CERTPROBLEM; + } + } + + /* Convert key-pair data to the in-memory certificate store */ + datablob.pbData = (BYTE*)certdata; + datablob.cbData = (DWORD)certsize; + + if(data->set.ssl.key_passwd) + pwd_len = strlen(data->set.ssl.key_passwd); + pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1)); + if(pszPassword) { + if(pwd_len > 0) + str_w_len = MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + data->set.ssl.key_passwd, + (int)pwd_len, + pszPassword, (int)(pwd_len + 1)); + + if((str_w_len >= 0) && (str_w_len <= (int)pwd_len)) + pszPassword[str_w_len] = 0; + else + pszPassword[0] = 0; + + if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) + cert_store = PFXImportCertStore(&datablob, pszPassword, + PKCS12_NO_PERSIST_KEY); + else + cert_store = PFXImportCertStore(&datablob, pszPassword, 0); + + free(pszPassword); + } + if(!blob) + free(certdata); + if(!cert_store) { + DWORD errorcode = GetLastError(); + if(errorcode == ERROR_INVALID_PASSWORD) + failf(data, "schannel: Failed to import cert file %s, " + "password is bad", + cert_showfilename_error); + else + failf(data, "schannel: Failed to import cert file %s, " + "last error is 0x%lx", + cert_showfilename_error, errorcode); + return CURLE_SSL_CERTPROBLEM; + } + + client_certs[0] = CertFindCertificateInStore( + cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + CERT_FIND_ANY, NULL, NULL); + + if(!client_certs[0]) { + failf(data, "schannel: Failed to get certificate from file %s" + ", last error is 0x%lx", + cert_showfilename_error, GetLastError()); + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } + } + else { + cert_store = + CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, + (HCRYPTPROV)NULL, + CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name, + cert_store_path); + if(!cert_store) { + char *path_utf8 = + curlx_convert_tchar_to_UTF8(cert_store_path); + failf(data, "schannel: Failed to open cert store %lx %s, " + "last error is 0x%lx", + cert_store_name, + (path_utf8 ? path_utf8 : "(unknown)"), + GetLastError()); + free(cert_store_path); + curlx_unicodefree(path_utf8); + curlx_unicodefree(cert_path); + return CURLE_SSL_CERTPROBLEM; + } + free(cert_store_path); + + cert_thumbprint.pbData = cert_thumbprint_data; + cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; + + if(!CryptStringToBinary(cert_thumbprint_str, + CERT_THUMBPRINT_STR_LEN, + CRYPT_STRING_HEX, + cert_thumbprint_data, + &cert_thumbprint.cbData, + NULL, NULL)) { + curlx_unicodefree(cert_path); + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } + + client_certs[0] = CertFindCertificateInStore( + cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + CERT_FIND_HASH, &cert_thumbprint, NULL); + + curlx_unicodefree(cert_path); + + if(!client_certs[0]) { + /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } + } + client_cert_store = cert_store; + } +#else + if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { + failf(data, "schannel: client cert support not built in"); + return CURLE_NOT_BUILT_IN; + } +#endif + + /* allocate memory for the reusable credential handle */ + backend->cred = (struct Curl_schannel_cred *) + calloc(1, sizeof(struct Curl_schannel_cred)); + if(!backend->cred) { + failf(data, "schannel: unable to allocate memory"); + +#ifdef HAS_CLIENT_CERT_PATH + if(client_certs[0]) + CertFreeCertificateContext(client_certs[0]); + if(client_cert_store) + CertCloseStore(client_cert_store, 0); +#endif + + return CURLE_OUT_OF_MEMORY; + } + backend->cred->refcount = 1; + +#ifdef HAS_CLIENT_CERT_PATH + /* Since we did not persist the key, we need to extend the store's + * lifetime until the end of the connection + */ + backend->cred->client_cert_store = client_cert_store; +#endif + + /* We support TLS 1.3 starting in Windows 10 version 1809 (OS build 17763) as + long as the user did not set a legacy algorithm list + (CURLOPT_SSL_CIPHER_LIST). */ + if(!conn_config->cipher_list && + curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + + char *ciphers13 = 0; + + bool disable_aes_gcm_sha384 = FALSE; + bool disable_aes_gcm_sha256 = FALSE; + bool disable_chacha_poly = FALSE; + bool disable_aes_ccm_8_sha256 = FALSE; + bool disable_aes_ccm_sha256 = FALSE; + + SCH_CREDENTIALS credentials = { 0 }; + TLS_PARAMETERS tls_parameters = { 0 }; + CRYPTO_SETTINGS crypto_settings[4] = { { 0 } }; + UNICODE_STRING blocked_ccm_modes[1] = { { 0 } }; + UNICODE_STRING blocked_gcm_modes[1] = { { 0 } }; + + int crypto_settings_idx = 0; + + + /* If TLS 1.3 ciphers are explicitly listed, then + * disable all the ciphers and re-enable which + * ciphers the user has provided. + */ + ciphers13 = conn_config->cipher_list13; + if(ciphers13) { + const int remaining_ciphers = 5; + + /* detect which remaining ciphers to enable + and then disable everything else. + */ + + char *startCur = ciphers13; + int algCount = 0; + char *nameEnd; + + disable_aes_gcm_sha384 = TRUE; + disable_aes_gcm_sha256 = TRUE; + disable_chacha_poly = TRUE; + disable_aes_ccm_8_sha256 = TRUE; + disable_aes_ccm_sha256 = TRUE; + + while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) { + size_t n; + char *namep; + nameEnd = strchr(startCur, ':'); + n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur); + namep = startCur; + + if(disable_aes_gcm_sha384 && + algo("TLS_AES_256_GCM_SHA384", namep, n)) { + disable_aes_gcm_sha384 = FALSE; + } + else if(disable_aes_gcm_sha256 + && algo("TLS_AES_128_GCM_SHA256", namep, n)) { + disable_aes_gcm_sha256 = FALSE; + } + else if(disable_chacha_poly + && algo("TLS_CHACHA20_POLY1305_SHA256", namep, n)) { + disable_chacha_poly = FALSE; + } + else if(disable_aes_ccm_8_sha256 + && algo("TLS_AES_128_CCM_8_SHA256", namep, n)) { + disable_aes_ccm_8_sha256 = FALSE; + } + else if(disable_aes_ccm_sha256 + && algo("TLS_AES_128_CCM_SHA256", namep, n)) { + disable_aes_ccm_sha256 = FALSE; + } + else { + failf(data, "schannel: Unknown TLS 1.3 cipher: %.*s", (int)n, namep); + return CURLE_SSL_CIPHER; + } + + startCur = nameEnd; + if(startCur) + startCur++; + + algCount++; + } + } + + if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256 + && disable_chacha_poly && disable_aes_ccm_8_sha256 + && disable_aes_ccm_sha256) { + failf(data, "schannel: All available TLS 1.3 ciphers were disabled"); + return CURLE_SSL_CIPHER; + } + + /* Disable TLS_AES_128_CCM_8_SHA256 and/or TLS_AES_128_CCM_SHA256 */ + if(disable_aes_ccm_8_sha256 || disable_aes_ccm_sha256) { + /* + Disallow AES_CCM algorithm. + */ + blocked_ccm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_CCM); + blocked_ccm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_CCM); + blocked_ccm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_CCM; + + crypto_settings[crypto_settings_idx].eAlgorithmUsage = + TlsParametersCngAlgUsageCipher; + crypto_settings[crypto_settings_idx].rgstrChainingModes = + blocked_ccm_modes; + crypto_settings[crypto_settings_idx].cChainingModes = + ARRAYSIZE(blocked_ccm_modes); + crypto_settings[crypto_settings_idx].strCngAlgId.Length = + sizeof(BCRYPT_AES_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = + sizeof(BCRYPT_AES_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = + (PWSTR)BCRYPT_AES_ALGORITHM; + + /* only disabling one of the CCM modes */ + if(disable_aes_ccm_8_sha256 != disable_aes_ccm_sha256) { + if(disable_aes_ccm_8_sha256) + crypto_settings[crypto_settings_idx].dwMinBitLength = 128; + else /* disable_aes_ccm_sha256 */ + crypto_settings[crypto_settings_idx].dwMaxBitLength = 64; + } + + crypto_settings_idx++; + } + + /* Disable TLS_AES_256_GCM_SHA384 and/or TLS_AES_128_GCM_SHA256 */ + if(disable_aes_gcm_sha384 || disable_aes_gcm_sha256) { + + /* + Disallow AES_GCM algorithm + */ + blocked_gcm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_GCM); + blocked_gcm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_GCM); + blocked_gcm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_GCM; + + /* if only one is disabled, then explicitly disable the + digest cipher suite (sha384 or sha256) */ + if(disable_aes_gcm_sha384 != disable_aes_gcm_sha256) { + crypto_settings[crypto_settings_idx].eAlgorithmUsage = + TlsParametersCngAlgUsageDigest; + crypto_settings[crypto_settings_idx].strCngAlgId.Length = + sizeof(disable_aes_gcm_sha384 ? + BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = + sizeof(disable_aes_gcm_sha384 ? + BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = + (PWSTR)(disable_aes_gcm_sha384 ? + BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM); + } + else { /* Disable both AES_GCM ciphers */ + crypto_settings[crypto_settings_idx].eAlgorithmUsage = + TlsParametersCngAlgUsageCipher; + crypto_settings[crypto_settings_idx].strCngAlgId.Length = + sizeof(BCRYPT_AES_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = + sizeof(BCRYPT_AES_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = + (PWSTR)BCRYPT_AES_ALGORITHM; + } + + crypto_settings[crypto_settings_idx].rgstrChainingModes = + blocked_gcm_modes; + crypto_settings[crypto_settings_idx].cChainingModes = 1; + + crypto_settings_idx++; + } + + /* + Disable ChaCha20-Poly1305. + */ + if(disable_chacha_poly) { + crypto_settings[crypto_settings_idx].eAlgorithmUsage = + TlsParametersCngAlgUsageCipher; + crypto_settings[crypto_settings_idx].strCngAlgId.Length = + sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = + sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = + (PWSTR)BCRYPT_CHACHA20_POLY1305_ALGORITHM; + crypto_settings_idx++; + } + + tls_parameters.pDisabledCrypto = crypto_settings; + + /* The number of blocked suites */ + tls_parameters.cDisabledCrypto = (DWORD)crypto_settings_idx; + credentials.pTlsParameters = &tls_parameters; + credentials.cTlsParameters = 1; + + credentials.dwVersion = SCH_CREDENTIALS_VERSION; + credentials.dwFlags = flags | SCH_USE_STRONG_CRYPTO; + + credentials.pTlsParameters->grbitDisabledProtocols = + (DWORD)~enabled_protocols; + +#ifdef HAS_CLIENT_CERT_PATH + if(client_certs[0]) { + credentials.cCreds = 1; + credentials.paCred = client_certs; + } +#endif + + sspi_status = + s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME, + SECPKG_CRED_OUTBOUND, NULL, + &credentials, NULL, NULL, + &backend->cred->cred_handle, + &backend->cred->time_stamp); + } + else { + /* Pre-Windows 10 1809 or the user set a legacy algorithm list. Although MS + does not document it, currently Schannel will not negotiate TLS 1.3 when + SCHANNEL_CRED is used. */ + ALG_ID algIds[NUM_CIPHERS]; + char *ciphers = conn_config->cipher_list; + SCHANNEL_CRED schannel_cred = { 0 }; + schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; + schannel_cred.dwFlags = flags; + schannel_cred.grbitEnabledProtocols = enabled_protocols; + + if(ciphers) { + if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) { + infof(data, "schannel: WARNING: This version of Schannel may " + "negotiate a less-secure TLS version than TLS 1.3 because the " + "user set an algorithm cipher list."); + } + if(conn_config->cipher_list13) { + failf(data, "schannel: This version of Schannel does not support " + "setting an algorithm cipher list and TLS 1.3 cipher list at " + "the same time"); + return CURLE_SSL_CIPHER; + } + result = set_ssl_ciphers(&schannel_cred, ciphers, algIds); + if(CURLE_OK != result) { + failf(data, "schannel: Failed setting algorithm cipher list"); + return result; + } + } + else { + schannel_cred.dwFlags = flags | SCH_USE_STRONG_CRYPTO; + } + +#ifdef HAS_CLIENT_CERT_PATH + if(client_certs[0]) { + schannel_cred.cCreds = 1; + schannel_cred.paCred = client_certs; + } +#endif + + sspi_status = + s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME, + SECPKG_CRED_OUTBOUND, NULL, + &schannel_cred, NULL, NULL, + &backend->cred->cred_handle, + &backend->cred->time_stamp); + } + +#ifdef HAS_CLIENT_CERT_PATH + if(client_certs[0]) + CertFreeCertificateContext(client_certs[0]); +#endif + + if(sspi_status != SEC_E_OK) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: AcquireCredentialsHandle failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + Curl_safefree(backend->cred); + switch(sspi_status) { + case SEC_E_INSUFFICIENT_MEMORY: + return CURLE_OUT_OF_MEMORY; + case SEC_E_NO_CREDENTIALS: + case SEC_E_SECPKG_NOT_FOUND: + case SEC_E_NOT_OWNER: + case SEC_E_UNKNOWN_CREDENTIALS: + case SEC_E_INTERNAL_ERROR: + default: + return CURLE_SSL_CONNECT_ERROR; + } + } + + return CURLE_OK; +} + +static CURLcode +schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + ssize_t written = -1; + struct ssl_connect_data *connssl = cf->ctx; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + SecBuffer outbuf; + SecBufferDesc outbuf_desc; + SecBuffer inbuf; + SecBufferDesc inbuf_desc; +#ifdef HAS_ALPN + unsigned char alpn_buffer[128]; +#endif + SECURITY_STATUS sspi_status = SEC_E_OK; + struct Curl_schannel_cred *old_cred = NULL; + CURLcode result; + + DEBUGASSERT(backend); + DEBUGF(infof(data, + "schannel: SSL/TLS connection with %s port %d (step 1/3)", + connssl->peer.hostname, connssl->peer.port)); + + if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, + VERSION_LESS_THAN_EQUAL)) { + /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and + algorithms that may not be supported by all servers. */ + infof(data, "schannel: Windows version is old and may not be able to " + "connect to some servers due to lack of SNI, algorithms, etc."); + } + +#ifdef HAS_ALPN + /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above. + Also it does not seem to be supported for Wine, see curl bug #983. */ + backend->use_alpn = connssl->alpn && + !GetProcAddress(GetModuleHandle(TEXT("ntdll")), + "wine_get_version") && + curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL); +#else + backend->use_alpn = false; +#endif + +#ifdef _WIN32_WCE +#ifdef HAS_MANUAL_VERIFY_API + /* certificate validation on CE does not seem to work right; we will + * do it following a more manual process. */ + backend->use_manual_cred_validation = true; +#else +#error "compiler too old to support requisite manual cert verify for Win CE" +#endif +#else +#ifdef HAS_MANUAL_VERIFY_API + if(conn_config->CAfile || conn_config->ca_info_blob) { + if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + backend->use_manual_cred_validation = true; + } + else { + failf(data, "schannel: this version of Windows is too old to support " + "certificate verification via CA bundle file."); + return CURLE_SSL_CACERT_BADFILE; + } + } + else + backend->use_manual_cred_validation = false; +#else + if(conn_config->CAfile || conn_config->ca_info_blob) { + failf(data, "schannel: CA cert support not built in"); + return CURLE_NOT_BUILT_IN; + } +#endif +#endif + + backend->cred = NULL; + + /* check for an existing reusable credential handle */ + if(ssl_config->primary.cache_session) { + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + (void **)&old_cred, NULL)) { + backend->cred = old_cred; + DEBUGF(infof(data, "schannel: reusing existing credential handle")); + + /* increment the reference counter of the credential/session handle */ + backend->cred->refcount++; + DEBUGF(infof(data, + "schannel: incremented credential handle refcount = %d", + backend->cred->refcount)); + } + Curl_ssl_sessionid_unlock(data); + } + + if(!backend->cred) { + char *snihost; + result = schannel_acquire_credential_handle(cf, data); + if(result) + return result; + /* schannel_acquire_credential_handle() sets backend->cred accordingly or + it returns error otherwise. */ + + /* A hostname associated with the credential is needed by + InitializeSecurityContext for SNI and other reasons. */ + snihost = connssl->peer.sni? connssl->peer.sni : connssl->peer.hostname; + backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); + if(!backend->cred->sni_hostname) + return CURLE_OUT_OF_MEMORY; + } + + /* Warn if SNI is disabled due to use of an IP address */ + if(connssl->peer.type != CURL_SSL_PEER_DNS) { + infof(data, "schannel: using IP address, SNI is not supported by OS."); + } + +#ifdef HAS_ALPN + if(backend->use_alpn) { + int cur = 0; + int list_start_index = 0; + unsigned int *extension_len = NULL; + unsigned short* list_len = NULL; + struct alpn_proto_buf proto; + + /* The first four bytes will be an unsigned int indicating number + of bytes of data in the rest of the buffer. */ + extension_len = (unsigned int *)(void *)(&alpn_buffer[cur]); + cur += (int)sizeof(unsigned int); + + /* The next four bytes are an indicator that this buffer will contain + ALPN data, as opposed to NPN, for example. */ + *(unsigned int *)(void *)&alpn_buffer[cur] = + SecApplicationProtocolNegotiationExt_ALPN; + cur += (int)sizeof(unsigned int); + + /* The next two bytes will be an unsigned short indicating the number + of bytes used to list the preferred protocols. */ + list_len = (unsigned short*)(void *)(&alpn_buffer[cur]); + cur += (int)sizeof(unsigned short); + + list_start_index = cur; + + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result) { + failf(data, "Error setting ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + memcpy(&alpn_buffer[cur], proto.data, proto.len); + cur += proto.len; + + *list_len = curlx_uitous(cur - list_start_index); + *extension_len = (unsigned int)(*list_len + + sizeof(unsigned int) + sizeof(unsigned short)); + + InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur); + InitSecBufferDesc(&inbuf_desc, &inbuf, 1); + + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } + else { + InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); + InitSecBufferDesc(&inbuf_desc, &inbuf, 1); + } +#else /* HAS_ALPN */ + InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); + InitSecBufferDesc(&inbuf_desc, &inbuf, 1); +#endif + + /* setup output buffer */ + InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); + InitSecBufferDesc(&outbuf_desc, &outbuf, 1); + + /* security request flags */ + backend->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_STREAM; + + if(!ssl_config->auto_client_cert) { + backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; + } + + /* allocate memory for the security context handle */ + backend->ctxt = (struct Curl_schannel_ctxt *) + calloc(1, sizeof(struct Curl_schannel_ctxt)); + if(!backend->ctxt) { + failf(data, "schannel: unable to allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + + /* Schannel InitializeSecurityContext: + https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx + + At the moment we do not pass inbuf unless we are using ALPN since we only + use it for that, and Wine (for which we currently disable ALPN) is giving + us problems with inbuf regardless. https://github.com/curl/curl/issues/983 + */ + sspi_status = s_pSecFn->InitializeSecurityContext( + &backend->cred->cred_handle, NULL, backend->cred->sni_hostname, + backend->req_flags, 0, 0, + (backend->use_alpn ? &inbuf_desc : NULL), + 0, &backend->ctxt->ctxt_handle, + &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); + + if(sspi_status != SEC_I_CONTINUE_NEEDED) { + char buffer[STRERROR_LEN]; + Curl_safefree(backend->ctxt); + switch(sspi_status) { + case SEC_E_INSUFFICIENT_MEMORY: + failf(data, "schannel: initial InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_OUT_OF_MEMORY; + case SEC_E_WRONG_PRINCIPAL: + failf(data, "schannel: SNI or certificate check failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_PEER_FAILED_VERIFICATION; + /* + case SEC_E_INVALID_HANDLE: + case SEC_E_INVALID_TOKEN: + case SEC_E_LOGON_DENIED: + case SEC_E_TARGET_UNKNOWN: + case SEC_E_NO_AUTHENTICATING_AUTHORITY: + case SEC_E_INTERNAL_ERROR: + case SEC_E_NO_CREDENTIALS: + case SEC_E_UNSUPPORTED_FUNCTION: + case SEC_E_APPLICATION_PROTOCOL_MISMATCH: + */ + default: + failf(data, "schannel: initial InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_SSL_CONNECT_ERROR; + } + } + + DEBUGF(infof(data, "schannel: sending initial handshake data: " + "sending %lu bytes.", outbuf.cbBuffer)); + + /* send initial handshake data which is now stored in output buffer */ + written = Curl_conn_cf_send(cf->next, data, + outbuf.pvBuffer, outbuf.cbBuffer, + &result); + s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); + if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { + failf(data, "schannel: failed to send initial handshake data: " + "sent %zd of %lu bytes", written, outbuf.cbBuffer); + return CURLE_SSL_CONNECT_ERROR; + } + + DEBUGF(infof(data, "schannel: sent initial handshake data: " + "sent %zd bytes", written)); + + backend->recv_unrecoverable_err = CURLE_OK; + backend->recv_sspi_close_notify = false; + backend->recv_connection_closed = false; + backend->recv_renegotiating = false; + backend->encdata_is_incomplete = false; + + /* continue to second handshake step */ + connssl->connecting_state = ssl_connect_2; + + return CURLE_OK; +} + +static CURLcode +schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + int i; + ssize_t nread = -1, written = -1; + unsigned char *reallocated_buffer; + SecBuffer outbuf[3]; + SecBufferDesc outbuf_desc; + SecBuffer inbuf[2]; + SecBufferDesc inbuf_desc; + SECURITY_STATUS sspi_status = SEC_E_OK; + CURLcode result; + bool doread; + const char *pubkey_ptr; + + DEBUGASSERT(backend); + + doread = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? FALSE : TRUE; + connssl->io_need = CURL_SSL_IO_NEED_NONE; + + DEBUGF(infof(data, + "schannel: SSL/TLS connection with %s port %d (step 2/3)", + connssl->peer.hostname, connssl->peer.port)); + + if(!backend->cred || !backend->ctxt) + return CURLE_SSL_CONNECT_ERROR; + + /* buffer to store previously received and decrypted data */ + if(!backend->decdata_buffer) { + backend->decdata_offset = 0; + backend->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; + backend->decdata_buffer = malloc(backend->decdata_length); + if(!backend->decdata_buffer) { + failf(data, "schannel: unable to allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + } + + /* buffer to store previously received and encrypted data */ + if(!backend->encdata_buffer) { + backend->encdata_is_incomplete = false; + backend->encdata_offset = 0; + backend->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; + backend->encdata_buffer = malloc(backend->encdata_length); + if(!backend->encdata_buffer) { + failf(data, "schannel: unable to allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + } + + /* if we need a bigger buffer to read a full message, increase buffer now */ + if(backend->encdata_length - backend->encdata_offset < + CURL_SCHANNEL_BUFFER_FREE_SIZE) { + /* increase internal encrypted data buffer */ + size_t reallocated_length = backend->encdata_offset + + CURL_SCHANNEL_BUFFER_FREE_SIZE; + reallocated_buffer = realloc(backend->encdata_buffer, + reallocated_length); + + if(!reallocated_buffer) { + failf(data, "schannel: unable to re-allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + else { + backend->encdata_buffer = reallocated_buffer; + backend->encdata_length = reallocated_length; + } + } + + for(;;) { + if(doread) { + /* read encrypted handshake data from socket */ + nread = Curl_conn_cf_recv(cf->next, data, + (char *) (backend->encdata_buffer + + backend->encdata_offset), + backend->encdata_length - + backend->encdata_offset, + &result); + if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + DEBUGF(infof(data, "schannel: failed to receive handshake, " + "need more data")); + return CURLE_OK; + } + else if((result != CURLE_OK) || (nread == 0)) { + failf(data, "schannel: failed to receive handshake, " + "SSL/TLS connection failed"); + return CURLE_SSL_CONNECT_ERROR; + } + + /* increase encrypted data buffer offset */ + backend->encdata_offset += nread; + backend->encdata_is_incomplete = false; + DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); + } + + DEBUGF(infof(data, + "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); + + /* setup input buffers */ + InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(backend->encdata_offset), + curlx_uztoul(backend->encdata_offset)); + InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); + InitSecBufferDesc(&inbuf_desc, inbuf, 2); + + /* setup output buffers */ + InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0); + InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0); + InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0); + InitSecBufferDesc(&outbuf_desc, outbuf, 3); + + if(!inbuf[0].pvBuffer) { + failf(data, "schannel: unable to allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + + /* copy received handshake data into input buffer */ + memcpy(inbuf[0].pvBuffer, backend->encdata_buffer, + backend->encdata_offset); + + sspi_status = s_pSecFn->InitializeSecurityContext( + &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, + backend->cred->sni_hostname, backend->req_flags, + 0, 0, &inbuf_desc, 0, NULL, + &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); + + /* free buffer for received handshake data */ + Curl_safefree(inbuf[0].pvBuffer); + + /* check if the handshake was incomplete */ + if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { + backend->encdata_is_incomplete = true; + connssl->io_need = CURL_SSL_IO_NEED_RECV; + DEBUGF(infof(data, + "schannel: received incomplete message, need more data")); + return CURLE_OK; + } + + /* If the server has requested a client certificate, attempt to continue + the handshake without one. This will allow connections to servers which + request a client certificate but do not require it. */ + if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS && + !(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { + backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; + connssl->io_need = CURL_SSL_IO_NEED_SEND; + DEBUGF(infof(data, + "schannel: a client certificate has been requested")); + return CURLE_OK; + } + + /* check if the handshake needs to be continued */ + if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) { + for(i = 0; i < 3; i++) { + /* search for handshake tokens that need to be send */ + if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) { + DEBUGF(infof(data, "schannel: sending next handshake data: " + "sending %lu bytes.", outbuf[i].cbBuffer)); + + /* send handshake token to server */ + written = Curl_conn_cf_send(cf->next, data, + outbuf[i].pvBuffer, outbuf[i].cbBuffer, + &result); + if((result != CURLE_OK) || + (outbuf[i].cbBuffer != (size_t) written)) { + failf(data, "schannel: failed to send next handshake data: " + "sent %zd of %lu bytes", written, outbuf[i].cbBuffer); + return CURLE_SSL_CONNECT_ERROR; + } + } + + /* free obsolete buffer */ + if(outbuf[i].pvBuffer) { + s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer); + } + } + } + else { + char buffer[STRERROR_LEN]; + switch(sspi_status) { + case SEC_E_INSUFFICIENT_MEMORY: + failf(data, "schannel: next InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_OUT_OF_MEMORY; + case SEC_E_WRONG_PRINCIPAL: + failf(data, "schannel: SNI or certificate check failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_PEER_FAILED_VERIFICATION; + case SEC_E_UNTRUSTED_ROOT: + failf(data, "schannel: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_PEER_FAILED_VERIFICATION; + /* + case SEC_E_INVALID_HANDLE: + case SEC_E_INVALID_TOKEN: + case SEC_E_LOGON_DENIED: + case SEC_E_TARGET_UNKNOWN: + case SEC_E_NO_AUTHENTICATING_AUTHORITY: + case SEC_E_INTERNAL_ERROR: + case SEC_E_NO_CREDENTIALS: + case SEC_E_UNSUPPORTED_FUNCTION: + case SEC_E_APPLICATION_PROTOCOL_MISMATCH: + */ + default: + failf(data, "schannel: next InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_SSL_CONNECT_ERROR; + } + } + + /* check if there was additional remaining encrypted data */ + if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) { + DEBUGF(infof(data, "schannel: encrypted data length: %lu", + inbuf[1].cbBuffer)); + /* + There are two cases where we could be getting extra data here: + 1) If we are renegotiating a connection and the handshake is already + complete (from the server perspective), it can encrypted app data + (not handshake data) in an extra buffer at this point. + 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a + connection and this extra data is part of the handshake. + We should process the data immediately; waiting for the socket to + be ready may fail since the server is done sending handshake data. + */ + /* check if the remaining data is less than the total amount + and therefore begins after the already processed data */ + if(backend->encdata_offset > inbuf[1].cbBuffer) { + memmove(backend->encdata_buffer, + (backend->encdata_buffer + backend->encdata_offset) - + inbuf[1].cbBuffer, inbuf[1].cbBuffer); + backend->encdata_offset = inbuf[1].cbBuffer; + if(sspi_status == SEC_I_CONTINUE_NEEDED) { + doread = FALSE; + continue; + } + } + } + else { + backend->encdata_offset = 0; + } + break; + } + + /* check if the handshake needs to be continued */ + if(sspi_status == SEC_I_CONTINUE_NEEDED) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + return CURLE_OK; + } + + /* check if the handshake is complete */ + if(sspi_status == SEC_E_OK) { + connssl->connecting_state = ssl_connect_3; + DEBUGF(infof(data, "schannel: SSL/TLS handshake complete")); + } + +#ifndef CURL_DISABLE_PROXY + pubkey_ptr = Curl_ssl_cf_is_proxy(cf)? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: + data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + pubkey_ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif + if(pubkey_ptr) { + result = schannel_pkp_pin_peer_pubkey(cf, data, pubkey_ptr); + if(result) { + failf(data, "SSL: public key does not match pinned public key"); + return result; + } + } + +#ifdef HAS_MANUAL_VERIFY_API + if(conn_config->verifypeer && backend->use_manual_cred_validation) { + /* Certificate verification also verifies the hostname if verifyhost */ + return Curl_verify_certificate(cf, data); + } +#endif + + /* Verify the hostname manually when certificate verification is disabled, + because in that case Schannel will not verify it. */ + if(!conn_config->verifypeer && conn_config->verifyhost) + return Curl_verify_host(cf, data); + + return CURLE_OK; +} + +static bool +valid_cert_encoding(const CERT_CONTEXT *cert_context) +{ + return (cert_context != NULL) && + ((cert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) && + (cert_context->pbCertEncoded != NULL) && + (cert_context->cbCertEncoded > 0); +} + +typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, + bool reverse_order, void *arg); + +static void +traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, + void *arg) +{ + const CERT_CONTEXT *current_context = NULL; + bool should_continue = true; + bool first = true; + bool reverse_order = false; + while(should_continue && + (current_context = CertEnumCertificatesInStore( + context->hCertStore, + current_context)) != NULL) { + /* Windows 11 22H2 OS Build 22621.674 or higher enumerates certificates in + leaf-to-root order while all previous versions of Windows enumerate + certificates in root-to-leaf order. Determine the order of enumeration + by comparing SECPKG_ATTR_REMOTE_CERT_CONTEXT's pbCertContext with the + first certificate's pbCertContext. */ + if(first && context->pbCertEncoded != current_context->pbCertEncoded) + reverse_order = true; + should_continue = func(current_context, reverse_order, arg); + first = false; + } + + if(current_context) + CertFreeCertificateContext(current_context); +} + +static bool +cert_counter_callback(const CERT_CONTEXT *ccert_context, bool reverse_order, + void *certs_count) +{ + (void)reverse_order; /* unused */ + if(valid_cert_encoding(ccert_context)) + (*(int *)certs_count)++; + return true; +} + +struct Adder_args +{ + struct Curl_easy *data; + CURLcode result; + int idx; + int certs_count; +}; + +static bool +add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order, + void *raw_arg) +{ + struct Adder_args *args = (struct Adder_args*)raw_arg; + args->result = CURLE_OK; + if(valid_cert_encoding(ccert_context)) { + const char *beg = (const char *) ccert_context->pbCertEncoded; + const char *end = beg + ccert_context->cbCertEncoded; + int insert_index = reverse_order ? (args->certs_count - 1) - args->idx : + args->idx; + args->result = Curl_extract_certinfo(args->data, insert_index, + beg, end); + args->idx++; + } + return args->result == CURLE_OK; +} + +static void schannel_session_free(void *sessionid, size_t idsize) +{ + /* this is expected to be called under sessionid lock */ + struct Curl_schannel_cred *cred = sessionid; + + (void)idsize; + if(cred) { + cred->refcount--; + if(cred->refcount == 0) { + s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); + curlx_unicodefree(cred->sni_hostname); +#ifdef HAS_CLIENT_CERT_PATH + if(cred->client_cert_store) { + CertCloseStore(cred->client_cert_store, 0); + cred->client_cert_store = NULL; + } +#endif + Curl_safefree(cred); + } + } +} + +static CURLcode +schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_OK; + SECURITY_STATUS sspi_status = SEC_E_OK; + CERT_CONTEXT *ccert_context = NULL; +#ifdef HAS_ALPN + SecPkgContext_ApplicationProtocol alpn_result; +#endif + + DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); + + DEBUGF(infof(data, + "schannel: SSL/TLS connection with %s port %d (step 3/3)", + connssl->peer.hostname, connssl->peer.port)); + + if(!backend->cred) + return CURLE_SSL_CONNECT_ERROR; + + /* check if the required context attributes are met */ + if(backend->ret_flags != backend->req_flags) { + if(!(backend->ret_flags & ISC_RET_SEQUENCE_DETECT)) + failf(data, "schannel: failed to setup sequence detection"); + if(!(backend->ret_flags & ISC_RET_REPLAY_DETECT)) + failf(data, "schannel: failed to setup replay detection"); + if(!(backend->ret_flags & ISC_RET_CONFIDENTIALITY)) + failf(data, "schannel: failed to setup confidentiality"); + if(!(backend->ret_flags & ISC_RET_ALLOCATED_MEMORY)) + failf(data, "schannel: failed to setup memory allocation"); + if(!(backend->ret_flags & ISC_RET_STREAM)) + failf(data, "schannel: failed to setup stream orientation"); + return CURLE_SSL_CONNECT_ERROR; + } + +#ifdef HAS_ALPN + if(backend->use_alpn) { + sspi_status = + s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, + SECPKG_ATTR_APPLICATION_PROTOCOL, + &alpn_result); + + if(sspi_status != SEC_E_OK) { + failf(data, "schannel: failed to retrieve ALPN result"); + return CURLE_SSL_CONNECT_ERROR; + } + + if(alpn_result.ProtoNegoStatus == + SecApplicationProtocolNegotiationStatus_Success) { + unsigned char prev_alpn = cf->conn->alpn; + + Curl_alpn_set_negotiated(cf, data, alpn_result.ProtocolId, + alpn_result.ProtocolIdSize); + if(backend->recv_renegotiating) { + if(prev_alpn != cf->conn->alpn && + prev_alpn != CURL_HTTP_VERSION_NONE) { + /* Renegotiation selected a different protocol now, we cannot + * deal with this */ + failf(data, "schannel: server selected an ALPN protocol too late"); + return CURLE_SSL_CONNECT_ERROR; + } + } + } + else { + if(!backend->recv_renegotiating) + Curl_alpn_set_negotiated(cf, data, NULL, 0); + } + } +#endif + + /* save the current session data for possible reuse */ + if(ssl_config->primary.cache_session) { + Curl_ssl_sessionid_lock(data); + /* Up ref count since call takes ownership */ + backend->cred->refcount++; + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, backend->cred, + sizeof(struct Curl_schannel_cred), + schannel_session_free); + Curl_ssl_sessionid_unlock(data); + if(result) + return result; + } + + if(data->set.ssl.certinfo) { + int certs_count = 0; + sspi_status = + s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &ccert_context); + + if((sspi_status != SEC_E_OK) || !ccert_context) { + failf(data, "schannel: failed to retrieve remote cert context"); + return CURLE_PEER_FAILED_VERIFICATION; + } + + traverse_cert_store(ccert_context, cert_counter_callback, &certs_count); + + result = Curl_ssl_init_certinfo(data, certs_count); + if(!result) { + struct Adder_args args; + args.data = data; + args.idx = 0; + args.certs_count = certs_count; + traverse_cert_store(ccert_context, add_cert_to_certinfo, &args); + result = args.result; + } + CertFreeCertificateContext(ccert_context); + if(result) + return result; + } + + connssl->connecting_state = ssl_connect_done; + + return CURLE_OK; +} + +static CURLcode +schannel_connect_common(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool nonblocking, bool *done) +{ + CURLcode result; + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + timediff_t timeout_ms; + int what; + + /* check if the connection has already been established */ + if(ssl_connection_complete == connssl->state) { + *done = TRUE; + return CURLE_OK; + } + + if(ssl_connect_1 == connssl->connecting_state) { + /* check out how much more time we are allowed */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL/TLS connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + result = schannel_connect_step1(cf, data); + if(result) + return result; + } + + while(ssl_connect_2 == connssl->connecting_state) { + + /* check out how much more time we are allowed */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL/TLS connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { + + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd : CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd : CURL_SOCKET_BAD; + + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + nonblocking ? 0 : timeout_ms); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO); + return CURLE_SSL_CONNECT_ERROR; + } + else if(0 == what) { + if(nonblocking) { + *done = FALSE; + return CURLE_OK; + } + else { + /* timeout */ + failf(data, "SSL/TLS connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + } + /* socket is readable or writable */ + } + + /* Run transaction, and return to the caller if it failed or if + * this connection is part of a multi handle and this loop would + * execute again. This permits the owner of a multi handle to + * abort a connection attempt before step2 has completed while + * ensuring that a client using select() or epoll() will always + * have a valid fdset to wait on. + */ + result = schannel_connect_step2(cf, data); + if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) + return result; + + } /* repeat step2 until all transactions are done. */ + + if(ssl_connect_3 == connssl->connecting_state) { + result = schannel_connect_step3(cf, data); + if(result) + return result; + } + + if(ssl_connect_done == connssl->connecting_state) { + connssl->state = ssl_connection_complete; + +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + /* When SSPI is used in combination with Schannel + * we need the Schannel context to create the Schannel + * binding to pass the IIS extended protection checks. + * Available on Windows 7 or later. + */ + { + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + DEBUGASSERT(backend); + cf->conn->sslContext = &backend->ctxt->ctxt_handle; + } +#endif + + *done = TRUE; + } + else + *done = FALSE; + + /* reset our connection state machine */ + connssl->connecting_state = ssl_connect_1; + + return CURLE_OK; +} + +static ssize_t +schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + ssize_t written = -1; + size_t data_len = 0; + unsigned char *ptr = NULL; + struct ssl_connect_data *connssl = cf->ctx; + SecBuffer outbuf[4]; + SecBufferDesc outbuf_desc; + SECURITY_STATUS sspi_status = SEC_E_OK; + CURLcode result; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + + DEBUGASSERT(backend); + + /* check if the maximum stream sizes were queried */ + if(backend->stream_sizes.cbMaximumMessage == 0) { + sspi_status = s_pSecFn->QueryContextAttributes( + &backend->ctxt->ctxt_handle, + SECPKG_ATTR_STREAM_SIZES, + &backend->stream_sizes); + if(sspi_status != SEC_E_OK) { + *err = CURLE_SEND_ERROR; + return -1; + } + } + + /* check if the buffer is longer than the maximum message length */ + if(len > backend->stream_sizes.cbMaximumMessage) { + len = backend->stream_sizes.cbMaximumMessage; + } + + /* calculate the complete message length and allocate a buffer for it */ + data_len = backend->stream_sizes.cbHeader + len + + backend->stream_sizes.cbTrailer; + ptr = (unsigned char *) malloc(data_len); + if(!ptr) { + *err = CURLE_OUT_OF_MEMORY; + return -1; + } + + /* setup output buffers (header, data, trailer, empty) */ + InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER, + ptr, backend->stream_sizes.cbHeader); + InitSecBuffer(&outbuf[1], SECBUFFER_DATA, + ptr + backend->stream_sizes.cbHeader, curlx_uztoul(len)); + InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER, + ptr + backend->stream_sizes.cbHeader + len, + backend->stream_sizes.cbTrailer); + InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0); + InitSecBufferDesc(&outbuf_desc, outbuf, 4); + + /* copy data into output buffer */ + memcpy(outbuf[1].pvBuffer, buf, len); + + /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */ + sspi_status = s_pSecFn->EncryptMessage(&backend->ctxt->ctxt_handle, 0, + &outbuf_desc, 0); + + /* check if the message was encrypted */ + if(sspi_status == SEC_E_OK) { + written = 0; + + /* send the encrypted message including header, data and trailer */ + len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer; + + /* + it is important to send the full message which includes the header, + encrypted payload, and trailer. Until the client receives all the + data a coherent message has not been delivered and the client + cannot read any of it. + + If we wanted to buffer the unwritten encrypted bytes, we would + tell the client that all data it has requested to be sent has been + sent. The unwritten encrypted bytes would be the first bytes to + send on the next invocation. + Here's the catch with this - if we tell the client that all the + bytes have been sent, will the client call this method again to + send the buffered data? Looking at who calls this function, it + seems the answer is NO. + */ + + /* send entire message or fail */ + while(len > (size_t)written) { + ssize_t this_write = 0; + int what; + timediff_t timeout_ms = Curl_timeleft(data, NULL, FALSE); + if(timeout_ms < 0) { + /* we already got the timeout */ + failf(data, "schannel: timed out sending data " + "(bytes sent: %zd)", written); + *err = CURLE_OPERATION_TIMEDOUT; + written = -1; + break; + } + else if(!timeout_ms) + timeout_ms = TIMEDIFF_T_MAX; + what = SOCKET_WRITABLE(Curl_conn_cf_get_socket(cf, data), timeout_ms); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + *err = CURLE_SEND_ERROR; + written = -1; + break; + } + else if(0 == what) { + failf(data, "schannel: timed out sending data " + "(bytes sent: %zd)", written); + *err = CURLE_OPERATION_TIMEDOUT; + written = -1; + break; + } + /* socket is writable */ + + this_write = Curl_conn_cf_send(cf->next, data, + ptr + written, len - written, + &result); + if(result == CURLE_AGAIN) + continue; + else if(result != CURLE_OK) { + *err = result; + written = -1; + break; + } + + written += this_write; + } + } + else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) { + *err = CURLE_OUT_OF_MEMORY; + } + else{ + *err = CURLE_SEND_ERROR; + } + + Curl_safefree(ptr); + + if(len == (size_t)written) + /* Encrypted message including header, data and trailer entirely sent. + The return value is the number of unencrypted bytes that were sent. */ + written = outbuf[1].cbBuffer; + + return written; +} + +static ssize_t +schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + size_t size = 0; + ssize_t nread = -1; + struct ssl_connect_data *connssl = cf->ctx; + unsigned char *reallocated_buffer; + size_t reallocated_length; + bool done = FALSE; + SecBuffer inbuf[4]; + SecBufferDesc inbuf_desc; + SECURITY_STATUS sspi_status = SEC_E_OK; + /* we want the length of the encrypted buffer to be at least large enough + that it can hold all the bytes requested and some TLS record overhead. */ + size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + + DEBUGASSERT(backend); + + /**************************************************************************** + * Do not return or set backend->recv_unrecoverable_err unless in the + * cleanup. The pattern for return error is set *err, optional infof, goto + * cleanup. + * + * Our priority is to always return as much decrypted data to the caller as + * possible, even if an error occurs. The state of the decrypted buffer must + * always be valid. Transfer of decrypted data to the caller's buffer is + * handled in the cleanup. + */ + + DEBUGF(infof(data, "schannel: client wants to read %zu bytes", len)); + *err = CURLE_OK; + + if(len && len <= backend->decdata_offset) { + infof(data, "schannel: enough decrypted data is already available"); + goto cleanup; + } + else if(backend->recv_unrecoverable_err) { + *err = backend->recv_unrecoverable_err; + infof(data, "schannel: an unrecoverable error occurred in a prior call"); + goto cleanup; + } + else if(backend->recv_sspi_close_notify) { + /* once a server has indicated shutdown there is no more encrypted data */ + infof(data, "schannel: server indicated shutdown in a prior call"); + goto cleanup; + } + /* it is debatable what to return when !len. Regardless we cannot return + immediately because there may be data to decrypt (in the case we want to + decrypt all encrypted cached data) so handle !len later in cleanup. + */ + else if(len && !backend->recv_connection_closed) { + /* increase enc buffer in order to fit the requested amount of data */ + size = backend->encdata_length - backend->encdata_offset; + if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || + backend->encdata_length < min_encdata_length) { + reallocated_length = backend->encdata_offset + + CURL_SCHANNEL_BUFFER_FREE_SIZE; + if(reallocated_length < min_encdata_length) { + reallocated_length = min_encdata_length; + } + reallocated_buffer = realloc(backend->encdata_buffer, + reallocated_length); + if(!reallocated_buffer) { + *err = CURLE_OUT_OF_MEMORY; + failf(data, "schannel: unable to re-allocate memory"); + goto cleanup; + } + + backend->encdata_buffer = reallocated_buffer; + backend->encdata_length = reallocated_length; + size = backend->encdata_length - backend->encdata_offset; + DEBUGF(infof(data, "schannel: encdata_buffer resized %zu", + backend->encdata_length)); + } + + DEBUGF(infof(data, + "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); + + /* read encrypted data from socket */ + nread = Curl_conn_cf_recv(cf->next, data, + (char *)(backend->encdata_buffer + + backend->encdata_offset), + size, err); + if(*err) { + nread = -1; + if(*err == CURLE_AGAIN) + DEBUGF(infof(data, + "schannel: recv returned CURLE_AGAIN")); + else if(*err == CURLE_RECV_ERROR) + infof(data, "schannel: recv returned CURLE_RECV_ERROR"); + else + infof(data, "schannel: recv returned error %d", *err); + } + else if(nread == 0) { + backend->recv_connection_closed = true; + DEBUGF(infof(data, "schannel: server closed the connection")); + } + else if(nread > 0) { + backend->encdata_offset += (size_t)nread; + backend->encdata_is_incomplete = false; + DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); + } + } + + DEBUGF(infof(data, + "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); + + /* decrypt loop */ + while(backend->encdata_offset > 0 && sspi_status == SEC_E_OK && + (!len || backend->decdata_offset < len || + backend->recv_connection_closed)) { + /* prepare data buffer for DecryptMessage call */ + InitSecBuffer(&inbuf[0], SECBUFFER_DATA, backend->encdata_buffer, + curlx_uztoul(backend->encdata_offset)); + + /* we need 3 more empty input buffers for possible output */ + InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); + InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0); + InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0); + InitSecBufferDesc(&inbuf_desc, inbuf, 4); + + /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx + */ + sspi_status = s_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle, + &inbuf_desc, 0, NULL); + + /* check if everything went fine (server may want to renegotiate + or shutdown the connection context) */ + if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE || + sspi_status == SEC_I_CONTEXT_EXPIRED) { + /* check for successfully decrypted data, even before actual + renegotiation or shutdown of the connection context */ + if(inbuf[1].BufferType == SECBUFFER_DATA) { + DEBUGF(infof(data, "schannel: decrypted data length: %lu", + inbuf[1].cbBuffer)); + + /* increase buffer in order to fit the received amount of data */ + size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? + inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; + if(backend->decdata_length - backend->decdata_offset < size || + backend->decdata_length < len) { + /* increase internal decrypted data buffer */ + reallocated_length = backend->decdata_offset + size; + /* make sure that the requested amount of data fits */ + if(reallocated_length < len) { + reallocated_length = len; + } + reallocated_buffer = realloc(backend->decdata_buffer, + reallocated_length); + if(!reallocated_buffer) { + *err = CURLE_OUT_OF_MEMORY; + failf(data, "schannel: unable to re-allocate memory"); + goto cleanup; + } + backend->decdata_buffer = reallocated_buffer; + backend->decdata_length = reallocated_length; + } + + /* copy decrypted data to internal buffer */ + size = inbuf[1].cbBuffer; + if(size) { + memcpy(backend->decdata_buffer + backend->decdata_offset, + inbuf[1].pvBuffer, size); + backend->decdata_offset += size; + } + + DEBUGF(infof(data, "schannel: decrypted data added: %zu", size)); + DEBUGF(infof(data, + "schannel: decrypted cached: offset %zu length %zu", + backend->decdata_offset, backend->decdata_length)); + } + + /* check for remaining encrypted data */ + if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) { + DEBUGF(infof(data, "schannel: encrypted data length: %lu", + inbuf[3].cbBuffer)); + + /* check if the remaining data is less than the total amount + * and therefore begins after the already processed data + */ + if(backend->encdata_offset > inbuf[3].cbBuffer) { + /* move remaining encrypted data forward to the beginning of + buffer */ + memmove(backend->encdata_buffer, + (backend->encdata_buffer + backend->encdata_offset) - + inbuf[3].cbBuffer, inbuf[3].cbBuffer); + backend->encdata_offset = inbuf[3].cbBuffer; + } + + DEBUGF(infof(data, + "schannel: encrypted cached: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); + } + else { + /* reset encrypted buffer offset, because there is no data remaining */ + backend->encdata_offset = 0; + } + + /* check if server wants to renegotiate the connection context */ + if(sspi_status == SEC_I_RENEGOTIATE) { + infof(data, "schannel: remote party requests renegotiation"); + if(*err && *err != CURLE_AGAIN) { + infof(data, "schannel: cannot renegotiate, an error is pending"); + goto cleanup; + } + + /* begin renegotiation */ + infof(data, "schannel: renegotiating SSL/TLS connection"); + connssl->state = ssl_connection_negotiating; + connssl->connecting_state = ssl_connect_2; + connssl->io_need = CURL_SSL_IO_NEED_SEND; + backend->recv_renegotiating = true; + *err = schannel_connect_common(cf, data, FALSE, &done); + backend->recv_renegotiating = false; + if(*err) { + infof(data, "schannel: renegotiation failed"); + goto cleanup; + } + /* now retry receiving data */ + sspi_status = SEC_E_OK; + infof(data, "schannel: SSL/TLS connection renegotiated"); + continue; + } + /* check if the server closed the connection */ + else if(sspi_status == SEC_I_CONTEXT_EXPIRED) { + /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not + returned so we have to work around that in cleanup. */ + backend->recv_sspi_close_notify = true; + if(!backend->recv_connection_closed) + backend->recv_connection_closed = true; + infof(data, + "schannel: server close notification received (close_notify)"); + goto cleanup; + } + } + else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { + backend->encdata_is_incomplete = true; + if(!*err) + *err = CURLE_AGAIN; + infof(data, "schannel: failed to decrypt data, need more data"); + goto cleanup; + } + else { +#ifndef CURL_DISABLE_VERBOSE_STRINGS + char buffer[STRERROR_LEN]; + infof(data, "schannel: failed to read data from server: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); +#endif + *err = CURLE_RECV_ERROR; + goto cleanup; + } + } + + DEBUGF(infof(data, + "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); + + DEBUGF(infof(data, + "schannel: decrypted data buffer: offset %zu length %zu", + backend->decdata_offset, backend->decdata_length)); + +cleanup: + /* Warning- there is no guarantee the encdata state is valid at this point */ + DEBUGF(infof(data, "schannel: schannel_recv cleanup")); + + /* Error if the connection has closed without a close_notify. + + The behavior here is a matter of debate. We do not want to be vulnerable + to a truncation attack however there is some browser precedent for + ignoring the close_notify for compatibility reasons. + + Additionally, Windows 2000 (v5.0) is a special case since it seems it + does not return close_notify. In that case if the connection was closed we + assume it was graceful (close_notify) since there does not seem to be a + way to tell. + */ + if(len && !backend->decdata_offset && backend->recv_connection_closed && + !backend->recv_sspi_close_notify) { + bool isWin2k = curlx_verify_windows_version(5, 0, 0, PLATFORM_WINNT, + VERSION_EQUAL); + + if(isWin2k && sspi_status == SEC_E_OK) + backend->recv_sspi_close_notify = true; + else { + *err = CURLE_RECV_ERROR; + infof(data, "schannel: server closed abruptly (missing close_notify)"); + } + } + + /* Any error other than CURLE_AGAIN is an unrecoverable error. */ + if(*err && *err != CURLE_AGAIN) + backend->recv_unrecoverable_err = *err; + + size = len < backend->decdata_offset ? len : backend->decdata_offset; + if(size) { + memcpy(buf, backend->decdata_buffer, size); + memmove(backend->decdata_buffer, backend->decdata_buffer + size, + backend->decdata_offset - size); + backend->decdata_offset -= size; + DEBUGF(infof(data, "schannel: decrypted data returned %zu", size)); + DEBUGF(infof(data, + "schannel: decrypted data buffer: offset %zu length %zu", + backend->decdata_offset, backend->decdata_length)); + *err = CURLE_OK; + return (ssize_t)size; + } + + if(!*err && !backend->recv_connection_closed) + *err = CURLE_AGAIN; + + /* it is debatable what to return when !len. We could return whatever error + we got from decryption but instead we override here so the return is + consistent. + */ + if(!len) + *err = CURLE_OK; + + return *err ? -1 : 0; +} + +static CURLcode schannel_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + return schannel_connect_common(cf, data, TRUE, done); +} + +static CURLcode schannel_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode result; + bool done = FALSE; + + result = schannel_connect_common(cf, data, FALSE, &done); + if(result) + return result; + + DEBUGASSERT(done); + + return CURLE_OK; +} + +static bool schannel_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + const struct ssl_connect_data *connssl = cf->ctx; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + + (void)data; + DEBUGASSERT(backend); + + if(backend->ctxt) /* SSL/TLS is in use */ + return (backend->decdata_offset > 0 || + (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) || + backend->recv_connection_closed || + backend->recv_sspi_close_notify || + backend->recv_unrecoverable_err); + else + return FALSE; +} + +/* shut down the SSL connection and clean up related memory. + this function can be called multiple times on the same connection including + if the SSL connection failed (eg connection made but failed handshake). */ +static CURLcode schannel_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx + * Shutting Down an Schannel Connection + */ + struct ssl_connect_data *connssl = cf->ctx; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + CURLcode result = CURLE_OK; + + if(cf->shutdown) { + *done = TRUE; + return CURLE_OK; + } + + DEBUGASSERT(data); + DEBUGASSERT(backend); + + /* Not supported in schannel */ + (void)send_shutdown; + + *done = FALSE; + if(backend->ctxt) { + infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", + connssl->peer.hostname, connssl->peer.port); + } + + if(!backend->ctxt || cf->shutdown) { + *done = TRUE; + goto out; + } + + if(backend->cred && backend->ctxt && !backend->sent_shutdown) { + SecBufferDesc BuffDesc; + SecBuffer Buffer; + SECURITY_STATUS sspi_status; + SecBuffer outbuf; + SecBufferDesc outbuf_desc; + DWORD dwshut = SCHANNEL_SHUTDOWN; + + InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut)); + InitSecBufferDesc(&BuffDesc, &Buffer, 1); + + sspi_status = s_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle, + &BuffDesc); + + if(sspi_status != SEC_E_OK) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: ApplyControlToken failure: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + result = CURLE_SEND_ERROR; + goto out; + } + + /* setup output buffer */ + InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); + InitSecBufferDesc(&outbuf_desc, &outbuf, 1); + + sspi_status = s_pSecFn->InitializeSecurityContext( + &backend->cred->cred_handle, + &backend->ctxt->ctxt_handle, + backend->cred->sni_hostname, + backend->req_flags, + 0, + 0, + NULL, + 0, + &backend->ctxt->ctxt_handle, + &outbuf_desc, + &backend->ret_flags, + &backend->ctxt->time_stamp); + + if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { + /* send close message which is in output buffer */ + ssize_t written = Curl_conn_cf_send(cf->next, data, + outbuf.pvBuffer, outbuf.cbBuffer, + &result); + s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); + if(!result) { + if(written < (ssize_t)outbuf.cbBuffer) { + /* TODO: handle partial sends */ + infof(data, "schannel: failed to send close msg: %s" + " (bytes written: %zd)", curl_easy_strerror(result), written); + result = CURLE_SEND_ERROR; + goto out; + } + backend->sent_shutdown = TRUE; + *done = TRUE; + } + else if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_SEND; + result = CURLE_OK; + goto out; + } + else { + if(!backend->recv_connection_closed) { + infof(data, "schannel: error sending close msg: %d", result); + result = CURLE_SEND_ERROR; + goto out; + } + /* Looks like server already closed the connection. + * An error to send our close notify is not a failure. */ + *done = TRUE; + result = CURLE_OK; + } + } + } + + /* If the connection seems open and we have not seen the close notify + * from the server yet, try to receive it. */ + if(backend->cred && backend->ctxt && + !backend->recv_sspi_close_notify && !backend->recv_connection_closed) { + char buffer[1024]; + ssize_t nread; + + nread = schannel_recv(cf, data, buffer, sizeof(buffer), &result); + if(nread > 0) { + /* still data coming in? */ + } + else if(nread == 0) { + /* We got the close notify alert and are done. */ + backend->recv_connection_closed = TRUE; + *done = TRUE; + } + else if(nread < 0 && result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + } + else { + CURL_TRC_CF(data, cf, "SSL shutdown, error %d", result); + result = CURLE_RECV_ERROR; + } + } + +out: + cf->shutdown = (result || *done); + return result; +} + +static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + + DEBUGASSERT(data); + DEBUGASSERT(backend); + + /* free SSPI Schannel API security context handle */ + if(backend->ctxt) { + DEBUGF(infof(data, "schannel: clear security context handle")); + s_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle); + Curl_safefree(backend->ctxt); + } + + /* free SSPI Schannel API credential handle */ + if(backend->cred) { + Curl_ssl_sessionid_lock(data); + schannel_session_free(backend->cred, 0); + Curl_ssl_sessionid_unlock(data); + backend->cred = NULL; + } + + /* free internal buffer for received encrypted data */ + if(backend->encdata_buffer) { + Curl_safefree(backend->encdata_buffer); + backend->encdata_length = 0; + backend->encdata_offset = 0; + backend->encdata_is_incomplete = false; + } + + /* free internal buffer for received decrypted data */ + if(backend->decdata_buffer) { + Curl_safefree(backend->decdata_buffer); + backend->decdata_length = 0; + backend->decdata_offset = 0; + } +} + +static int schannel_init(void) +{ + return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0); +} + +static void schannel_cleanup(void) +{ + Curl_sspi_global_cleanup(); +} + +static size_t schannel_version(char *buffer, size_t size) +{ + return msnprintf(buffer, size, "Schannel"); +} + +static CURLcode schannel_random(struct Curl_easy *data UNUSED_PARAM, + unsigned char *entropy, size_t length) +{ + (void)data; + + return Curl_win32_random(entropy, length); +} + +static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *pinnedpubkey) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + CERT_CONTEXT *pCertContextServer = NULL; + + /* Result is returned to caller */ + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + + DEBUGASSERT(backend); + + /* if a path was not specified, do not pin */ + if(!pinnedpubkey) + return CURLE_OK; + + do { + SECURITY_STATUS sspi_status; + const char *x509_der; + DWORD x509_der_len; + struct Curl_X509certificate x509_parsed; + struct Curl_asn1Element *pubkey; + + sspi_status = + s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &pCertContextServer); + + if((sspi_status != SEC_E_OK) || !pCertContextServer) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: Failed to read remote certificate context: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + break; /* failed */ + } + + + if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && + (pCertContextServer->cbCertEncoded > 0))) + break; + + x509_der = (const char *)pCertContextServer->pbCertEncoded; + x509_der_len = pCertContextServer->cbCertEncoded; + memset(&x509_parsed, 0, sizeof(x509_parsed)); + if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) + break; + + pubkey = &x509_parsed.subjectPublicKeyInfo; + if(!pubkey->header || pubkey->end <= pubkey->header) { + failf(data, "SSL: failed retrieving public key from server certificate"); + break; + } + + result = Curl_pin_peer_pubkey(data, + pinnedpubkey, + (const unsigned char *)pubkey->header, + (size_t)(pubkey->end - pubkey->header)); + if(result) { + failf(data, "SSL: public key does not match pinned public key"); + } + } while(0); + + if(pCertContextServer) + CertFreeCertificateContext(pCertContextServer); + + return result; +} + +static void schannel_checksum(const unsigned char *input, + size_t inputlen, + unsigned char *checksum, + size_t checksumlen, + DWORD provType, + const unsigned int algId) +{ +#ifdef CURL_WINDOWS_APP + (void)input; + (void)inputlen; + (void)provType; + (void)algId; + memset(checksum, 0, checksumlen); +#else + HCRYPTPROV hProv = 0; + HCRYPTHASH hHash = 0; + DWORD cbHashSize = 0; + DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); + DWORD dwChecksumLen = (DWORD)checksumlen; + + /* since this can fail in multiple ways, zero memory first so we never + * return old data + */ + memset(checksum, 0, checksumlen); + + if(!CryptAcquireContext(&hProv, NULL, NULL, provType, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return; /* failed */ + + do { + if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) + break; /* failed */ + + if(!CryptHashData(hHash, input, (DWORD)inputlen, 0)) + break; /* failed */ + + /* get hash size */ + if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, + &dwHashSizeLen, 0)) + break; /* failed */ + + /* check hash size */ + if(checksumlen < cbHashSize) + break; /* failed */ + + if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0)) + break; /* failed */ + } while(0); + + if(hHash) + CryptDestroyHash(hHash); + + if(hProv) + CryptReleaseContext(hProv, 0); +#endif +} + +static CURLcode schannel_sha256sum(const unsigned char *input, + size_t inputlen, + unsigned char *sha256sum, + size_t sha256len) +{ + schannel_checksum(input, inputlen, sha256sum, sha256len, + PROV_RSA_AES, CALG_SHA_256); + return CURLE_OK; +} + +static void *schannel_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) +{ + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + (void)info; + DEBUGASSERT(backend); + return &backend->ctxt->ctxt_handle; +} + +HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + struct schannel_cert_share *share; + const struct ssl_general_config *cfg = &data->set.general_ssl; + timediff_t timeout_ms; + timediff_t elapsed_ms; + struct curltime now; + unsigned char info_blob_digest[CURL_SHA256_DIGEST_LENGTH]; + + DEBUGASSERT(multi); + + if(!multi) { + return NULL; + } + + share = Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY, + sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1); + if(!share || !share->cert_store) { + return NULL; + } + + /* zero ca_cache_timeout completely disables caching */ + if(!cfg->ca_cache_timeout) { + return NULL; + } + + /* check for cache timeout by using the cached_x509_store_expired timediff + calculation pattern from openssl.c. + negative timeout means retain forever. */ + timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + if(timeout_ms >= 0) { + now = Curl_now(); + elapsed_ms = Curl_timediff(now, share->time); + if(elapsed_ms >= timeout_ms) { + return NULL; + } + } + + if(ca_info_blob) { + if(!share->CAinfo_blob_digest) { + return NULL; + } + if(share->CAinfo_blob_size != ca_info_blob->len) { + return NULL; + } + schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH); + if(memcmp(share->CAinfo_blob_digest, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH)) { + return NULL; + } + } + else { + if(!conn_config->CAfile || !share->CAfile || + strcmp(share->CAfile, conn_config->CAfile)) { + return NULL; + } + } + + return share->cert_store; +} + +static void schannel_cert_share_free(void *key, size_t key_len, void *p) +{ + struct schannel_cert_share *share = p; + DEBUGASSERT(key_len == (sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1)); + DEBUGASSERT(!memcmp(MPROTO_SCHANNEL_CERT_SHARE_KEY, key, key_len)); + (void)key; + (void)key_len; + if(share->cert_store) { + CertCloseStore(share->cert_store, 0); + } + free(share->CAinfo_blob_digest); + free(share->CAfile); + free(share); +} + +bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + HCERTSTORE cert_store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + struct schannel_cert_share *share; + unsigned char *CAinfo_blob_digest = NULL; + size_t CAinfo_blob_size = 0; + char *CAfile = NULL; + + DEBUGASSERT(multi); + + if(!multi) { + return false; + } + + share = Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY, + sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1); + if(!share) { + share = calloc(1, sizeof(*share)); + if(!share) { + return false; + } + if(!Curl_hash_add2(&multi->proto_hash, + (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY, + sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1, + share, schannel_cert_share_free)) { + free(share); + return false; + } + } + + if(ca_info_blob) { + CAinfo_blob_digest = malloc(CURL_SHA256_DIGEST_LENGTH); + if(!CAinfo_blob_digest) { + return false; + } + schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + CAinfo_blob_digest, + CURL_SHA256_DIGEST_LENGTH); + CAinfo_blob_size = ca_info_blob->len; + } + else { + if(conn_config->CAfile) { + CAfile = strdup(conn_config->CAfile); + if(!CAfile) { + return false; + } + } + } + + /* free old cache data */ + if(share->cert_store) { + CertCloseStore(share->cert_store, 0); + } + free(share->CAinfo_blob_digest); + free(share->CAfile); + + share->time = Curl_now(); + share->cert_store = cert_store; + share->CAinfo_blob_digest = CAinfo_blob_digest; + share->CAinfo_blob_size = CAinfo_blob_size; + share->CAfile = CAfile; + return true; +} + +const struct Curl_ssl Curl_ssl_schannel = { + { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */ + + SSLSUPP_CERTINFO | +#ifdef HAS_MANUAL_VERIFY_API + SSLSUPP_CAINFO_BLOB | +#endif +#ifndef CURL_WINDOWS_APP + SSLSUPP_PINNEDPUBKEY | +#endif + SSLSUPP_TLS13_CIPHERSUITES | + SSLSUPP_CA_CACHE | + SSLSUPP_HTTPS_PROXY, + + sizeof(struct schannel_ssl_backend_data), + + schannel_init, /* init */ + schannel_cleanup, /* cleanup */ + schannel_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + schannel_shutdown, /* shutdown */ + schannel_data_pending, /* data_pending */ + schannel_random, /* random */ + Curl_none_cert_status_request, /* cert_status_request */ + schannel_connect, /* connect */ + schannel_connect_nonblocking, /* connect_nonblocking */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ + schannel_get_internals, /* get_internals */ + schannel_close, /* close_one */ + Curl_none_close_all, /* close_all */ + Curl_none_set_engine, /* set_engine */ + Curl_none_set_engine_default, /* set_engine_default */ + Curl_none_engines_list, /* engines_list */ + Curl_none_false_start, /* false_start */ + schannel_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ + NULL, /* disassociate_connection */ + schannel_recv, /* recv decrypted data */ + schannel_send, /* send data to encrypt */ +}; + +#endif /* USE_SCHANNEL */ diff --git a/third_party/curl/lib/vtls/schannel.h b/third_party/curl/lib/vtls/schannel.h new file mode 100644 index 0000000000..b26334bcf8 --- /dev/null +++ b/third_party/curl/lib/vtls/schannel.h @@ -0,0 +1,86 @@ +#ifndef HEADER_CURL_SCHANNEL_H +#define HEADER_CURL_SCHANNEL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Marc Hoersken, , et al. + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_SCHANNEL + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4201) +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif +/* Wincrypt must be included before anything that could include OpenSSL. */ +#if defined(USE_WIN32_CRYPTO) +#include +/* Undefine wincrypt conflicting symbols for BoringSSL. */ +#undef X509_NAME +#undef X509_EXTENSIONS +#undef PKCS7_ISSUER_AND_SERIAL +#undef PKCS7_SIGNER_INFO +#undef OCSP_REQUEST +#undef OCSP_RESPONSE +#endif + +#include +#include +#include "curl_sspi.h" + +#include "cfilters.h" +#include "urldata.h" + +/* has been included via the above . + * Or in case of ldap.c, it was included via . + * And since has this: + * #define X509_NAME ((LPCSTR) 7) + * + * And in BoringSSL's there is: + * typedef struct X509_name_st X509_NAME; + * etc. + * + * this will cause all kinds of C-preprocessing paste errors in + * BoringSSL's : So just undefine those defines here + * (and only here). + */ +#if defined(OPENSSL_IS_BORINGSSL) +# undef X509_NAME +# undef X509_CERT_PAIR +# undef X509_EXTENSIONS +#endif + +extern const struct Curl_ssl Curl_ssl_schannel; + +CURLcode Curl_verify_host(struct Curl_cfilter *cf, + struct Curl_easy *data); + +CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, + struct Curl_easy *data); + +#endif /* USE_SCHANNEL */ +#endif /* HEADER_CURL_SCHANNEL_H */ diff --git a/third_party/curl/lib/vtls/schannel_int.h b/third_party/curl/lib/vtls/schannel_int.h new file mode 100644 index 0000000000..b04f1804b5 --- /dev/null +++ b/third_party/curl/lib/vtls/schannel_int.h @@ -0,0 +1,185 @@ +#ifndef HEADER_CURL_SCHANNEL_INT_H +#define HEADER_CURL_SCHANNEL_INT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Marc Hoersken, , et al. + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_SCHANNEL + +#if (defined(__MINGW32__) || defined(CERT_CHAIN_REVOCATION_CHECK_CHAIN)) \ + && !defined(CURL_WINDOWS_APP) +#define HAS_MANUAL_VERIFY_API +#endif + +#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) \ + && !defined(DISABLE_SCHANNEL_CLIENT_CERT) +#define HAS_CLIENT_CERT_PATH +#endif + +#ifndef CRYPT_DECODE_NOCOPY_FLAG +#define CRYPT_DECODE_NOCOPY_FLAG 0x1 +#endif + +#ifndef CRYPT_DECODE_ALLOC_FLAG +#define CRYPT_DECODE_ALLOC_FLAG 0x8000 +#endif + +#ifndef CERT_ALT_NAME_DNS_NAME +#define CERT_ALT_NAME_DNS_NAME 3 +#endif + +#ifndef CERT_ALT_NAME_IP_ADDRESS +#define CERT_ALT_NAME_IP_ADDRESS 8 +#endif + +#if defined(_MSC_VER) && (_MSC_VER <= 1600) +/* Workaround for warning: + 'type cast' : conversion from 'int' to 'LPCSTR' of greater size */ +#undef CERT_STORE_PROV_MEMORY +#undef CERT_STORE_PROV_SYSTEM_A +#undef CERT_STORE_PROV_SYSTEM_W +#define CERT_STORE_PROV_MEMORY ((LPCSTR)(size_t)2) +#define CERT_STORE_PROV_SYSTEM_A ((LPCSTR)(size_t)9) +#define CERT_STORE_PROV_SYSTEM_W ((LPCSTR)(size_t)10) +#endif + +#ifndef SCH_CREDENTIALS_VERSION + +#define SCH_CREDENTIALS_VERSION 0x00000005 + +typedef enum _eTlsAlgorithmUsage +{ + TlsParametersCngAlgUsageKeyExchange, + TlsParametersCngAlgUsageSignature, + TlsParametersCngAlgUsageCipher, + TlsParametersCngAlgUsageDigest, + TlsParametersCngAlgUsageCertSig +} eTlsAlgorithmUsage; + +typedef struct _CRYPTO_SETTINGS +{ + eTlsAlgorithmUsage eAlgorithmUsage; + UNICODE_STRING strCngAlgId; + DWORD cChainingModes; + PUNICODE_STRING rgstrChainingModes; + DWORD dwMinBitLength; + DWORD dwMaxBitLength; +} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS; + +typedef struct _TLS_PARAMETERS +{ + DWORD cAlpnIds; + PUNICODE_STRING rgstrAlpnIds; + DWORD grbitDisabledProtocols; + DWORD cDisabledCrypto; + PCRYPTO_SETTINGS pDisabledCrypto; + DWORD dwFlags; +} TLS_PARAMETERS, * PTLS_PARAMETERS; + +typedef struct _SCH_CREDENTIALS +{ + DWORD dwVersion; + DWORD dwCredFormat; + DWORD cCreds; + PCCERT_CONTEXT* paCred; + HCERTSTORE hRootStore; + + DWORD cMappers; + struct _HMAPPER **aphMappers; + + DWORD dwSessionLifespan; + DWORD dwFlags; + DWORD cTlsParameters; + PTLS_PARAMETERS pTlsParameters; +} SCH_CREDENTIALS, * PSCH_CREDENTIALS; + +#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16 +#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16 +#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16 +#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16 + +#endif /* SCH_CREDENTIALS_VERSION */ + +struct Curl_schannel_cred { + CredHandle cred_handle; + TimeStamp time_stamp; + TCHAR *sni_hostname; +#ifdef HAS_CLIENT_CERT_PATH + HCERTSTORE client_cert_store; +#endif + int refcount; +}; + +struct Curl_schannel_ctxt { + CtxtHandle ctxt_handle; + TimeStamp time_stamp; +}; + +struct schannel_ssl_backend_data { + struct Curl_schannel_cred *cred; + struct Curl_schannel_ctxt *ctxt; + SecPkgContext_StreamSizes stream_sizes; + size_t encdata_length, decdata_length; + size_t encdata_offset, decdata_offset; + unsigned char *encdata_buffer, *decdata_buffer; + /* encdata_is_incomplete: if encdata contains only a partial record that + cannot be decrypted without another recv() (that is, status is + SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds + more bytes into encdata then set this back to false. */ + bool encdata_is_incomplete; + unsigned long req_flags, ret_flags; + CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ + bool recv_sspi_close_notify; /* true if connection closed by close_notify */ + bool recv_connection_closed; /* true if connection closed, regardless how */ + bool recv_renegotiating; /* true if recv is doing renegotiation */ + bool use_alpn; /* true if ALPN is used for this connection */ +#ifdef HAS_MANUAL_VERIFY_API + bool use_manual_cred_validation; /* true if manual cred validation is used */ +#endif + BIT(sent_shutdown); +}; + +/* key to use at `multi->proto_hash` */ +#define MPROTO_SCHANNEL_CERT_SHARE_KEY "tls:schannel:cert:share" + +struct schannel_cert_share { + unsigned char *CAinfo_blob_digest; /* CA info blob digest */ + size_t CAinfo_blob_size; /* CA info blob size */ + char *CAfile; /* CAfile path used to generate + certificate store */ + HCERTSTORE cert_store; /* cached certificate store or + NULL if none */ + struct curltime time; /* when the cached store was created */ +}; + +HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data); + +bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + HCERTSTORE cert_store); + +#endif /* USE_SCHANNEL */ +#endif /* HEADER_CURL_SCHANNEL_INT_H */ diff --git a/third_party/curl/lib/vtls/schannel_verify.c b/third_party/curl/lib/vtls/schannel_verify.c new file mode 100644 index 0000000000..96ad0e517a --- /dev/null +++ b/third_party/curl/lib/vtls/schannel_verify.c @@ -0,0 +1,794 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Marc Hoersken, + * Copyright (C) Mark Salisbury, + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Source file for Schannel-specific certificate verification. This code should + * only be invoked by code in schannel.c. + */ + +#include "curl_setup.h" + +#ifdef USE_SCHANNEL +#ifndef USE_WINDOWS_SSPI +# error "cannot compile SCHANNEL support without SSPI." +#endif + +#include "schannel.h" +#include "schannel_int.h" + +#include "vtls.h" +#include "vtls_int.h" +#include "sendf.h" +#include "strerror.h" +#include "curl_multibyte.h" +#include "curl_printf.h" +#include "hostcheck.h" +#include "version_win32.h" + +/* The last #include file should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +#define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend) + + +#ifdef HAS_MANUAL_VERIFY_API + +#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ +#define BEGIN_CERT "-----BEGIN CERTIFICATE-----" +#define END_CERT "\n-----END CERTIFICATE-----" + +struct cert_chain_engine_config_win7 { + DWORD cbSize; + HCERTSTORE hRestrictedRoot; + HCERTSTORE hRestrictedTrust; + HCERTSTORE hRestrictedOther; + DWORD cAdditionalStore; + HCERTSTORE *rghAdditionalStore; + DWORD dwFlags; + DWORD dwUrlRetrievalTimeout; + DWORD MaximumCachedCertificates; + DWORD CycleDetectionModulus; + HCERTSTORE hExclusiveRoot; + HCERTSTORE hExclusiveTrustedPeople; +}; + +static int is_cr_or_lf(char c) +{ + return c == '\r' || c == '\n'; +} + +/* Search the substring needle,needlelen into string haystack,haystacklen + * Strings do not need to be terminated by a '\0'. + * Similar of OSX/Linux memmem (not available on Visual Studio). + * Return position of beginning of first occurrence or NULL if not found + */ +static const char *c_memmem(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen) +{ + const char *p; + char first; + const char *str_limit = (const char *)haystack + haystacklen; + if(!needlelen || needlelen > haystacklen) + return NULL; + first = *(const char *)needle; + for(p = (const char *)haystack; p <= (str_limit - needlelen); p++) + if(((*p) == first) && (memcmp(p, needle, needlelen) == 0)) + return p; + + return NULL; +} + +static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, + const char *ca_buffer, + size_t ca_buffer_size, + const char *ca_file_text, + struct Curl_easy *data) +{ + const size_t begin_cert_len = strlen(BEGIN_CERT); + const size_t end_cert_len = strlen(END_CERT); + CURLcode result = CURLE_OK; + int num_certs = 0; + bool more_certs = 1; + const char *current_ca_file_ptr = ca_buffer; + const char *ca_buffer_limit = ca_buffer + ca_buffer_size; + + while(more_certs && (current_ca_file_ptr MAX_CAFILE_SIZE) { + failf(data, + "schannel: CA file exceeds max size of %u bytes", + MAX_CAFILE_SIZE); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + + ca_file_bufsize = (size_t)file_size.QuadPart; + ca_file_buffer = (char *)malloc(ca_file_bufsize + 1); + if(!ca_file_buffer) { + result = CURLE_OUT_OF_MEMORY; + goto cleanup; + } + + while(total_bytes_read < ca_file_bufsize) { + DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read); + DWORD bytes_read = 0; + + if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read, + bytes_to_read, &bytes_read, NULL)) { + char buffer[STRERROR_LEN]; + failf(data, + "schannel: failed to read from CA file '%s': %s", + ca_file, + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + if(bytes_read == 0) { + /* Premature EOF -- adjust the bufsize to the new value */ + ca_file_bufsize = total_bytes_read; + } + else { + total_bytes_read += bytes_read; + } + } + + /* Null terminate the buffer */ + ca_file_buffer[ca_file_bufsize] = '\0'; + + result = add_certs_data_to_store(trust_store, + ca_file_buffer, ca_file_bufsize, + ca_file, + data); + +cleanup: + if(ca_file_handle != INVALID_HANDLE_VALUE) { + CloseHandle(ca_file_handle); + } + Curl_safefree(ca_file_buffer); + curlx_unicodefree(ca_file_tstr); + + return result; +} + +#endif /* HAS_MANUAL_VERIFY_API */ + +/* + * Returns the number of characters necessary to populate all the host_names. + * If host_names is not NULL, populate it with all the hostnames. Each string + * in the host_names is null-terminated and the last string is double + * null-terminated. If no DNS names are found, a single null-terminated empty + * string is returned. + */ +static DWORD cert_get_name_string(struct Curl_easy *data, + CERT_CONTEXT *cert_context, + LPTSTR host_names, + DWORD length) +{ + DWORD actual_length = 0; +#if defined(CURL_WINDOWS_APP) + (void)data; + (void)cert_context; + (void)host_names; + (void)length; +#else + BOOL compute_content = FALSE; + CERT_INFO *cert_info = NULL; + CERT_EXTENSION *extension = NULL; + CRYPT_DECODE_PARA decode_para = {0, 0, 0}; + CERT_ALT_NAME_INFO *alt_name_info = NULL; + DWORD alt_name_info_size = 0; + BOOL ret_val = FALSE; + LPTSTR current_pos = NULL; + DWORD i; + +#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG + /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ + if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + /* CertGetNameString will provide the 8-bit character string without + * any decoding */ + DWORD name_flags = + CERT_NAME_DISABLE_IE4_UTF8_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG; + actual_length = CertGetNameString(cert_context, + CERT_NAME_DNS_TYPE, + name_flags, + NULL, + host_names, + length); + return actual_length; + } +#endif + + compute_content = host_names != NULL && length != 0; + + /* Initialize default return values. */ + actual_length = 1; + if(compute_content) { + *host_names = '\0'; + } + + if(!cert_context) { + failf(data, "schannel: Null certificate context."); + return actual_length; + } + + cert_info = cert_context->pCertInfo; + if(!cert_info) { + failf(data, "schannel: Null certificate info."); + return actual_length; + } + + extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, + cert_info->cExtension, + cert_info->rgExtension); + if(!extension) { + failf(data, "schannel: CertFindExtension() returned no extension."); + return actual_length; + } + + decode_para.cbSize = sizeof(CRYPT_DECODE_PARA); + + ret_val = + CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + szOID_SUBJECT_ALT_NAME2, + extension->Value.pbData, + extension->Value.cbData, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, + &decode_para, + &alt_name_info, + &alt_name_info_size); + if(!ret_val) { + failf(data, + "schannel: CryptDecodeObjectEx() returned no alternate name " + "information."); + return actual_length; + } + + current_pos = host_names; + + /* Iterate over the alternate names and populate host_names. */ + for(i = 0; i < alt_name_info->cAltEntry; i++) { + const CERT_ALT_NAME_ENTRY *entry = &alt_name_info->rgAltEntry[i]; + wchar_t *dns_w = NULL; + size_t current_length = 0; + + if(entry->dwAltNameChoice != CERT_ALT_NAME_DNS_NAME) { + continue; + } + if(!entry->pwszDNSName) { + infof(data, "schannel: Empty DNS name."); + continue; + } + current_length = wcslen(entry->pwszDNSName) + 1; + if(!compute_content) { + actual_length += (DWORD)current_length; + continue; + } + /* Sanity check to prevent buffer overrun. */ + if((actual_length + current_length) > length) { + failf(data, "schannel: Not enough memory to list all hostnames."); + break; + } + dns_w = entry->pwszDNSName; + /* pwszDNSName is in ia5 string format and hence does not contain any + * non-ascii characters. */ + while(*dns_w != '\0') { + *current_pos++ = (TCHAR)(*dns_w++); + } + *current_pos++ = '\0'; + actual_length += (DWORD)current_length; + } + if(compute_content) { + /* Last string has double null-terminator. */ + *current_pos = '\0'; + } +#endif + return actual_length; +} + +/* Verify the server's hostname */ +CURLcode Curl_verify_host(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + SECURITY_STATUS sspi_status; + CURLcode result = CURLE_PEER_FAILED_VERIFICATION; + CERT_CONTEXT *pCertContextServer = NULL; + TCHAR *cert_hostname_buff = NULL; + size_t cert_hostname_buff_index = 0; + const char *conn_hostname = connssl->peer.hostname; + size_t hostlen = strlen(conn_hostname); + DWORD len = 0; + DWORD actual_len = 0; + + sspi_status = + s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &pCertContextServer); + + if((sspi_status != SEC_E_OK) || !pCertContextServer) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: Failed to read remote certificate context: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + result = CURLE_PEER_FAILED_VERIFICATION; + goto cleanup; + } + + /* Determine the size of the string needed for the cert hostname */ + len = cert_get_name_string(data, pCertContextServer, NULL, 0); + if(len == 0) { + failf(data, + "schannel: CertGetNameString() returned no " + "certificate name information"); + result = CURLE_PEER_FAILED_VERIFICATION; + goto cleanup; + } + + /* CertGetNameString guarantees that the returned name will not contain + * embedded null bytes. This appears to be undocumented behavior. + */ + cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR)); + if(!cert_hostname_buff) { + result = CURLE_OUT_OF_MEMORY; + goto cleanup; + } + actual_len = cert_get_name_string( + data, pCertContextServer, (LPTSTR)cert_hostname_buff, len); + + /* Sanity check */ + if(actual_len != len) { + failf(data, + "schannel: CertGetNameString() returned certificate " + "name information of unexpected size"); + result = CURLE_PEER_FAILED_VERIFICATION; + goto cleanup; + } + + /* cert_hostname_buff contains all DNS names, where each name is + * null-terminated and the last DNS name is double null-terminated. Due to + * this encoding, use the length of the buffer to iterate over all names. + */ + result = CURLE_PEER_FAILED_VERIFICATION; + while(cert_hostname_buff_index < len && + cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') && + result == CURLE_PEER_FAILED_VERIFICATION) { + + char *cert_hostname; + + /* Comparing the cert name and the connection hostname encoded as UTF-8 + * is acceptable since both values are assumed to use ASCII + * (or some equivalent) encoding + */ + cert_hostname = curlx_convert_tchar_to_UTF8( + &cert_hostname_buff[cert_hostname_buff_index]); + if(!cert_hostname) { + result = CURLE_OUT_OF_MEMORY; + } + else { + if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), + conn_hostname, hostlen)) { + infof(data, + "schannel: connection hostname (%s) validated " + "against certificate name (%s)", + conn_hostname, cert_hostname); + result = CURLE_OK; + } + else { + size_t cert_hostname_len; + + infof(data, + "schannel: connection hostname (%s) did not match " + "against certificate name (%s)", + conn_hostname, cert_hostname); + + cert_hostname_len = + _tcslen(&cert_hostname_buff[cert_hostname_buff_index]); + + /* Move on to next cert name */ + cert_hostname_buff_index += cert_hostname_len + 1; + + result = CURLE_PEER_FAILED_VERIFICATION; + } + curlx_unicodefree(cert_hostname); + } + } + + if(result == CURLE_PEER_FAILED_VERIFICATION) { + failf(data, + "schannel: CertGetNameString() failed to match " + "connection hostname (%s) against server certificate names", + conn_hostname); + } + else if(result != CURLE_OK) + failf(data, "schannel: server certificate name verification failed"); + +cleanup: + Curl_safefree(cert_hostname_buff); + + if(pCertContextServer) + CertFreeCertificateContext(pCertContextServer); + + return result; +} + + +#ifdef HAS_MANUAL_VERIFY_API +/* Verify the server's certificate and hostname */ +CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + SECURITY_STATUS sspi_status; + CURLcode result = CURLE_OK; + CERT_CONTEXT *pCertContextServer = NULL; + const CERT_CHAIN_CONTEXT *pChainContext = NULL; + HCERTCHAINENGINE cert_chain_engine = NULL; + HCERTSTORE trust_store = NULL; + HCERTSTORE own_trust_store = NULL; + + DEBUGASSERT(BACKEND); + + sspi_status = + s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &pCertContextServer); + + if((sspi_status != SEC_E_OK) || !pCertContextServer) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: Failed to read remote certificate context: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + result = CURLE_PEER_FAILED_VERIFICATION; + } + + if(result == CURLE_OK && + (conn_config->CAfile || conn_config->ca_info_blob) && + BACKEND->use_manual_cred_validation) { + /* + * Create a chain engine that uses the certificates in the CA file as + * trusted certificates. This is only supported on Windows 7+. + */ + + if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, + VERSION_LESS_THAN)) { + failf(data, "schannel: this version of Windows is too old to support " + "certificate verification via CA bundle file."); + result = CURLE_SSL_CACERT_BADFILE; + } + else { + /* try cache */ + trust_store = Curl_schannel_get_cached_cert_store(cf, data); + + if(trust_store) { + infof(data, "schannel: reusing certificate store from cache"); + } + else { + /* Open the certificate store */ + trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY, + 0, + (HCRYPTPROV)NULL, + CERT_STORE_CREATE_NEW_FLAG, + NULL); + if(!trust_store) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: failed to create certificate store: %s", + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + result = CURLE_SSL_CACERT_BADFILE; + } + else { + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + own_trust_store = trust_store; + + if(ca_info_blob) { + result = add_certs_data_to_store(trust_store, + (const char *)ca_info_blob->data, + ca_info_blob->len, + "(memory blob)", + data); + } + else { + result = add_certs_file_to_store(trust_store, + conn_config->CAfile, + data); + } + if(result == CURLE_OK) { + if(Curl_schannel_set_cached_cert_store(cf, data, trust_store)) { + own_trust_store = NULL; + } + } + } + } + } + + if(result == CURLE_OK) { + struct cert_chain_engine_config_win7 engine_config; + BOOL create_engine_result; + + memset(&engine_config, 0, sizeof(engine_config)); + engine_config.cbSize = sizeof(engine_config); + engine_config.hExclusiveRoot = trust_store; + + /* CertCreateCertificateChainEngine will check the expected size of the + * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size + * does not match the expected size. When this occurs, it indicates that + * CAINFO is not supported on the version of Windows in use. + */ + create_engine_result = + CertCreateCertificateChainEngine( + (CERT_CHAIN_ENGINE_CONFIG *)&engine_config, &cert_chain_engine); + if(!create_engine_result) { + char buffer[STRERROR_LEN]; + failf(data, + "schannel: failed to create certificate chain engine: %s", + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + result = CURLE_SSL_CACERT_BADFILE; + } + } + } + + if(result == CURLE_OK) { + CERT_CHAIN_PARA ChainPara; + + memset(&ChainPara, 0, sizeof(ChainPara)); + ChainPara.cbSize = sizeof(ChainPara); + + if(!CertGetCertificateChain(cert_chain_engine, + pCertContextServer, + NULL, + pCertContextServer->hCertStore, + &ChainPara, + (ssl_config->no_revoke ? 0 : + CERT_CHAIN_REVOCATION_CHECK_CHAIN), + NULL, + &pChainContext)) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: CertGetCertificateChain failed: %s", + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + pChainContext = NULL; + result = CURLE_PEER_FAILED_VERIFICATION; + } + + if(result == CURLE_OK) { + CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0]; + DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED); + dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; + + if(data->set.ssl.revoke_best_effort) { + /* Ignore errors when root certificates are missing the revocation + * list URL, or when the list could not be downloaded because the + * server is currently unreachable. */ + dwTrustErrorMask &= ~(DWORD)(CERT_TRUST_REVOCATION_STATUS_UNKNOWN | + CERT_TRUST_IS_OFFLINE_REVOCATION); + } + + if(dwTrustErrorMask) { + if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_IS_REVOKED"); + else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_IS_PARTIAL_CHAIN"); + else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_IS_UNTRUSTED_ROOT"); + else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_IS_NOT_TIME_VALID"); + else if(dwTrustErrorMask & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_REVOCATION_STATUS_UNKNOWN"); + else + failf(data, "schannel: CertGetCertificateChain error mask: 0x%08lx", + dwTrustErrorMask); + result = CURLE_PEER_FAILED_VERIFICATION; + } + } + } + + if(result == CURLE_OK) { + if(conn_config->verifyhost) { + result = Curl_verify_host(cf, data); + } + } + + if(cert_chain_engine) { + CertFreeCertificateChainEngine(cert_chain_engine); + } + + if(own_trust_store) { + CertCloseStore(own_trust_store, 0); + } + + if(pChainContext) + CertFreeCertificateChain(pChainContext); + + if(pCertContextServer) + CertFreeCertificateContext(pCertContextServer); + + return result; +} + +#endif /* HAS_MANUAL_VERIFY_API */ +#endif /* USE_SCHANNEL */ diff --git a/third_party/curl/lib/vtls/sectransp.c b/third_party/curl/lib/vtls/sectransp.c new file mode 100644 index 0000000000..c2803e8bfc --- /dev/null +++ b/third_party/curl/lib/vtls/sectransp.c @@ -0,0 +1,2927 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * Copyright (C) Nick Zitzmann, . + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Source file for all iOS and macOS SecureTransport-specific code for the + * TLS/SSL layer. No code but vtls.c should ever call or use these functions. + */ + +#include "curl_setup.h" + +#ifdef USE_SECTRANSP + +#include "urldata.h" /* for the Curl_easy definition */ +#include "curl_base64.h" +#include "strtok.h" +#include "multiif.h" +#include "strcase.h" +#include "x509asn1.h" +#include "strerror.h" +#include "cipher_suite.h" + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif /* __clang__ */ + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress" +#endif + +#include + +#include +/* For some reason, when building for iOS, the omnibus header above does + * not include SecureTransport.h as of iOS SDK 5.1. */ +#include +#include +#include + +/* The Security framework has changed greatly between iOS and different macOS + versions, and we will try to support as many of them as we can (back to + Leopard and iOS 5) by using macros and weak-linking. + + In general, you want to build this using the most recent OS SDK, since some + features require curl to be built against the latest SDK. TLS 1.1 and 1.2 + support, for instance, require the macOS 10.8 SDK or later. TLS 1.3 + requires the macOS 10.13 or iOS 11 SDK or later. */ +#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 +#error "The Secure Transport backend requires Leopard or later." +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1050 */ + +#define CURL_BUILD_IOS 0 +#define CURL_BUILD_IOS_7 0 +#define CURL_BUILD_IOS_9 0 +#define CURL_BUILD_IOS_11 0 +#define CURL_BUILD_IOS_13 0 +#define CURL_BUILD_MAC 1 +/* This is the maximum API level we are allowed to use when building: */ +#define CURL_BUILD_MAC_10_5 MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 +#define CURL_BUILD_MAC_10_6 MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 +#define CURL_BUILD_MAC_10_7 MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 +#define CURL_BUILD_MAC_10_8 MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 +#define CURL_BUILD_MAC_10_9 MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 +#define CURL_BUILD_MAC_10_11 MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 +#define CURL_BUILD_MAC_10_13 MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 +#define CURL_BUILD_MAC_10_15 MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 +/* These macros mean "the following code is present to allow runtime backward + compatibility with at least this cat or earlier": + (You set this at build-time using the compiler command line option + "-mmacosx-version-min.") */ +#define CURL_SUPPORT_MAC_10_5 MAC_OS_X_VERSION_MIN_REQUIRED <= 1050 +#define CURL_SUPPORT_MAC_10_6 MAC_OS_X_VERSION_MIN_REQUIRED <= 1060 +#define CURL_SUPPORT_MAC_10_7 MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 +#define CURL_SUPPORT_MAC_10_8 MAC_OS_X_VERSION_MIN_REQUIRED <= 1080 +#define CURL_SUPPORT_MAC_10_9 MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 + +#elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE +#define CURL_BUILD_IOS 1 +#define CURL_BUILD_IOS_7 __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 +#define CURL_BUILD_IOS_9 __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 +#define CURL_BUILD_IOS_11 __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 +#define CURL_BUILD_IOS_13 __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 +#define CURL_BUILD_MAC 0 +#define CURL_BUILD_MAC_10_5 0 +#define CURL_BUILD_MAC_10_6 0 +#define CURL_BUILD_MAC_10_7 0 +#define CURL_BUILD_MAC_10_8 0 +#define CURL_BUILD_MAC_10_9 0 +#define CURL_BUILD_MAC_10_11 0 +#define CURL_BUILD_MAC_10_13 0 +#define CURL_BUILD_MAC_10_15 0 +#define CURL_SUPPORT_MAC_10_5 0 +#define CURL_SUPPORT_MAC_10_6 0 +#define CURL_SUPPORT_MAC_10_7 0 +#define CURL_SUPPORT_MAC_10_8 0 +#define CURL_SUPPORT_MAC_10_9 0 + +#else +#error "The Secure Transport backend requires iOS or macOS." +#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ + +#if CURL_BUILD_MAC +#include +#endif /* CURL_BUILD_MAC */ + +#include "sendf.h" +#include "inet_pton.h" +#include "connect.h" +#include "select.h" +#include "vtls.h" +#include "vtls_int.h" +#include "sectransp.h" +#include "curl_printf.h" +#include "strdup.h" + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + + +/* From MacTypes.h (which we cannot include because it is not present in + iOS: */ +#define ioErr -36 +#define paramErr -50 + +struct st_ssl_backend_data { + SSLContextRef ssl_ctx; + bool ssl_direction; /* true if writing, false if reading */ + size_t ssl_write_buffered_length; + BIT(sent_shutdown); +}; + +/* Create the list of default ciphers to use by making an intersection of the + * ciphers supported by Secure Transport and the list below, using the order + * of the former. + * This list is based on TLS recommendations by Mozilla, balancing between + * security and wide compatibility: "Most ciphers that are not clearly broken + * and dangerous to use are supported" + */ +static const uint16_t default_ciphers[] = { + TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ + TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ + TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ + +#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ +#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ + +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS + TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ + TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, /* 0x0067 */ + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, /* 0x006B */ + TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ + TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, /* 0x009E */ + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, /* 0x009F */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ + +#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ + + /* TLSv1.3 is not supported by sectransp, but there is also other + * code referencing TLSv1.3, like: kTLSProtocol13 ? */ + TLS_AES_128_GCM_SHA256, /* 0x1301 */ + TLS_AES_256_GCM_SHA384, /* 0x1302 */ + TLS_CHACHA20_POLY1305_SHA256, /* 0x1303 */ +#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ +}; + +#define DEFAULT_CIPHERS_LEN sizeof(default_ciphers)/sizeof(default_ciphers[0]) + + +/* pinned public key support tests */ + +/* version 1 supports macOS 10.12+ and iOS 10+ */ +#if ((TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || \ + (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)) +#define SECTRANSP_PINNEDPUBKEY_V1 1 +#endif + +/* version 2 supports MacOSX 10.7+ */ +#if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) +#define SECTRANSP_PINNEDPUBKEY_V2 1 +#endif + +#if defined(SECTRANSP_PINNEDPUBKEY_V1) || defined(SECTRANSP_PINNEDPUBKEY_V2) +/* this backend supports CURLOPT_PINNEDPUBLICKEY */ +#define SECTRANSP_PINNEDPUBKEY 1 +#endif /* SECTRANSP_PINNEDPUBKEY */ + +#ifdef SECTRANSP_PINNEDPUBKEY +/* both new and old APIs return rsa keys missing the spki header (not DER) */ +static const unsigned char rsa4096SpkiHeader[] = { + 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00}; + +static const unsigned char rsa2048SpkiHeader[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00}; +#ifdef SECTRANSP_PINNEDPUBKEY_V1 +/* the *new* version does not return DER encoded ecdsa certs like the old... */ +static const unsigned char ecDsaSecp256r1SpkiHeader[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, + 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00}; + +static const unsigned char ecDsaSecp384r1SpkiHeader[] = { + 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, + 0x00, 0x22, 0x03, 0x62, 0x00}; +#endif /* SECTRANSP_PINNEDPUBKEY_V1 */ +#endif /* SECTRANSP_PINNEDPUBKEY */ + +static OSStatus sectransp_bio_cf_in_read(SSLConnectionRef connection, + void *buf, + size_t *dataLength) /* IN/OUT */ +{ + struct Curl_cfilter *cf = (struct Curl_cfilter *)connection; + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nread; + CURLcode result; + OSStatus rtn = noErr; + + DEBUGASSERT(data); + nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result); + CURL_TRC_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d", + *dataLength, nread, result); + if(nread < 0) { + switch(result) { + case CURLE_OK: + case CURLE_AGAIN: + rtn = errSSLWouldBlock; + backend->ssl_direction = false; + break; + default: + rtn = ioErr; + break; + } + nread = 0; + } + else if(nread == 0) { + rtn = errSSLClosedGraceful; + } + else if((size_t)nread < *dataLength) { + rtn = errSSLWouldBlock; + } + *dataLength = nread; + return rtn; +} + +static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection, + const void *buf, + size_t *dataLength) /* IN/OUT */ +{ + struct Curl_cfilter *cf = (struct Curl_cfilter *)connection; + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nwritten; + CURLcode result; + OSStatus rtn = noErr; + + DEBUGASSERT(data); + nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result); + CURL_TRC_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d", + *dataLength, nwritten, result); + if(nwritten <= 0) { + if(result == CURLE_AGAIN) { + rtn = errSSLWouldBlock; + backend->ssl_direction = true; + } + else { + rtn = ioErr; + } + nwritten = 0; + } + else if((size_t)nwritten < *dataLength) { + rtn = errSSLWouldBlock; + } + *dataLength = nwritten; + return rtn; +} + +#if CURL_BUILD_MAC +CF_INLINE void GetDarwinVersionNumber(int *major, int *minor) +{ + int mib[2]; + char *os_version; + size_t os_version_len; + char *os_version_major, *os_version_minor; + char *tok_buf; + + /* Get the Darwin kernel version from the kernel using sysctl(): */ + mib[0] = CTL_KERN; + mib[1] = KERN_OSRELEASE; + if(sysctl(mib, 2, NULL, &os_version_len, NULL, 0) == -1) + return; + os_version = malloc(os_version_len*sizeof(char)); + if(!os_version) + return; + if(sysctl(mib, 2, os_version, &os_version_len, NULL, 0) == -1) { + free(os_version); + return; + } + + /* Parse the version: */ + os_version_major = strtok_r(os_version, ".", &tok_buf); + os_version_minor = strtok_r(NULL, ".", &tok_buf); + *major = atoi(os_version_major); + *minor = atoi(os_version_minor); + free(os_version); +} +#endif /* CURL_BUILD_MAC */ + +/* Apple provides a myriad of ways of getting information about a certificate + into a string. Some are not available under iOS or newer cats. Here's a + unified function for getting a string describing the certificate that ought + to work in all cats starting with Leopard. */ +CF_INLINE CFStringRef getsubject(SecCertificateRef cert) +{ + CFStringRef server_cert_summary = CFSTR("(null)"); + +#if CURL_BUILD_IOS + /* iOS: There is only one way to do this. */ + server_cert_summary = SecCertificateCopySubjectSummary(cert); +#else +#if CURL_BUILD_MAC_10_7 + /* Lion & later: Get the long description if we can. */ + if(&SecCertificateCopyLongDescription) + server_cert_summary = + SecCertificateCopyLongDescription(NULL, cert, NULL); + else +#endif /* CURL_BUILD_MAC_10_7 */ +#if CURL_BUILD_MAC_10_6 + /* Snow Leopard: Get the certificate summary. */ + if(&SecCertificateCopySubjectSummary) + server_cert_summary = SecCertificateCopySubjectSummary(cert); + else +#endif /* CURL_BUILD_MAC_10_6 */ + /* Leopard is as far back as we go... */ + (void)SecCertificateCopyCommonName(cert, &server_cert_summary); +#endif /* CURL_BUILD_IOS */ + return server_cert_summary; +} + +static CURLcode CopyCertSubject(struct Curl_easy *data, + SecCertificateRef cert, char **certp) +{ + CFStringRef c = getsubject(cert); + CURLcode result = CURLE_OK; + const char *direct; + char *cbuf = NULL; + *certp = NULL; + + if(!c) { + failf(data, "SSL: invalid CA certificate subject"); + return CURLE_PEER_FAILED_VERIFICATION; + } + + /* If the subject is already available as UTF-8 encoded (ie 'direct') then + use that, else convert it. */ + direct = CFStringGetCStringPtr(c, kCFStringEncodingUTF8); + if(direct) { + *certp = strdup(direct); + if(!*certp) { + failf(data, "SSL: out of memory"); + result = CURLE_OUT_OF_MEMORY; + } + } + else { + size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1; + cbuf = calloc(1, cbuf_size); + if(cbuf) { + if(!CFStringGetCString(c, cbuf, (CFIndex)cbuf_size, + kCFStringEncodingUTF8)) { + failf(data, "SSL: invalid CA certificate subject"); + result = CURLE_PEER_FAILED_VERIFICATION; + } + else + /* pass back the buffer */ + *certp = cbuf; + } + else { + failf(data, "SSL: could not allocate %zu bytes of memory", cbuf_size); + result = CURLE_OUT_OF_MEMORY; + } + } + if(result) + free(cbuf); + CFRelease(c); + return result; +} + +#if CURL_SUPPORT_MAC_10_6 +/* The SecKeychainSearch API was deprecated in Lion, and using it will raise + deprecation warnings, so let's not compile this unless it is necessary: */ +static OSStatus CopyIdentityWithLabelOldSchool(char *label, + SecIdentityRef *out_c_a_k) +{ + OSStatus status = errSecItemNotFound; + SecKeychainAttributeList attr_list; + SecKeychainAttribute attr; + SecKeychainSearchRef search = NULL; + SecCertificateRef cert = NULL; + + /* Set up the attribute list: */ + attr_list.count = 1L; + attr_list.attr = &attr; + + /* Set up our lone search criterion: */ + attr.tag = kSecLabelItemAttr; + attr.data = label; + attr.length = (UInt32)strlen(label); + + /* Start searching: */ + status = SecKeychainSearchCreateFromAttributes(NULL, + kSecCertificateItemClass, + &attr_list, + &search); + if(status == noErr) { + status = SecKeychainSearchCopyNext(search, + (SecKeychainItemRef *)&cert); + if(status == noErr && cert) { + /* If we found a certificate, does it have a private key? */ + status = SecIdentityCreateWithCertificate(NULL, cert, out_c_a_k); + CFRelease(cert); + } + } + + if(search) + CFRelease(search); + return status; +} +#endif /* CURL_SUPPORT_MAC_10_6 */ + +static OSStatus CopyIdentityWithLabel(char *label, + SecIdentityRef *out_cert_and_key) +{ + OSStatus status = errSecItemNotFound; + +#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS + CFArrayRef keys_list; + CFIndex keys_list_count; + CFIndex i; + + /* SecItemCopyMatching() was introduced in iOS and Snow Leopard. + kSecClassIdentity was introduced in Lion. If both exist, let's use them + to find the certificate. */ + if(&SecItemCopyMatching && kSecClassIdentity) { + CFTypeRef keys[5]; + CFTypeRef values[5]; + CFDictionaryRef query_dict; + CFStringRef label_cf = CFStringCreateWithCString(NULL, label, + kCFStringEncodingUTF8); + + /* Set up our search criteria and expected results: */ + values[0] = kSecClassIdentity; /* we want a certificate and a key */ + keys[0] = kSecClass; + values[1] = kCFBooleanTrue; /* we want a reference */ + keys[1] = kSecReturnRef; + values[2] = kSecMatchLimitAll; /* kSecMatchLimitOne would be better if the + * label matching below worked correctly */ + keys[2] = kSecMatchLimit; + /* identity searches need a SecPolicyRef in order to work */ + values[3] = SecPolicyCreateSSL(false, NULL); + keys[3] = kSecMatchPolicy; + /* match the name of the certificate (does not work in macOS 10.12.1) */ + values[4] = label_cf; + keys[4] = kSecAttrLabel; + query_dict = CFDictionaryCreate(NULL, (const void **)keys, + (const void **)values, 5L, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease(values[3]); + + /* Do we have a match? */ + status = SecItemCopyMatching(query_dict, (CFTypeRef *) &keys_list); + + /* Because kSecAttrLabel matching does not work with kSecClassIdentity, + * we need to find the correct identity ourselves */ + if(status == noErr) { + keys_list_count = CFArrayGetCount(keys_list); + *out_cert_and_key = NULL; + status = 1; + for(i = 0; idata, + (CFIndex)blob->len); + status = (pkcs_data != NULL) ? errSecSuccess : errSecAllocate; + resource_imported = (pkcs_data != NULL); + } + else { + pkcs_url = + CFURLCreateFromFileSystemRepresentation(NULL, + (const UInt8 *)cPath, + (CFIndex)strlen(cPath), false); + resource_imported = + CFURLCreateDataAndPropertiesFromResource(NULL, + pkcs_url, &pkcs_data, + NULL, NULL, &status); + } + + if(resource_imported) { + CFArrayRef items = NULL; + + /* On iOS SecPKCS12Import will never add the client certificate to the + * Keychain. + * + * It gives us back a SecIdentityRef that we can use directly. */ +#if CURL_BUILD_IOS + const void *cKeys[] = {kSecImportExportPassphrase}; + const void *cValues[] = {password}; + CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues, + password ? 1L : 0L, NULL, NULL); + + if(options) { + status = SecPKCS12Import(pkcs_data, options, &items); + CFRelease(options); + } + + + /* On macOS SecPKCS12Import will always add the client certificate to + * the Keychain. + * + * As this does not match iOS, and apps may not want to see their client + * certificate saved in the user's keychain, we use SecItemImport + * with a NULL keychain to avoid importing it. + * + * This returns a SecCertificateRef from which we can construct a + * SecIdentityRef. + */ +#elif CURL_BUILD_MAC_10_7 + SecItemImportExportKeyParameters keyParams; + SecExternalFormat inputFormat = kSecFormatPKCS12; + SecExternalItemType inputType = kSecItemTypeCertificate; + + memset(&keyParams, 0x00, sizeof(keyParams)); + keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; + keyParams.passphrase = password; + + status = SecItemImport(pkcs_data, NULL, &inputFormat, &inputType, + 0, &keyParams, NULL, &items); +#endif + + + /* Extract the SecIdentityRef */ + if(status == errSecSuccess && items && CFArrayGetCount(items)) { + CFIndex i, count; + count = CFArrayGetCount(items); + + for(i = 0; i < count; i++) { + CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i); + CFTypeID itemID = CFGetTypeID(item); + + if(itemID == CFDictionaryGetTypeID()) { + CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue( + (CFDictionaryRef) item, + kSecImportItemIdentity); + CFRetain(identity); + *out_cert_and_key = (SecIdentityRef) identity; + break; + } +#if CURL_BUILD_MAC_10_7 + else if(itemID == SecCertificateGetTypeID()) { + status = SecIdentityCreateWithCertificate(NULL, + (SecCertificateRef) item, + out_cert_and_key); + break; + } +#endif + } + } + + if(items) + CFRelease(items); + CFRelease(pkcs_data); + } +#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ + if(password) + CFRelease(password); + if(pkcs_url) + CFRelease(pkcs_url); + return status; +} + +/* This code was borrowed from nss.c, with some modifications: + * Determine whether the nickname passed in is a filename that needs to + * be loaded as a PEM or a nickname. + * + * returns 1 for a file + * returns 0 for not a file + */ +CF_INLINE bool is_file(const char *filename) +{ + struct_stat st; + + if(!filename) + return false; + + if(stat(filename, &st) == 0) + return S_ISREG(st.st_mode); + return false; +} + +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS +static CURLcode sectransp_version_from_curl(SSLProtocol *darwinver, + long ssl_version) +{ + switch(ssl_version) { + case CURL_SSLVERSION_TLSv1_0: + *darwinver = kTLSProtocol1; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_1: + *darwinver = kTLSProtocol11; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_2: + *darwinver = kTLSProtocol12; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_3: + /* TLS 1.3 support first appeared in iOS 11 and macOS 10.13 */ +#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \ + defined(HAVE_BUILTIN_AVAILABLE) + if(__builtin_available(macOS 10.13, iOS 11.0, *)) { + *darwinver = kTLSProtocol13; + return CURLE_OK; + } +#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && + defined(HAVE_BUILTIN_AVAILABLE) */ + break; + } + return CURLE_SSL_CONNECT_ERROR; +} +#endif + +static CURLcode set_ssl_version_min_max(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + long ssl_version = conn_config->version; + long ssl_version_max = conn_config->version_max; + long max_supported_version_by_os; + + DEBUGASSERT(backend); + + /* macOS 10.5-10.7 supported TLS 1.0 only. + macOS 10.8 and later, and iOS 5 and later, added TLS 1.1 and 1.2. + macOS 10.13 and later, and iOS 11 and later, added TLS 1.3. */ +#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \ + defined(HAVE_BUILTIN_AVAILABLE) + if(__builtin_available(macOS 10.13, iOS 11.0, *)) { + max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_3; + } + else { + max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2; + } +#else + max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2; +#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && + defined(HAVE_BUILTIN_AVAILABLE) */ + + switch(ssl_version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + ssl_version = CURL_SSLVERSION_TLSv1_0; + break; + } + + switch(ssl_version_max) { + case CURL_SSLVERSION_MAX_NONE: + case CURL_SSLVERSION_MAX_DEFAULT: + ssl_version_max = max_supported_version_by_os; + break; + } + +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS + if(&SSLSetProtocolVersionMax) { + SSLProtocol darwin_ver_min = kTLSProtocol1; + SSLProtocol darwin_ver_max = kTLSProtocol1; + CURLcode result = sectransp_version_from_curl(&darwin_ver_min, + ssl_version); + if(result) { + failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); + return result; + } + result = sectransp_version_from_curl(&darwin_ver_max, + ssl_version_max >> 16); + if(result) { + failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); + return result; + } + + (void)SSLSetProtocolVersionMin(backend->ssl_ctx, darwin_ver_min); + (void)SSLSetProtocolVersionMax(backend->ssl_ctx, darwin_ver_max); + return result; + } + else { +#if CURL_SUPPORT_MAC_10_8 + long i = ssl_version; + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, + kSSLProtocolAll, + false); + for(; i <= (ssl_version_max >> 16); i++) { + switch(i) { + case CURL_SSLVERSION_TLSv1_0: + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, + kTLSProtocol1, + true); + break; + case CURL_SSLVERSION_TLSv1_1: + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, + kTLSProtocol11, + true); + break; + case CURL_SSLVERSION_TLSv1_2: + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, + kTLSProtocol12, + true); + break; + case CURL_SSLVERSION_TLSv1_3: + failf(data, "Your version of the OS does not support TLSv1.3"); + return CURLE_SSL_CONNECT_ERROR; + } + } + return CURLE_OK; +#endif /* CURL_SUPPORT_MAC_10_8 */ + } +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ + failf(data, "Secure Transport: cannot set SSL protocol"); + return CURLE_SSL_CONNECT_ERROR; +} + +static int sectransp_cipher_suite_get_str(uint16_t id, char *buf, + size_t buf_size, bool prefer_rfc) +{ + /* are these fortezza suites even supported ? */ + if(id == SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA) + msnprintf(buf, buf_size, "%s", "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA"); + else if(id == SSL_FORTEZZA_DMS_WITH_NULL_SHA) + msnprintf(buf, buf_size, "%s", "SSL_FORTEZZA_DMS_WITH_NULL_SHA"); + /* can TLS_EMPTY_RENEGOTIATION_INFO_SCSV even be set ? */ + else if(id == TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + msnprintf(buf, buf_size, "%s", "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"); + /* do we still need to support these SSL2-only ciphers ? */ + else if(id == SSL_RSA_WITH_RC2_CBC_MD5) + msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_RC2_CBC_MD5"); + else if(id == SSL_RSA_WITH_IDEA_CBC_MD5) + msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_IDEA_CBC_MD5"); + else if(id == SSL_RSA_WITH_DES_CBC_MD5) + msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_DES_CBC_MD5"); + else if(id == SSL_RSA_WITH_3DES_EDE_CBC_MD5) + msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_3DES_EDE_CBC_MD5"); + else + return Curl_cipher_suite_get_str(id, buf, buf_size, prefer_rfc); + return 0; +} + +static uint16_t sectransp_cipher_suite_walk_str(const char **str, + const char **end) +{ + uint16_t id = Curl_cipher_suite_walk_str(str, end); + size_t len = *end - *str; + + if(!id) { + /* are these fortezza suites even supported ? */ + if(strncasecompare("SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA", *str, len)) + id = SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA; + else if(strncasecompare("SSL_FORTEZZA_DMS_WITH_NULL_SHA", *str, len)) + id = SSL_FORTEZZA_DMS_WITH_NULL_SHA; + /* can TLS_EMPTY_RENEGOTIATION_INFO_SCSV even be set ? */ + else if(strncasecompare("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", *str, len)) + id = TLS_EMPTY_RENEGOTIATION_INFO_SCSV; + /* do we still need to support these SSL2-only ciphers ? */ + else if(strncasecompare("SSL_RSA_WITH_RC2_CBC_MD5", *str, len)) + id = SSL_RSA_WITH_RC2_CBC_MD5; + else if(strncasecompare("SSL_RSA_WITH_IDEA_CBC_MD5", *str, len)) + id = SSL_RSA_WITH_IDEA_CBC_MD5; + else if(strncasecompare("SSL_RSA_WITH_DES_CBC_MD5", *str, len)) + id = SSL_RSA_WITH_DES_CBC_MD5; + else if(strncasecompare("SSL_RSA_WITH_3DES_EDE_CBC_MD5", *str, len)) + id = SSL_RSA_WITH_3DES_EDE_CBC_MD5; + } + return id; +} + +/* allocated memory must be freed */ +static SSLCipherSuite * sectransp_get_supported_ciphers(SSLContextRef ssl_ctx, + size_t *len) +{ + SSLCipherSuite *ciphers = NULL; + OSStatus err = noErr; + *len = 0; + + err = SSLGetNumberSupportedCiphers(ssl_ctx, len); + if(err != noErr) + goto failed; + + ciphers = malloc(*len * sizeof(SSLCipherSuite)); + if(!ciphers) + goto failed; + + err = SSLGetSupportedCiphers(ssl_ctx, ciphers, len); + if(err != noErr) + goto failed; + +#if CURL_BUILD_MAC + { + int maj = 0, min = 0; + GetDarwinVersionNumber(&maj, &min); + /* There is a known bug in early versions of Mountain Lion where ST's ECC + ciphers (cipher suite 0xC001 through 0xC032) simply do not work. + Work around the problem here by disabling those ciphers if we are + running in an affected version of OS X. */ + if(maj == 12 && min <= 3) { + size_t i = 0, j = 0; + for(; i < *len; i++) { + if(ciphers[i] >= 0xC001 && ciphers[i] <= 0xC032) + continue; + ciphers[j++] = ciphers[i]; + } + *len = j; + } + } +#endif + + return ciphers; +failed: + *len = 0; + Curl_safefree(ciphers); + return NULL; +} + +static CURLcode sectransp_set_default_ciphers(struct Curl_easy *data, + SSLContextRef ssl_ctx) +{ + CURLcode ret = CURLE_SSL_CIPHER; + size_t count = 0, i, j; + OSStatus err; + size_t supported_len; + SSLCipherSuite *ciphers = NULL; + + ciphers = sectransp_get_supported_ciphers(ssl_ctx, &supported_len); + if(!ciphers) { + failf(data, "SSL: Failed to get supported ciphers"); + goto failed; + } + + /* Intersect the ciphers supported by Secure Transport with the default + * ciphers, using the order of the former. */ + for(i = 0; i < supported_len; i++) { + for(j = 0; j < DEFAULT_CIPHERS_LEN; j++) { + if(default_ciphers[j] == ciphers[i]) { + ciphers[count++] = ciphers[i]; + break; + } + } + } + + if(count == 0) { + failf(data, "SSL: no supported default ciphers"); + goto failed; + } + + err = SSLSetEnabledCiphers(ssl_ctx, ciphers, count); + if(err != noErr) { + failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); + goto failed; + } + + ret = CURLE_OK; +failed: + Curl_safefree(ciphers); + return ret; +} + +static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, + SSLContextRef ssl_ctx, + const char *ciphers) +{ + CURLcode ret = CURLE_SSL_CIPHER; + size_t count = 0, i; + const char *ptr, *end; + OSStatus err; + size_t supported_len; + SSLCipherSuite *supported = NULL; + SSLCipherSuite *selected = NULL; + + supported = sectransp_get_supported_ciphers(ssl_ctx, &supported_len); + if(!supported) { + failf(data, "SSL: Failed to get supported ciphers"); + goto failed; + } + + selected = malloc(supported_len * sizeof(SSLCipherSuite)); + if(!selected) { + failf(data, "SSL: Failed to allocate memory"); + goto failed; + } + + for(ptr = ciphers; ptr[0] != '\0' && count < supported_len; ptr = end) { + uint16_t id = sectransp_cipher_suite_walk_str(&ptr, &end); + + /* Check if cipher is supported */ + if(id) { + for(i = 0; i < supported_len && supported[i] != id; i++); + if(i == supported_len) + id = 0; + } + if(!id) { + if(ptr[0] != '\0') + infof(data, "SSL: unknown cipher in list: \"%.*s\"", (int) (end - ptr), + ptr); + continue; + } + + /* No duplicates allowed (so selected cannot overflow) */ + for(i = 0; i < count && selected[i] != id; i++); + if(i < count) { + infof(data, "SSL: duplicate cipher in list: \"%.*s\"", (int) (end - ptr), + ptr); + continue; + } + + selected[count++] = id; + } + + if(count == 0) { + failf(data, "SSL: no supported cipher in list"); + goto failed; + } + + err = SSLSetEnabledCiphers(ssl_ctx, selected, count); + if(err != noErr) { + failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); + goto failed; + } + + ret = CURLE_OK; +failed: + Curl_safefree(supported); + Curl_safefree(selected); + return ret; +} + +static void sectransp_session_free(void *sessionid, size_t idsize) +{ + /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a + cached session ID inside the Security framework. There is a private + function that does this, but I do not want to have to explain to you why I + got your application rejected from the App Store due to the use of a + private API, so the best we can do is free up our own char array that we + created way back in sectransp_connect_step1... */ + (void)idsize; + Curl_safefree(sessionid); +} + +static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + const struct curl_blob *ssl_cablob = conn_config->ca_info_blob; + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ssl_cablob ? NULL : conn_config->CAfile); + const bool verifypeer = conn_config->verifypeer; + char * const ssl_cert = ssl_config->primary.clientcert; + const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; + char *ciphers; + OSStatus err = noErr; + CURLcode result; +#if CURL_BUILD_MAC + int darwinver_maj = 0, darwinver_min = 0; + + DEBUGASSERT(backend); + + CURL_TRC_CF(data, cf, "connect_step1"); + GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); +#endif /* CURL_BUILD_MAC */ + +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS + if(&SSLCreateContext) { /* use the newer API if available */ + if(backend->ssl_ctx) + CFRelease(backend->ssl_ctx); + backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); + if(!backend->ssl_ctx) { + failf(data, "SSL: could not create a context"); + return CURLE_OUT_OF_MEMORY; + } + } + else { + /* The old ST API does not exist under iOS, so do not compile it: */ +#if CURL_SUPPORT_MAC_10_8 + if(backend->ssl_ctx) + (void)SSLDisposeContext(backend->ssl_ctx); + err = SSLNewContext(false, &(backend->ssl_ctx)); + if(err != noErr) { + failf(data, "SSL: could not create a context: OSStatus %d", err); + return CURLE_OUT_OF_MEMORY; + } +#endif /* CURL_SUPPORT_MAC_10_8 */ + } +#else + if(backend->ssl_ctx) + (void)SSLDisposeContext(backend->ssl_ctx); + err = SSLNewContext(false, &(backend->ssl_ctx)); + if(err != noErr) { + failf(data, "SSL: could not create a context: OSStatus %d", err); + return CURLE_OUT_OF_MEMORY; + } +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ + backend->ssl_write_buffered_length = 0UL; /* reset buffered write length */ + + /* check to see if we have been told to use an explicit SSL/TLS version */ +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS + if(&SSLSetProtocolVersionMax) { + switch(conn_config->version) { + case CURL_SSLVERSION_TLSv1: + (void)SSLSetProtocolVersionMin(backend->ssl_ctx, kTLSProtocol1); +#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \ + defined(HAVE_BUILTIN_AVAILABLE) + if(__builtin_available(macOS 10.13, iOS 11.0, *)) { + (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol13); + } + else { + (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12); + } +#else + (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12); +#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && + defined(HAVE_BUILTIN_AVAILABLE) */ + break; + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + case CURL_SSLVERSION_TLSv1_3: + result = set_ssl_version_min_max(cf, data); + if(result != CURLE_OK) + return result; + break; + case CURL_SSLVERSION_SSLv3: + case CURL_SSLVERSION_SSLv2: + failf(data, "SSL versions not supported"); + return CURLE_NOT_BUILT_IN; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { +#if CURL_SUPPORT_MAC_10_8 + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, + kSSLProtocolAll, + false); + switch(conn_config->version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, + kTLSProtocol1, + true); + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, + kTLSProtocol11, + true); + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, + kTLSProtocol12, + true); + break; + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + case CURL_SSLVERSION_TLSv1_3: + result = set_ssl_version_min_max(cf, data); + if(result != CURLE_OK) + return result; + break; + case CURL_SSLVERSION_SSLv3: + case CURL_SSLVERSION_SSLv2: + failf(data, "SSL versions not supported"); + return CURLE_NOT_BUILT_IN; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* CURL_SUPPORT_MAC_10_8 */ + } +#else + if(conn_config->version_max != CURL_SSLVERSION_MAX_NONE) { + failf(data, "Your version of the OS does not support to set maximum" + " SSL/TLS version"); + return CURLE_SSL_CONNECT_ERROR; + } + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false); + switch(conn_config->version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + case CURL_SSLVERSION_TLSv1_0: + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, + kTLSProtocol1, + true); + break; + case CURL_SSLVERSION_TLSv1_1: + failf(data, "Your version of the OS does not support TLSv1.1"); + return CURLE_SSL_CONNECT_ERROR; + case CURL_SSLVERSION_TLSv1_2: + failf(data, "Your version of the OS does not support TLSv1.2"); + return CURLE_SSL_CONNECT_ERROR; + case CURL_SSLVERSION_TLSv1_3: + failf(data, "Your version of the OS does not support TLSv1.3"); + return CURLE_SSL_CONNECT_ERROR; + case CURL_SSLVERSION_SSLv2: + case CURL_SSLVERSION_SSLv3: + failf(data, "SSL versions not supported"); + return CURLE_NOT_BUILT_IN; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ + +#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \ + defined(HAVE_BUILTIN_AVAILABLE) + if(connssl->alpn) { + if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { + struct alpn_proto_buf proto; + size_t i; + CFStringRef cstr; + CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0, + &kCFTypeArrayCallBacks); + for(i = 0; i < connssl->alpn->count; ++i) { + cstr = CFStringCreateWithCString(NULL, connssl->alpn->entries[i], + kCFStringEncodingUTF8); + if(!cstr) + return CURLE_OUT_OF_MEMORY; + CFArrayAppendValue(alpnArr, cstr); + CFRelease(cstr); + } + err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr); + if(err != noErr) + infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d", + err); + CFRelease(alpnArr); + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } + } +#endif + + if(ssl_config->key) { + infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure " + "Transport. The private key must be in the Keychain."); + } + + if(ssl_cert || ssl_cert_blob) { + bool is_cert_data = ssl_cert_blob != NULL; + bool is_cert_file = (!is_cert_data) && is_file(ssl_cert); + SecIdentityRef cert_and_key = NULL; + + /* User wants to authenticate with a client cert. Look for it. Assume that + the user wants to use an identity loaded from the Keychain. If not, try + it as a file on disk */ + + if(!is_cert_data) + err = CopyIdentityWithLabel(ssl_cert, &cert_and_key); + else + err = !noErr; + if((err != noErr) && (is_cert_file || is_cert_data)) { + if(!ssl_config->cert_type) + infof(data, "SSL: Certificate type not set, assuming " + "PKCS#12 format."); + else if(!strcasecompare(ssl_config->cert_type, "P12")) { + failf(data, "SSL: The Security framework only supports " + "loading identities that are in PKCS#12 format."); + return CURLE_SSL_CERTPROBLEM; + } + + err = CopyIdentityFromPKCS12File(ssl_cert, ssl_cert_blob, + ssl_config->key_passwd, + &cert_and_key); + } + + if(err == noErr && cert_and_key) { + SecCertificateRef cert = NULL; + CFTypeRef certs_c[1]; + CFArrayRef certs; + + /* If we found one, print it out: */ + err = SecIdentityCopyCertificate(cert_and_key, &cert); + if(err == noErr) { + char *certp; + result = CopyCertSubject(data, cert, &certp); + if(!result) { + infof(data, "Client certificate: %s", certp); + free(certp); + } + + CFRelease(cert); + if(result == CURLE_PEER_FAILED_VERIFICATION) + return CURLE_SSL_CERTPROBLEM; + if(result) + return result; + } + certs_c[0] = cert_and_key; + certs = CFArrayCreate(NULL, (const void **)certs_c, 1L, + &kCFTypeArrayCallBacks); + err = SSLSetCertificate(backend->ssl_ctx, certs); + if(certs) + CFRelease(certs); + if(err != noErr) { + failf(data, "SSL: SSLSetCertificate() failed: OSStatus %d", err); + return CURLE_SSL_CERTPROBLEM; + } + CFRelease(cert_and_key); + } + else { + const char *cert_showfilename_error = + is_cert_data ? "(memory blob)" : ssl_cert; + + switch(err) { + case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */ + failf(data, "SSL: Incorrect password for the certificate \"%s\" " + "and its private key.", cert_showfilename_error); + break; + case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */ + failf(data, "SSL: Couldn't make sense of the data in the " + "certificate \"%s\" and its private key.", + cert_showfilename_error); + break; + case -25260: /* errSecPassphraseRequired */ + failf(data, "SSL The certificate \"%s\" requires a password.", + cert_showfilename_error); + break; + case errSecItemNotFound: + failf(data, "SSL: cannot find the certificate \"%s\" and its private " + "key in the Keychain.", cert_showfilename_error); + break; + default: + failf(data, "SSL: cannot load the certificate \"%s\" and its private " + "key: OSStatus %d", cert_showfilename_error, err); + break; + } + return CURLE_SSL_CERTPROBLEM; + } + } + + /* SSL always tries to verify the peer, this only says whether it should + * fail to connect if the verification fails, or if it should continue + * anyway. In the latter case the result of the verification is checked with + * SSL_get_verify_result() below. */ +#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS + /* Snow Leopard introduced the SSLSetSessionOption() function, but due to + a library bug with the way the kSSLSessionOptionBreakOnServerAuth flag + works, it does not work as expected under Snow Leopard, Lion or + Mountain Lion. + So we need to call SSLSetEnableCertVerify() on those older cats in order + to disable certificate validation if the user turned that off. + (SecureTransport will always validate the certificate chain by + default.) + Note: + Darwin 11.x.x is Lion (10.7) + Darwin 12.x.x is Mountain Lion (10.8) + Darwin 13.x.x is Mavericks (10.9) + Darwin 14.x.x is Yosemite (10.10) + Darwin 15.x.x is El Capitan (10.11) + */ +#if CURL_BUILD_MAC + if(&SSLSetSessionOption && darwinver_maj >= 13) { +#else + if(&SSLSetSessionOption) { +#endif /* CURL_BUILD_MAC */ + bool break_on_auth = !conn_config->verifypeer || + ssl_cafile || ssl_cablob; + err = SSLSetSessionOption(backend->ssl_ctx, + kSSLSessionOptionBreakOnServerAuth, + break_on_auth); + if(err != noErr) { + failf(data, "SSL: SSLSetSessionOption() failed: OSStatus %d", err); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { +#if CURL_SUPPORT_MAC_10_8 + err = SSLSetEnableCertVerify(backend->ssl_ctx, + conn_config->verifypeer?true:false); + if(err != noErr) { + failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* CURL_SUPPORT_MAC_10_8 */ + } +#else + err = SSLSetEnableCertVerify(backend->ssl_ctx, + conn_config->verifypeer?true:false); + if(err != noErr) { + failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ + + if((ssl_cafile || ssl_cablob) && verifypeer) { + bool is_cert_data = ssl_cablob != NULL; + bool is_cert_file = (!is_cert_data) && is_file(ssl_cafile); + + if(!(is_cert_file || is_cert_data)) { + failf(data, "SSL: cannot load CA certificate file %s", + ssl_cafile ? ssl_cafile : "(blob memory)"); + return CURLE_SSL_CACERT_BADFILE; + } + } + + /* Configure hostname check. SNI is used if available. + * Both hostname check and SNI require SSLSetPeerDomainName(). + * Also: the verifyhost setting influences SNI usage */ + if(conn_config->verifyhost) { + char *server = connssl->peer.sni? + connssl->peer.sni : connssl->peer.hostname; + err = SSLSetPeerDomainName(backend->ssl_ctx, server, strlen(server)); + + if(err != noErr) { + failf(data, "SSL: SSLSetPeerDomainName() failed: OSStatus %d", + err); + return CURLE_SSL_CONNECT_ERROR; + } + + if(connssl->peer.type != CURL_SSL_PEER_DNS) { + infof(data, "WARNING: using IP address, SNI is being disabled by " + "the OS."); + } + } + else { + infof(data, "WARNING: disabling hostname validation also disables SNI."); + } + + ciphers = conn_config->cipher_list; + if(ciphers) { + result = sectransp_set_selected_ciphers(data, backend->ssl_ctx, ciphers); + } + else { + result = sectransp_set_default_ciphers(data, backend->ssl_ctx); + } + if(result != CURLE_OK) { + failf(data, "SSL: Unable to set ciphers for SSL/TLS handshake. " + "Error code: %d", (int)result); + return CURLE_SSL_CIPHER; + } + +#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 + /* We want to enable 1/n-1 when using a CBC cipher unless the user + specifically does not want us doing that: */ + if(&SSLSetSessionOption) { + SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord, + !ssl_config->enable_beast); + SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart, + ssl_config->falsestart); /* false start support */ + } +#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ + + /* Check if there is a cached ID we can/should use here! */ + if(ssl_config->primary.cache_session) { + char *ssl_sessionid; + size_t ssl_sessionid_len; + + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + (void **)&ssl_sessionid, &ssl_sessionid_len)) { + /* we got a session id, use it! */ + err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); + Curl_ssl_sessionid_unlock(data); + if(err != noErr) { + failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); + return CURLE_SSL_CONNECT_ERROR; + } + /* Informational message */ + infof(data, "SSL reusing session ID"); + } + /* If there is not one, then let's make one up! This has to be done prior + to starting the handshake. */ + else { + ssl_sessionid = + aprintf("%s:%d:%d:%s:%d", + ssl_cafile ? ssl_cafile : "(blob memory)", + verifypeer, conn_config->verifyhost, connssl->peer.hostname, + connssl->peer.port); + ssl_sessionid_len = strlen(ssl_sessionid); + + err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); + if(err != noErr) { + Curl_ssl_sessionid_unlock(data); + failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); + return CURLE_SSL_CONNECT_ERROR; + } + + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, ssl_sessionid, + ssl_sessionid_len, + sectransp_session_free); + Curl_ssl_sessionid_unlock(data); + if(result) + return result; + } + } + + err = SSLSetIOFuncs(backend->ssl_ctx, + sectransp_bio_cf_in_read, + sectransp_bio_cf_out_write); + if(err != noErr) { + failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err); + return CURLE_SSL_CONNECT_ERROR; + } + + err = SSLSetConnection(backend->ssl_ctx, cf); + if(err != noErr) { + failf(data, "SSL: SSLSetConnection() failed: %d", err); + return CURLE_SSL_CONNECT_ERROR; + } + + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; +} + +static long pem_to_der(const char *in, unsigned char **out, size_t *outlen) +{ + char *sep_start, *sep_end, *cert_start, *cert_end; + size_t i, j, err; + size_t len; + char *b64; + + /* Jump through the separators at the beginning of the certificate. */ + sep_start = strstr(in, "-----"); + if(!sep_start) + return 0; + cert_start = strstr(sep_start + 1, "-----"); + if(!cert_start) + return -1; + + cert_start += 5; + + /* Find separator after the end of the certificate. */ + cert_end = strstr(cert_start, "-----"); + if(!cert_end) + return -1; + + sep_end = strstr(cert_end + 1, "-----"); + if(!sep_end) + return -1; + sep_end += 5; + + len = cert_end - cert_start; + b64 = malloc(len + 1); + if(!b64) + return -1; + + /* Create base64 string without linefeeds. */ + for(i = 0, j = 0; i < len; i++) { + if(cert_start[i] != '\r' && cert_start[i] != '\n') + b64[j++] = cert_start[i]; + } + b64[j] = '\0'; + + err = Curl_base64_decode((const char *)b64, out, outlen); + free(b64); + if(err) { + free(*out); + return -1; + } + + return sep_end - in; +} + +#define MAX_CERTS_SIZE (50*1024*1024) /* arbitrary - to catch mistakes */ + +static int read_cert(const char *file, unsigned char **out, size_t *outlen) +{ + int fd; + ssize_t n; + unsigned char buf[512]; + struct dynbuf certs; + + Curl_dyn_init(&certs, MAX_CERTS_SIZE); + + fd = open(file, 0); + if(fd < 0) + return -1; + + for(;;) { + n = read(fd, buf, sizeof(buf)); + if(!n) + break; + if(n < 0) { + close(fd); + Curl_dyn_free(&certs); + return -1; + } + if(Curl_dyn_addn(&certs, buf, n)) { + close(fd); + return -1; + } + } + close(fd); + + *out = Curl_dyn_uptr(&certs); + *outlen = Curl_dyn_len(&certs); + + return 0; +} + +static CURLcode append_cert_to_array(struct Curl_easy *data, + const unsigned char *buf, size_t buflen, + CFMutableArrayRef array) +{ + char *certp; + CURLcode result; + SecCertificateRef cacert; + CFDataRef certdata; + + certdata = CFDataCreate(kCFAllocatorDefault, buf, (CFIndex)buflen); + if(!certdata) { + failf(data, "SSL: failed to allocate array for CA certificate"); + return CURLE_OUT_OF_MEMORY; + } + + cacert = SecCertificateCreateWithData(kCFAllocatorDefault, certdata); + CFRelease(certdata); + if(!cacert) { + failf(data, "SSL: failed to create SecCertificate from CA certificate"); + return CURLE_SSL_CACERT_BADFILE; + } + + /* Check if cacert is valid. */ + result = CopyCertSubject(data, cacert, &certp); + switch(result) { + case CURLE_OK: + break; + case CURLE_PEER_FAILED_VERIFICATION: + return CURLE_SSL_CACERT_BADFILE; + case CURLE_OUT_OF_MEMORY: + default: + return result; + } + free(certp); + + CFArrayAppendValue(array, cacert); + CFRelease(cacert); + + return CURLE_OK; +} + +static CURLcode verify_cert_buf(struct Curl_cfilter *cf, + struct Curl_easy *data, + const unsigned char *certbuf, size_t buflen, + SSLContextRef ctx) +{ + int n = 0; + CURLcode rc; + long res; + unsigned char *der; + size_t derlen, offset = 0; + OSStatus ret; + SecTrustResultType trust_eval; + CFMutableArrayRef array = NULL; + SecTrustRef trust = NULL; + CURLcode result = CURLE_PEER_FAILED_VERIFICATION; + (void)cf; + /* + * Certbuf now contains the contents of the certificate file, which can be + * - a single DER certificate, + * - a single PEM certificate or + * - a bunch of PEM certificates (certificate bundle). + * + * Go through certbuf, and convert any PEM certificate in it into DER + * format. + */ + array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + if(!array) { + failf(data, "SSL: out of memory creating CA certificate array"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + while(offset < buflen) { + n++; + + /* + * Check if the certificate is in PEM format, and convert it to DER. If + * this fails, we assume the certificate is in DER format. + */ + res = pem_to_der((const char *)certbuf + offset, &der, &derlen); + if(res < 0) { + failf(data, "SSL: invalid CA certificate #%d (offset %zu) in bundle", + n, offset); + result = CURLE_SSL_CACERT_BADFILE; + goto out; + } + offset += res; + + if(res == 0 && offset == 0) { + /* This is not a PEM file, probably a certificate in DER format. */ + rc = append_cert_to_array(data, certbuf, buflen, array); + if(rc != CURLE_OK) { + CURL_TRC_CF(data, cf, "append_cert for CA failed"); + result = rc; + goto out; + } + break; + } + else if(res == 0) { + /* No more certificates in the bundle. */ + break; + } + + rc = append_cert_to_array(data, der, derlen, array); + free(der); + if(rc != CURLE_OK) { + CURL_TRC_CF(data, cf, "append_cert for CA failed"); + result = rc; + goto out; + } + } + + ret = SSLCopyPeerTrust(ctx, &trust); + if(!trust) { + failf(data, "SSL: error getting certificate chain"); + goto out; + } + else if(ret != noErr) { + failf(data, "SSLCopyPeerTrust() returned error %d", ret); + goto out; + } + + CURL_TRC_CF(data, cf, "setting %d trust anchors", n); + ret = SecTrustSetAnchorCertificates(trust, array); + if(ret != noErr) { + failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); + goto out; + } + ret = SecTrustSetAnchorCertificatesOnly(trust, true); + if(ret != noErr) { + failf(data, "SecTrustSetAnchorCertificatesOnly() returned error %d", ret); + goto out; + } + + trust_eval = 0; + ret = SecTrustEvaluate(trust, &trust_eval); + if(ret != noErr) { + failf(data, "SecTrustEvaluate() returned error %d", ret); + goto out; + } + + switch(trust_eval) { + case kSecTrustResultUnspecified: + /* what does this really mean? */ + CURL_TRC_CF(data, cf, "trust result: Unspecified"); + result = CURLE_OK; + goto out; + case kSecTrustResultProceed: + CURL_TRC_CF(data, cf, "trust result: Proceed"); + result = CURLE_OK; + goto out; + + case kSecTrustResultRecoverableTrustFailure: + failf(data, "SSL: peer not verified: RecoverableTrustFailure"); + goto out; + case kSecTrustResultDeny: + failf(data, "SSL: peer not verified: Deny"); + goto out; + default: + failf(data, "SSL: perr not verified: result=%d", trust_eval); + goto out; + } + +out: + if(trust) + CFRelease(trust); + if(array) + CFRelease(array); + return result; +} + +static CURLcode verify_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, const char *cafile, + const struct curl_blob *ca_info_blob, + SSLContextRef ctx) +{ + CURLcode result; + unsigned char *certbuf; + size_t buflen; + bool free_certbuf = FALSE; + + if(ca_info_blob) { + CURL_TRC_CF(data, cf, "verify_peer, CA from config blob"); + certbuf = ca_info_blob->data; + buflen = ca_info_blob->len; + } + else if(cafile) { + CURL_TRC_CF(data, cf, "verify_peer, CA from file '%s'", cafile); + if(read_cert(cafile, &certbuf, &buflen) < 0) { + failf(data, "SSL: failed to read or invalid CA certificate"); + return CURLE_SSL_CACERT_BADFILE; + } + free_certbuf = TRUE; + } + else + return CURLE_SSL_CACERT_BADFILE; + + result = verify_cert_buf(cf, data, certbuf, buflen, ctx); + if(free_certbuf) + free(certbuf); + return result; +} + + +#ifdef SECTRANSP_PINNEDPUBKEY +static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, + SSLContextRef ctx, + const char *pinnedpubkey) +{ /* Scratch */ + size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24; + unsigned char *pubkey = NULL, *realpubkey = NULL; + const unsigned char *spkiHeader = NULL; + CFDataRef publicKeyBits = NULL; + + /* Result is returned to caller */ + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + + /* if a path was not specified, do not pin */ + if(!pinnedpubkey) + return CURLE_OK; + + + if(!ctx) + return result; + + do { + SecTrustRef trust; + OSStatus ret; + SecKeyRef keyRef; + + ret = SSLCopyPeerTrust(ctx, &trust); + if(ret != noErr || !trust) + break; + + keyRef = SecTrustCopyPublicKey(trust); + CFRelease(trust); + if(!keyRef) + break; + +#ifdef SECTRANSP_PINNEDPUBKEY_V1 + + publicKeyBits = SecKeyCopyExternalRepresentation(keyRef, NULL); + CFRelease(keyRef); + if(!publicKeyBits) + break; + +#elif SECTRANSP_PINNEDPUBKEY_V2 + + { + OSStatus success; + success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, + &publicKeyBits); + CFRelease(keyRef); + if(success != errSecSuccess || !publicKeyBits) + break; + } + +#endif /* SECTRANSP_PINNEDPUBKEY_V2 */ + + pubkeylen = (size_t)CFDataGetLength(publicKeyBits); + pubkey = (unsigned char *)CFDataGetBytePtr(publicKeyBits); + + switch(pubkeylen) { + case 526: + /* 4096 bit RSA pubkeylen == 526 */ + spkiHeader = rsa4096SpkiHeader; + break; + case 270: + /* 2048 bit RSA pubkeylen == 270 */ + spkiHeader = rsa2048SpkiHeader; + break; +#ifdef SECTRANSP_PINNEDPUBKEY_V1 + case 65: + /* ecDSA secp256r1 pubkeylen == 65 */ + spkiHeader = ecDsaSecp256r1SpkiHeader; + spkiHeaderLength = 26; + break; + case 97: + /* ecDSA secp384r1 pubkeylen == 97 */ + spkiHeader = ecDsaSecp384r1SpkiHeader; + spkiHeaderLength = 23; + break; + default: + infof(data, "SSL: unhandled public key length: %zu", pubkeylen); +#elif SECTRANSP_PINNEDPUBKEY_V2 + default: + /* ecDSA secp256r1 pubkeylen == 91 header already included? + * ecDSA secp384r1 header already included too + * we assume rest of algorithms do same, so do nothing + */ + result = Curl_pin_peer_pubkey(data, pinnedpubkey, pubkey, + pubkeylen); +#endif /* SECTRANSP_PINNEDPUBKEY_V2 */ + continue; /* break from loop */ + } + + realpubkeylen = pubkeylen + spkiHeaderLength; + realpubkey = malloc(realpubkeylen); + if(!realpubkey) + break; + + memcpy(realpubkey, spkiHeader, spkiHeaderLength); + memcpy(realpubkey + spkiHeaderLength, pubkey, pubkeylen); + + result = Curl_pin_peer_pubkey(data, pinnedpubkey, realpubkey, + realpubkeylen); + + } while(0); + + Curl_safefree(realpubkey); + if(publicKeyBits) + CFRelease(publicKeyBits); + + return result; +} +#endif /* SECTRANSP_PINNEDPUBKEY */ + +static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + OSStatus err; + SSLCipherSuite cipher; + SSLProtocol protocol = 0; + + DEBUGASSERT(ssl_connect_2 == connssl->connecting_state); + DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step2"); + + /* Here goes nothing: */ +check_handshake: + connssl->io_need = CURL_SSL_IO_NEED_NONE; + err = SSLHandshake(backend->ssl_ctx); + + if(err != noErr) { + switch(err) { + case errSSLWouldBlock: /* they are not done with us yet */ + connssl->io_need = backend->ssl_direction ? + CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV; + return CURLE_OK; + + /* The below is errSSLServerAuthCompleted; it is not defined in + Leopard's headers */ + case -9841: + if((conn_config->CAfile || conn_config->ca_info_blob) && + conn_config->verifypeer) { + CURLcode result = verify_cert(cf, data, conn_config->CAfile, + conn_config->ca_info_blob, + backend->ssl_ctx); + if(result) + return result; + } + /* the documentation says we need to call SSLHandshake() again */ + goto check_handshake; + + /* Problem with encrypt / decrypt */ + case errSSLPeerDecodeError: + failf(data, "Decode failed"); + break; + case errSSLDecryptionFail: + case errSSLPeerDecryptionFail: + failf(data, "Decryption failed"); + break; + case errSSLPeerDecryptError: + failf(data, "A decryption error occurred"); + break; + case errSSLBadCipherSuite: + failf(data, "A bad SSL cipher suite was encountered"); + break; + case errSSLCrypto: + failf(data, "An underlying cryptographic error was encountered"); + break; +#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 + case errSSLWeakPeerEphemeralDHKey: + failf(data, "Indicates a weak ephemeral Diffie-Hellman key"); + break; +#endif + + /* Problem with the message record validation */ + case errSSLBadRecordMac: + case errSSLPeerBadRecordMac: + failf(data, "A record with a bad message authentication code (MAC) " + "was encountered"); + break; + case errSSLRecordOverflow: + case errSSLPeerRecordOverflow: + failf(data, "A record overflow occurred"); + break; + + /* Problem with zlib decompression */ + case errSSLPeerDecompressFail: + failf(data, "Decompression failed"); + break; + + /* Problem with access */ + case errSSLPeerAccessDenied: + failf(data, "Access was denied"); + break; + case errSSLPeerInsufficientSecurity: + failf(data, "There is insufficient security for this operation"); + break; + + /* These are all certificate problems with the server: */ + case errSSLXCertChainInvalid: + failf(data, "SSL certificate problem: Invalid certificate chain"); + return CURLE_PEER_FAILED_VERIFICATION; + case errSSLUnknownRootCert: + failf(data, "SSL certificate problem: Untrusted root certificate"); + return CURLE_PEER_FAILED_VERIFICATION; + case errSSLNoRootCert: + failf(data, "SSL certificate problem: No root certificate"); + return CURLE_PEER_FAILED_VERIFICATION; + case errSSLCertNotYetValid: + failf(data, "SSL certificate problem: The certificate chain had a " + "certificate that is not yet valid"); + return CURLE_PEER_FAILED_VERIFICATION; + case errSSLCertExpired: + case errSSLPeerCertExpired: + failf(data, "SSL certificate problem: Certificate chain had an " + "expired certificate"); + return CURLE_PEER_FAILED_VERIFICATION; + case errSSLBadCert: + case errSSLPeerBadCert: + failf(data, "SSL certificate problem: Couldn't understand the server " + "certificate format"); + return CURLE_PEER_FAILED_VERIFICATION; + case errSSLPeerUnsupportedCert: + failf(data, "SSL certificate problem: An unsupported certificate " + "format was encountered"); + return CURLE_PEER_FAILED_VERIFICATION; + case errSSLPeerCertRevoked: + failf(data, "SSL certificate problem: The certificate was revoked"); + return CURLE_PEER_FAILED_VERIFICATION; + case errSSLPeerCertUnknown: + failf(data, "SSL certificate problem: The certificate is unknown"); + return CURLE_PEER_FAILED_VERIFICATION; + + /* These are all certificate problems with the client: */ + case errSecAuthFailed: + failf(data, "SSL authentication failed"); + break; + case errSSLPeerHandshakeFail: + failf(data, "SSL peer handshake failed, the server most likely " + "requires a client certificate to connect"); + break; + case errSSLPeerUnknownCA: + failf(data, "SSL server rejected the client certificate due to " + "the certificate being signed by an unknown certificate " + "authority"); + break; + + /* This error is raised if the server's cert did not match the server's + hostname: */ + case errSSLHostNameMismatch: + failf(data, "SSL certificate peer verification failed, the " + "certificate did not match \"%s\"\n", connssl->peer.dispname); + return CURLE_PEER_FAILED_VERIFICATION; + + /* Problem with SSL / TLS negotiation */ + case errSSLNegotiation: + failf(data, "Could not negotiate an SSL cipher suite with the server"); + break; + case errSSLBadConfiguration: + failf(data, "A configuration error occurred"); + break; + case errSSLProtocol: + failf(data, "SSL protocol error"); + break; + case errSSLPeerProtocolVersion: + failf(data, "A bad protocol version was encountered"); + break; + case errSSLPeerNoRenegotiation: + failf(data, "No renegotiation is allowed"); + break; + + /* Generic handshake errors: */ + case errSSLConnectionRefused: + failf(data, "Server dropped the connection during the SSL handshake"); + break; + case errSSLClosedAbort: + failf(data, "Server aborted the SSL handshake"); + break; + case errSSLClosedGraceful: + failf(data, "The connection closed gracefully"); + break; + case errSSLClosedNoNotify: + failf(data, "The server closed the session with no notification"); + break; + /* Sometimes paramErr happens with buggy ciphers: */ + case paramErr: + case errSSLInternal: + case errSSLPeerInternalError: + failf(data, "Internal SSL engine error encountered during the " + "SSL handshake"); + break; + case errSSLFatalAlert: + failf(data, "Fatal SSL engine error encountered during the SSL " + "handshake"); + break; + /* Unclassified error */ + case errSSLBufferOverflow: + failf(data, "An insufficient buffer was provided"); + break; + case errSSLIllegalParam: + failf(data, "An illegal parameter was encountered"); + break; + case errSSLModuleAttach: + failf(data, "Module attach failure"); + break; + case errSSLSessionNotFound: + failf(data, "An attempt to restore an unknown session failed"); + break; + case errSSLPeerExportRestriction: + failf(data, "An export restriction occurred"); + break; + case errSSLPeerUserCancelled: + failf(data, "The user canceled the operation"); + break; + case errSSLPeerUnexpectedMsg: + failf(data, "Peer rejected unexpected message"); + break; +#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 + /* Treating non-fatal error as fatal like before */ + case errSSLClientHelloReceived: + failf(data, "A non-fatal result for providing a server name " + "indication"); + break; +#endif + + /* Error codes defined in the enum but should never be returned. + We list them here just in case. */ +#if CURL_BUILD_MAC_10_6 + /* Only returned when kSSLSessionOptionBreakOnCertRequested is set */ + case errSSLClientCertRequested: + failf(data, "Server requested a client certificate during the " + "handshake"); + return CURLE_SSL_CLIENTCERT; +#endif +#if CURL_BUILD_MAC_10_9 + /* Alias for errSSLLast, end of error range */ + case errSSLUnexpectedRecord: + failf(data, "Unexpected (skipped) record in DTLS"); + break; +#endif + default: + /* May also return codes listed in Security Framework Result Codes */ + failf(data, "Unknown SSL protocol error in connection to %s:%d", + connssl->peer.hostname, err); + break; + } + return CURLE_SSL_CONNECT_ERROR; + } + else { + char cipher_str[64]; + /* we have been connected fine, we are not waiting for anything else. */ + connssl->connecting_state = ssl_connect_3; + +#ifdef SECTRANSP_PINNEDPUBKEY + if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) { + CURLcode result = + pkp_pin_peer_pubkey(data, backend->ssl_ctx, + data->set.str[STRING_SSL_PINNEDPUBLICKEY]); + if(result) { + failf(data, "SSL: public key does not match pinned public key"); + return result; + } + } +#endif /* SECTRANSP_PINNEDPUBKEY */ + + /* Informational message */ + (void)SSLGetNegotiatedCipher(backend->ssl_ctx, &cipher); + (void)SSLGetNegotiatedProtocolVersion(backend->ssl_ctx, &protocol); + + sectransp_cipher_suite_get_str((uint16_t) cipher, cipher_str, + sizeof(cipher_str), true); + switch(protocol) { + case kSSLProtocol2: + infof(data, "SSL 2.0 connection using %s", cipher_str); + break; + case kSSLProtocol3: + infof(data, "SSL 3.0 connection using %s", cipher_str); + break; + case kTLSProtocol1: + infof(data, "TLS 1.0 connection using %s", cipher_str); + break; +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS + case kTLSProtocol11: + infof(data, "TLS 1.1 connection using %s", cipher_str); + break; + case kTLSProtocol12: + infof(data, "TLS 1.2 connection using %s", cipher_str); + break; +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ +#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 + case kTLSProtocol13: + infof(data, "TLS 1.3 connection using %s", cipher_str); + break; +#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ + default: + infof(data, "Unknown protocol connection"); + break; + } + +#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \ + defined(HAVE_BUILTIN_AVAILABLE) + if(connssl->alpn) { + if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { + CFArrayRef alpnArr = NULL; + CFStringRef chosenProtocol = NULL; + err = SSLCopyALPNProtocols(backend->ssl_ctx, &alpnArr); + + if(err == noErr && alpnArr && CFArrayGetCount(alpnArr) >= 1) + chosenProtocol = CFArrayGetValueAtIndex(alpnArr, 0); + +#ifdef USE_HTTP2 + if(chosenProtocol && + !CFStringCompare(chosenProtocol, CFSTR(ALPN_H2), 0)) { + cf->conn->alpn = CURL_HTTP_VERSION_2; + } + else +#endif + if(chosenProtocol && + !CFStringCompare(chosenProtocol, CFSTR(ALPN_HTTP_1_1), 0)) { + cf->conn->alpn = CURL_HTTP_VERSION_1_1; + } + else + infof(data, VTLS_INFOF_NO_ALPN); + + Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? + BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + + /* chosenProtocol is a reference to the string within alpnArr + and does not need to be freed separately */ + if(alpnArr) + CFRelease(alpnArr); + } + } +#endif + + return CURLE_OK; + } +} + +static CURLcode +add_cert_to_certinfo(struct Curl_easy *data, + SecCertificateRef server_cert, + int idx) +{ + CURLcode result = CURLE_OK; + const char *beg; + const char *end; + CFDataRef cert_data = SecCertificateCopyData(server_cert); + + if(!cert_data) + return CURLE_PEER_FAILED_VERIFICATION; + + beg = (const char *)CFDataGetBytePtr(cert_data); + end = beg + CFDataGetLength(cert_data); + result = Curl_extract_certinfo(data, idx, beg, end); + CFRelease(cert_data); + return result; +} + +static CURLcode +collect_server_cert_single(struct Curl_cfilter *cf, struct Curl_easy *data, + SecCertificateRef server_cert, + CFIndex idx) +{ + CURLcode result = CURLE_OK; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(data->set.verbose) { + char *certp; + result = CopyCertSubject(data, server_cert, &certp); + if(!result) { + infof(data, "Server certificate: %s", certp); + free(certp); + } + } +#endif + if(ssl_config->certinfo) + result = add_cert_to_certinfo(data, server_cert, (int)idx); + return result; +} + +/* This should be called during step3 of the connection at the earliest */ +static CURLcode collect_server_cert(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + const bool show_verbose_server_cert = data->set.verbose; +#else + const bool show_verbose_server_cert = false; +#endif + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = ssl_config->certinfo ? + CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + CFArrayRef server_certs = NULL; + SecCertificateRef server_cert; + OSStatus err; + CFIndex i, count; + SecTrustRef trust = NULL; + + DEBUGASSERT(backend); + + if(!show_verbose_server_cert && !ssl_config->certinfo) + return CURLE_OK; + + if(!backend->ssl_ctx) + return result; + +#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS +#if CURL_BUILD_IOS +#pragma unused(server_certs) + err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); + /* For some reason, SSLCopyPeerTrust() can return noErr and yet return + a null trust, so be on guard for that: */ + if(err == noErr && trust) { + count = SecTrustGetCertificateCount(trust); + if(ssl_config->certinfo) + result = Curl_ssl_init_certinfo(data, (int)count); + for(i = 0L ; !result && (i < count) ; i++) { + server_cert = SecTrustGetCertificateAtIndex(trust, i); + result = collect_server_cert_single(cf, data, server_cert, i); + } + CFRelease(trust); + } +#else + /* SSLCopyPeerCertificates() is deprecated as of Mountain Lion. + The function SecTrustGetCertificateAtIndex() is officially present + in Lion, but it is unfortunately also present in Snow Leopard as + private API and does not work as expected. So we have to look for + a different symbol to make sure this code is only executed under + Lion or later. */ + if(&SecTrustCopyPublicKey) { +#pragma unused(server_certs) + err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); + /* For some reason, SSLCopyPeerTrust() can return noErr and yet return + a null trust, so be on guard for that: */ + if(err == noErr && trust) { + count = SecTrustGetCertificateCount(trust); + if(ssl_config->certinfo) + result = Curl_ssl_init_certinfo(data, (int)count); + for(i = 0L ; !result && (i < count) ; i++) { + server_cert = SecTrustGetCertificateAtIndex(trust, i); + result = collect_server_cert_single(cf, data, server_cert, i); + } + CFRelease(trust); + } + } + else { +#if CURL_SUPPORT_MAC_10_8 + err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); + /* Just in case SSLCopyPeerCertificates() returns null too... */ + if(err == noErr && server_certs) { + count = CFArrayGetCount(server_certs); + if(ssl_config->certinfo) + result = Curl_ssl_init_certinfo(data, (int)count); + for(i = 0L ; !result && (i < count) ; i++) { + server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, + i); + result = collect_server_cert_single(cf, data, server_cert, i); + } + CFRelease(server_certs); + } +#endif /* CURL_SUPPORT_MAC_10_8 */ + } +#endif /* CURL_BUILD_IOS */ +#else +#pragma unused(trust) + err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); + if(err == noErr) { + count = CFArrayGetCount(server_certs); + if(ssl_config->certinfo) + result = Curl_ssl_init_certinfo(data, (int)count); + for(i = 0L ; !result && (i < count) ; i++) { + server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i); + result = collect_server_cert_single(cf, data, server_cert, i); + } + CFRelease(server_certs); + } +#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ + return result; +} + +static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + CURLcode result; + + CURL_TRC_CF(data, cf, "connect_step3"); + /* There is no step 3! + * Well, okay, let's collect server certificates, and if verbose mode is on, + * let's print the details of the server certificates. */ + result = collect_server_cert(cf, data); + if(result) + return result; + + connssl->connecting_state = ssl_connect_done; + return CURLE_OK; +} + +static CURLcode +sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, + bool nonblocking, + bool *done) +{ + CURLcode result; + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + int what; + + /* check if the connection has already been established */ + if(ssl_connection_complete == connssl->state) { + *done = TRUE; + return CURLE_OK; + } + + if(ssl_connect_1 == connssl->connecting_state) { + /* Find out how much more time we are allowed */ + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + result = sectransp_connect_step1(cf, data); + if(result) + return result; + } + + while(ssl_connect_2 == connssl->connecting_state) { + + /* check allowed time left */ + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { + + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; + + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + nonblocking ? 0 : timeout_ms); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + return CURLE_SSL_CONNECT_ERROR; + } + else if(0 == what) { + if(nonblocking) { + *done = FALSE; + return CURLE_OK; + } + else { + /* timeout */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + } + /* socket is readable or writable */ + } + + /* Run transaction, and return to the caller if it failed or if this + * connection is done nonblocking and this loop would execute again. This + * permits the owner of a multi handle to abort a connection attempt + * before step2 has completed while ensuring that a client using select() + * or epoll() will always have a valid fdset to wait on. + */ + result = sectransp_connect_step2(cf, data); + if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) + return result; + + } /* repeat step2 until all transactions are done. */ + + + if(ssl_connect_3 == connssl->connecting_state) { + result = sectransp_connect_step3(cf, data); + if(result) + return result; + } + + if(ssl_connect_done == connssl->connecting_state) { + CURL_TRC_CF(data, cf, "connected"); + connssl->state = ssl_connection_complete; + *done = TRUE; + } + else + *done = FALSE; + + /* Reset our connect state machine */ + connssl->connecting_state = ssl_connect_1; + + return CURLE_OK; +} + +static CURLcode sectransp_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + return sectransp_connect_common(cf, data, TRUE, done); +} + +static CURLcode sectransp_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode result; + bool done = FALSE; + + result = sectransp_connect_common(cf, data, FALSE, &done); + + if(result) + return result; + + DEBUGASSERT(done); + + return CURLE_OK; +} + +static ssize_t sectransp_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, + size_t buffersize, + CURLcode *curlcode); + +static CURLcode sectransp_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + CURLcode result = CURLE_OK; + ssize_t nread; + char buf[1024]; + size_t i; + + DEBUGASSERT(backend); + if(!backend->ssl_ctx || cf->shutdown) { + *done = TRUE; + goto out; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + + if(send_shutdown && !backend->sent_shutdown) { + OSStatus err; + + CURL_TRC_CF(data, cf, "shutdown, send close notify"); + err = SSLClose(backend->ssl_ctx); + switch(err) { + case noErr: + backend->sent_shutdown = TRUE; + break; + case errSSLWouldBlock: + connssl->io_need = CURL_SSL_IO_NEED_SEND; + result = CURLE_OK; + goto out; + default: + CURL_TRC_CF(data, cf, "shutdown, error: %d", (int)err); + result = CURLE_SEND_ERROR; + goto out; + } + } + + for(i = 0; i < 10; ++i) { + if(!backend->sent_shutdown) { + nread = sectransp_recv(cf, data, buf, (int)sizeof(buf), &result); + } + else { + /* We would like to read the close notify from the server using + * secure transport, however SSLRead() no longer works after we + * sent the notify from our side. So, we just read from the + * underlying filter and hope it will end. */ + nread = Curl_conn_cf_recv(cf->next, data, buf, sizeof(buf), &result); + } + CURL_TRC_CF(data, cf, "shutdown read -> %zd, %d", nread, result); + if(nread <= 0) + break; + } + + if(nread > 0) { + /* still data coming in? */ + connssl->io_need = CURL_SSL_IO_NEED_RECV; + } + else if(nread == 0) { + /* We got the close notify alert and are done. */ + CURL_TRC_CF(data, cf, "shutdown done"); + *done = TRUE; + } + else if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + result = CURLE_OK; + } + else { + DEBUGASSERT(result); + CURL_TRC_CF(data, cf, "shutdown, error: %d", result); + } + +out: + cf->shutdown = (result || *done); + return result; +} + +static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + + (void) data; + + DEBUGASSERT(backend); + + if(backend->ssl_ctx) { + CURL_TRC_CF(data, cf, "close"); +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS + if(&SSLCreateContext) + CFRelease(backend->ssl_ctx); +#if CURL_SUPPORT_MAC_10_8 + else + (void)SSLDisposeContext(backend->ssl_ctx); +#endif /* CURL_SUPPORT_MAC_10_8 */ +#else + (void)SSLDisposeContext(backend->ssl_ctx); +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ + backend->ssl_ctx = NULL; + } +} + +static size_t sectransp_version(char *buffer, size_t size) +{ + return msnprintf(buffer, size, "SecureTransport"); +} + +static bool sectransp_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + const struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + OSStatus err; + size_t buffer; + + (void)data; + DEBUGASSERT(backend); + + if(backend->ssl_ctx) { /* SSL is in use */ + CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending"); + err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); + if(err == noErr) + return buffer > 0UL; + return false; + } + else + return false; +} + +static CURLcode sectransp_random(struct Curl_easy *data UNUSED_PARAM, + unsigned char *entropy, size_t length) +{ + /* arc4random_buf() is not available on cats older than Lion, so let's + do this manually for the benefit of the older cats. */ + size_t i; + u_int32_t random_number = 0; + + (void)data; + + for(i = 0 ; i < length ; i++) { + if(i % sizeof(u_int32_t) == 0) + random_number = arc4random(); + entropy[i] = random_number & 0xFF; + random_number >>= 8; + } + i = random_number = 0; + return CURLE_OK; +} + +static CURLcode sectransp_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum, /* output */ + size_t sha256len) +{ + (void)sha256len; + assert(sha256len >= CURL_SHA256_DIGEST_LENGTH); + (void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum); + return CURLE_OK; +} + +static bool sectransp_false_start(void) +{ +#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 + if(&SSLSetSessionOption) + return TRUE; +#endif + return FALSE; +} + +static ssize_t sectransp_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, + size_t len, + CURLcode *curlcode) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + size_t processed = 0UL; + OSStatus err; + + DEBUGASSERT(backend); + + /* The SSLWrite() function works a little differently than expected. The + fourth argument (processed) is currently documented in Apple's + documentation as: "On return, the length, in bytes, of the data actually + written." + + Now, one could interpret that as "written to the socket," but actually, + it returns the amount of data that was written to a buffer internal to + the SSLContextRef instead. So it is possible for SSLWrite() to return + errSSLWouldBlock and a number of bytes "written" because those bytes were + encrypted and written to a buffer, not to the socket. + + So if this happens, then we need to keep calling SSLWrite() over and + over again with no new data until it quits returning errSSLWouldBlock. */ + + /* Do we have buffered data to write from the last time we were called? */ + if(backend->ssl_write_buffered_length) { + /* Write the buffered data: */ + err = SSLWrite(backend->ssl_ctx, NULL, 0UL, &processed); + switch(err) { + case noErr: + /* processed is always going to be 0 because we did not write to + the buffer, so return how much was written to the socket */ + processed = backend->ssl_write_buffered_length; + backend->ssl_write_buffered_length = 0UL; + break; + case errSSLWouldBlock: /* argh, try again */ + *curlcode = CURLE_AGAIN; + return -1L; + default: + failf(data, "SSLWrite() returned error %d", err); + *curlcode = CURLE_SEND_ERROR; + return -1L; + } + } + else { + /* We have got new data to write: */ + err = SSLWrite(backend->ssl_ctx, mem, len, &processed); + if(err != noErr) { + switch(err) { + case errSSLWouldBlock: + /* Data was buffered but not sent, we have to tell the caller + to try sending again, and remember how much was buffered */ + backend->ssl_write_buffered_length = len; + *curlcode = CURLE_AGAIN; + return -1L; + default: + failf(data, "SSLWrite() returned error %d", err); + *curlcode = CURLE_SEND_ERROR; + return -1L; + } + } + } + return (ssize_t)processed; +} + +static ssize_t sectransp_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, + size_t buffersize, + CURLcode *curlcode) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + size_t processed = 0UL; + OSStatus err; + + DEBUGASSERT(backend); + +again: + *curlcode = CURLE_OK; + err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed); + + if(err != noErr) { + switch(err) { + case errSSLWouldBlock: /* return how much we read (if anything) */ + if(processed) { + return (ssize_t)processed; + } + *curlcode = CURLE_AGAIN; + return -1L; + + /* errSSLClosedGraceful - server gracefully shut down the SSL session + errSSLClosedNoNotify - server hung up on us instead of sending a + closure alert notice, read() is returning 0 + Either way, inform the caller that the server disconnected. */ + case errSSLClosedGraceful: + case errSSLClosedNoNotify: + *curlcode = CURLE_OK; + return 0; + + /* The below is errSSLPeerAuthCompleted; it is not defined in + Leopard's headers */ + case -9841: + if((conn_config->CAfile || conn_config->ca_info_blob) && + conn_config->verifypeer) { + CURLcode result = verify_cert(cf, data, conn_config->CAfile, + conn_config->ca_info_blob, + backend->ssl_ctx); + if(result) { + *curlcode = result; + return -1; + } + } + goto again; + default: + failf(data, "SSLRead() return error %d", err); + *curlcode = CURLE_RECV_ERROR; + return -1L; + } + } + return (ssize_t)processed; +} + +static void *sectransp_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) +{ + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + (void)info; + DEBUGASSERT(backend); + return backend->ssl_ctx; +} + +const struct Curl_ssl Curl_ssl_sectransp = { + { CURLSSLBACKEND_SECURETRANSPORT, "secure-transport" }, /* info */ + + SSLSUPP_CAINFO_BLOB | + SSLSUPP_CERTINFO | +#ifdef SECTRANSP_PINNEDPUBKEY + SSLSUPP_PINNEDPUBKEY | +#endif /* SECTRANSP_PINNEDPUBKEY */ + SSLSUPP_HTTPS_PROXY, + + sizeof(struct st_ssl_backend_data), + + Curl_none_init, /* init */ + Curl_none_cleanup, /* cleanup */ + sectransp_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + sectransp_shutdown, /* shutdown */ + sectransp_data_pending, /* data_pending */ + sectransp_random, /* random */ + Curl_none_cert_status_request, /* cert_status_request */ + sectransp_connect, /* connect */ + sectransp_connect_nonblocking, /* connect_nonblocking */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ + sectransp_get_internals, /* get_internals */ + sectransp_close, /* close_one */ + Curl_none_close_all, /* close_all */ + Curl_none_set_engine, /* set_engine */ + Curl_none_set_engine_default, /* set_engine_default */ + Curl_none_engines_list, /* engines_list */ + sectransp_false_start, /* false_start */ + sectransp_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ + NULL, /* disassociate_connection */ + sectransp_recv, /* recv decrypted data */ + sectransp_send, /* send data to encrypt */ +}; + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif /* USE_SECTRANSP */ diff --git a/third_party/curl/lib/vtls/sectransp.h b/third_party/curl/lib/vtls/sectransp.h new file mode 100644 index 0000000000..0f1085ad91 --- /dev/null +++ b/third_party/curl/lib/vtls/sectransp.h @@ -0,0 +1,34 @@ +#ifndef HEADER_CURL_SECTRANSP_H +#define HEADER_CURL_SECTRANSP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Nick Zitzmann, . + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_SECTRANSP + +extern const struct Curl_ssl Curl_ssl_sectransp; + +#endif /* USE_SECTRANSP */ +#endif /* HEADER_CURL_SECTRANSP_H */ diff --git a/third_party/curl/lib/vtls/vtls.c b/third_party/curl/lib/vtls/vtls.c new file mode 100644 index 0000000000..e601f4b16e --- /dev/null +++ b/third_party/curl/lib/vtls/vtls.c @@ -0,0 +1,2254 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* This file is for implementing all "generic" SSL functions that all libcurl + internals should use. It is then responsible for calling the proper + "backend" function. + + SSL-functions in libcurl should call functions in this source file, and not + to any specific SSL-layer. + + Curl_ssl_ - prefix for generic ones + + Note that this source code uses the functions of the configured SSL + backend via the global Curl_ssl instance. + + "SSL/TLS Strong Encryption: An Introduction" + https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html +*/ + +#include "curl_setup.h" + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#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" +#include "url.h" +#include "progress.h" +#include "share.h" +#include "multiif.h" +#include "timeval.h" +#include "curl_md5.h" +#include "warnless.h" +#include "curl_base64.h" +#include "curl_printf.h" +#include "inet_pton.h" +#include "connect.h" +#include "select.h" +#include "strdup.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + + +/* convenience macro to check if this handle is using a shared SSL session */ +#define SSLSESSION_SHARED(data) (data->share && \ + (data->share->specifier & \ + (1<var) { \ + dest->var = strdup(source->var); \ + if(!dest->var) \ + return FALSE; \ + } \ + else \ + dest->var = NULL; \ + } while(0) + +#define CLONE_BLOB(var) \ + do { \ + if(blobdup(&dest->var, source->var)) \ + return FALSE; \ + } while(0) + +static CURLcode blobdup(struct curl_blob **dest, + struct curl_blob *src) +{ + DEBUGASSERT(dest); + DEBUGASSERT(!*dest); + if(src) { + /* only if there is data to dupe! */ + struct curl_blob *d; + d = malloc(sizeof(struct curl_blob) + src->len); + if(!d) + return CURLE_OUT_OF_MEMORY; + d->len = src->len; + /* Always duplicate because the connection may survive longer than the + handle that passed in the blob. */ + d->flags = CURL_BLOB_COPY; + d->data = (void *)((char *)d + sizeof(struct curl_blob)); + memcpy(d->data, src->data, src->len); + *dest = d; + } + return CURLE_OK; +} + +/* returns TRUE if the blobs are identical */ +static bool blobcmp(struct curl_blob *first, struct curl_blob *second) +{ + if(!first && !second) /* both are NULL */ + return TRUE; + if(!first || !second) /* one is NULL */ + return FALSE; + if(first->len != second->len) /* different sizes */ + return FALSE; + return !memcmp(first->data, second->data, first->len); /* same data */ +} + +#ifdef USE_SSL +static const struct alpn_spec ALPN_SPEC_H11 = { + { ALPN_HTTP_1_1 }, 1 +}; +#ifdef USE_HTTP2 +static const struct alpn_spec ALPN_SPEC_H2_H11 = { + { ALPN_H2, ALPN_HTTP_1_1 }, 2 +}; +#endif + +static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn) +{ + if(!use_alpn) + return NULL; +#ifdef USE_HTTP2 + if(httpwant >= CURL_HTTP_VERSION_2) + return &ALPN_SPEC_H2_H11; +#else + (void)httpwant; +#endif + /* Use the ALPN protocol "http/1.1" for HTTP/1.x. + Avoid "http/1.0" because some servers do not support it. */ + return &ALPN_SPEC_H11; +} +#endif /* USE_SSL */ + + +void Curl_ssl_easy_config_init(struct Curl_easy *data) +{ + /* + * libcurl 7.10 introduced SSL verification *by default*! This needs to be + * switched off unless wanted. + */ + data->set.ssl.primary.verifypeer = TRUE; + data->set.ssl.primary.verifyhost = TRUE; + data->set.ssl.primary.cache_session = TRUE; /* caching by default */ +#ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl = data->set.ssl; +#endif +} + +static bool +match_ssl_primary_config(struct Curl_easy *data, + struct ssl_primary_config *c1, + struct ssl_primary_config *c2) +{ + (void)data; + if((c1->version == c2->version) && + (c1->version_max == c2->version_max) && + (c1->ssl_options == c2->ssl_options) && + (c1->verifypeer == c2->verifypeer) && + (c1->verifyhost == c2->verifyhost) && + (c1->verifystatus == c2->verifystatus) && + blobcmp(c1->cert_blob, c2->cert_blob) && + blobcmp(c1->ca_info_blob, c2->ca_info_blob) && + blobcmp(c1->issuercert_blob, c2->issuercert_blob) && + Curl_safecmp(c1->CApath, c2->CApath) && + Curl_safecmp(c1->CAfile, c2->CAfile) && + Curl_safecmp(c1->issuercert, c2->issuercert) && + Curl_safecmp(c1->clientcert, c2->clientcert) && +#ifdef USE_TLS_SRP + !Curl_timestrcmp(c1->username, c2->username) && + !Curl_timestrcmp(c1->password, c2->password) && +#endif + strcasecompare(c1->cipher_list, c2->cipher_list) && + strcasecompare(c1->cipher_list13, c2->cipher_list13) && + strcasecompare(c1->curves, c2->curves) && + strcasecompare(c1->CRLfile, c2->CRLfile) && + strcasecompare(c1->pinned_key, c2->pinned_key)) + return TRUE; + + return FALSE; +} + +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy) +{ +#ifndef CURL_DISABLE_PROXY + if(proxy) + return match_ssl_primary_config(data, &data->set.proxy_ssl.primary, + &candidate->proxy_ssl_config); +#else + (void)proxy; +#endif + return match_ssl_primary_config(data, &data->set.ssl.primary, + &candidate->ssl_config); +} + +static bool clone_ssl_primary_config(struct ssl_primary_config *source, + struct ssl_primary_config *dest) +{ + dest->version = source->version; + dest->version_max = source->version_max; + dest->verifypeer = source->verifypeer; + dest->verifyhost = source->verifyhost; + dest->verifystatus = source->verifystatus; + dest->cache_session = source->cache_session; + dest->ssl_options = source->ssl_options; + + CLONE_BLOB(cert_blob); + CLONE_BLOB(ca_info_blob); + CLONE_BLOB(issuercert_blob); + CLONE_STRING(CApath); + CLONE_STRING(CAfile); + CLONE_STRING(issuercert); + CLONE_STRING(clientcert); + CLONE_STRING(cipher_list); + CLONE_STRING(cipher_list13); + CLONE_STRING(pinned_key); + CLONE_STRING(curves); + CLONE_STRING(CRLfile); +#ifdef USE_TLS_SRP + CLONE_STRING(username); + CLONE_STRING(password); +#endif + + return TRUE; +} + +static void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) +{ + Curl_safefree(sslc->CApath); + Curl_safefree(sslc->CAfile); + Curl_safefree(sslc->issuercert); + Curl_safefree(sslc->clientcert); + Curl_safefree(sslc->cipher_list); + Curl_safefree(sslc->cipher_list13); + Curl_safefree(sslc->pinned_key); + Curl_safefree(sslc->cert_blob); + Curl_safefree(sslc->ca_info_blob); + Curl_safefree(sslc->issuercert_blob); + Curl_safefree(sslc->curves); + Curl_safefree(sslc->CRLfile); +#ifdef USE_TLS_SRP + Curl_safefree(sslc->username); + Curl_safefree(sslc->password); +#endif +} + +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) +{ + data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH]; + data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE]; + data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; + data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; + data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; + data->set.ssl.primary.cipher_list = + data->set.str[STRING_SSL_CIPHER_LIST]; + data->set.ssl.primary.cipher_list13 = + data->set.str[STRING_SSL_CIPHER13_LIST]; + data->set.ssl.primary.pinned_key = + data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; + data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; + data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; +#ifdef USE_TLS_SRP + data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; + data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; +#endif + data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE]; + data->set.ssl.key = data->set.str[STRING_KEY]; + data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE]; + data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD]; + data->set.ssl.primary.clientcert = data->set.str[STRING_CERT]; + data->set.ssl.key_blob = data->set.blobs[BLOB_KEY]; + +#ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; + data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; + data->set.proxy_ssl.primary.cipher_list = + data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; + data->set.proxy_ssl.primary.cipher_list13 = + data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; + data->set.proxy_ssl.primary.pinned_key = + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; + data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; + data->set.proxy_ssl.primary.ca_info_blob = + data->set.blobs[BLOB_CAINFO_PROXY]; + data->set.proxy_ssl.primary.issuercert = + data->set.str[STRING_SSL_ISSUERCERT_PROXY]; + data->set.proxy_ssl.primary.issuercert_blob = + data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; + data->set.proxy_ssl.primary.CRLfile = + data->set.str[STRING_SSL_CRLFILE_PROXY]; + data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; + data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY]; + data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; + data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; + data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY]; + data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY]; +#ifdef USE_TLS_SRP + data->set.proxy_ssl.primary.username = + data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; + data->set.proxy_ssl.primary.password = + data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; +#endif +#endif /* CURL_DISABLE_PROXY */ + + return CURLE_OK; +} + +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Clone "primary" SSL configurations from the esay handle to + * the connection. They are used for connection cache matching and + * probably outlive the easy handle */ + if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) + return CURLE_OUT_OF_MEMORY; +#ifndef CURL_DISABLE_PROXY + if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary, + &conn->proxy_ssl_config)) + return CURLE_OUT_OF_MEMORY; +#endif + return CURLE_OK; +} + +void Curl_ssl_conn_config_cleanup(struct connectdata *conn) +{ + Curl_free_primary_ssl_config(&conn->ssl_config); +#ifndef CURL_DISABLE_PROXY + Curl_free_primary_ssl_config(&conn->proxy_ssl_config); +#endif +} + +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy) +{ + /* May be called on an easy that has no connection yet */ + if(data->conn) { + struct ssl_primary_config *src, *dest; +#ifndef CURL_DISABLE_PROXY + src = for_proxy? &data->set.proxy_ssl.primary : &data->set.ssl.primary; + dest = for_proxy? &data->conn->proxy_ssl_config : &data->conn->ssl_config; +#else + (void)for_proxy; + src = &data->set.ssl.primary; + dest = &data->conn->ssl_config; +#endif + dest->verifyhost = src->verifyhost; + dest->verifypeer = src->verifypeer; + dest->verifystatus = src->verifystatus; + } +} + +#ifdef USE_SSL +static int multissl_setup(const struct Curl_ssl *backend); +#endif + +curl_sslbackend Curl_ssl_backend(void) +{ +#ifdef USE_SSL + multissl_setup(NULL); + return Curl_ssl->info.id; +#else + return CURLSSLBACKEND_NONE; +#endif +} + +#ifdef USE_SSL + +/* "global" init done? */ +static bool init_ssl = FALSE; + +/** + * Global SSL init + * + * @retval 0 error initializing SSL + * @retval 1 SSL initialized successfully + */ +int Curl_ssl_init(void) +{ + /* make sure this is only done once */ + if(init_ssl) + return 1; + init_ssl = TRUE; /* never again */ + + return Curl_ssl->init(); +} + +static bool ssl_prefs_check(struct Curl_easy *data) +{ + /* check for CURLOPT_SSLVERSION invalid parameter value */ + const unsigned char sslver = data->set.ssl.primary.version; + if(sslver >= CURL_SSLVERSION_LAST) { + failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION"); + return FALSE; + } + + switch(data->set.ssl.primary.version_max) { + case CURL_SSLVERSION_MAX_NONE: + case CURL_SSLVERSION_MAX_DEFAULT: + break; + + default: + if((data->set.ssl.primary.version_max >> 16) < sslver) { + failf(data, "CURL_SSLVERSION_MAX incompatible with CURL_SSLVERSION"); + return FALSE; + } + } + + return TRUE; +} + +static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, + const struct alpn_spec *alpn) +{ + struct ssl_connect_data *ctx; + + (void)data; + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) + return NULL; + + ctx->alpn = alpn; + ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data); + if(!ctx->backend) { + free(ctx); + return NULL; + } + return ctx; +} + +static void cf_ctx_free(struct ssl_connect_data *ctx) +{ + if(ctx) { + free(ctx->backend); + free(ctx); + } +} + +static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + CURLcode result; + + if(!ssl_prefs_check(data)) + return CURLE_SSL_CONNECT_ERROR; + + /* mark this is being ssl-enabled from here on. */ + connssl->state = ssl_connection_negotiating; + + result = Curl_ssl->connect_blocking(cf, data); + + if(!result) { + DEBUGASSERT(connssl->state == ssl_connection_complete); + } + + return result; +} + +static CURLcode +ssl_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data, + bool *done) +{ + if(!ssl_prefs_check(data)) + return CURLE_SSL_CONNECT_ERROR; + + /* mark this is being ssl requested from here on. */ + return Curl_ssl->connect_nonblocking(cf, data, done); +} + +/* + * Lock shared SSL session data + */ +void Curl_ssl_sessionid_lock(struct Curl_easy *data) +{ + if(SSLSESSION_SHARED(data)) + Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE); +} + +/* + * Unlock shared SSL session data + */ +void Curl_ssl_sessionid_unlock(struct Curl_easy *data) +{ + if(SSLSESSION_SHARED(data)) + Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); +} + +/* + * Check if there is a session ID for the given connection in the cache, and if + * there is one suitable, it is provided. Returns TRUE when no entry matched. + */ +bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + void **ssl_sessionid, + size_t *idsize) /* set 0 if unknown */ +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct Curl_ssl_session *check; + size_t i; + long *general_age; + bool no_match = TRUE; + + *ssl_sessionid = NULL; + if(!ssl_config) + return TRUE; + + DEBUGASSERT(ssl_config->primary.cache_session); + + if(!ssl_config->primary.cache_session || !data->state.session) + /* session ID reuse is disabled or the session cache has not been + setup */ + return TRUE; + + /* Lock if shared */ + if(SSLSESSION_SHARED(data)) + general_age = &data->share->sessionage; + else + general_age = &data->state.sessionage; + + for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) { + check = &data->state.session[i]; + if(!check->sessionid) + /* not session ID means blank entry */ + continue; + if(strcasecompare(peer->hostname, check->name) && + ((!cf->conn->bits.conn_to_host && !check->conn_to_host) || + (cf->conn->bits.conn_to_host && check->conn_to_host && + strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) && + ((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) || + (cf->conn->bits.conn_to_port && check->conn_to_port != -1 && + cf->conn->conn_to_port == check->conn_to_port)) && + (peer->port == check->remote_port) && + (peer->transport == check->transport) && + strcasecompare(cf->conn->handler->scheme, check->scheme) && + match_ssl_primary_config(data, conn_config, &check->ssl_config)) { + /* yes, we have a session ID! */ + (*general_age)++; /* increase general age */ + check->age = *general_age; /* set this as used in this age */ + *ssl_sessionid = check->sessionid; + if(idsize) + *idsize = check->idsize; + no_match = FALSE; + break; + } + } + + DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d", + no_match? "Did not find": "Found", + Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host", + cf->conn->handler->scheme, peer->hostname, peer->port)); + return no_match; +} + +/* + * Kill a single session ID entry in the cache. + */ +void Curl_ssl_kill_session(struct Curl_ssl_session *session) +{ + if(session->sessionid) { + /* defensive check */ + + /* free the ID the SSL-layer specific way */ + session->sessionid_free(session->sessionid, session->idsize); + + session->sessionid = NULL; + session->sessionid_free = NULL; + session->age = 0; /* fresh */ + + Curl_free_primary_ssl_config(&session->ssl_config); + + Curl_safefree(session->name); + Curl_safefree(session->conn_to_host); + } +} + +/* + * Delete the given session ID from the cache. + */ +void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid) +{ + size_t i; + + for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) { + struct Curl_ssl_session *check = &data->state.session[i]; + + if(check->sessionid == ssl_sessionid) { + Curl_ssl_kill_session(check); + break; + } + } +} + +CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + void *ssl_sessionid, + size_t idsize, + Curl_ssl_sessionid_dtor *sessionid_free_cb) +{ + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + size_t i; + struct Curl_ssl_session *store; + long oldest_age; + char *clone_host = NULL; + char *clone_conn_to_host = NULL; + int conn_to_port; + long *general_age; + void *old_sessionid; + size_t old_size; + CURLcode result = CURLE_OUT_OF_MEMORY; + + DEBUGASSERT(ssl_sessionid); + DEBUGASSERT(sessionid_free_cb); + + if(!data->state.session) { + sessionid_free_cb(ssl_sessionid, idsize); + return CURLE_OK; + } + + if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size)) { + if((old_size == idsize) && + ((old_sessionid == ssl_sessionid) || + (idsize && !memcmp(old_sessionid, ssl_sessionid, idsize)))) { + /* the very same */ + sessionid_free_cb(ssl_sessionid, idsize); + return CURLE_OK; + } + Curl_ssl_delsessionid(data, old_sessionid); + } + + store = &data->state.session[0]; + oldest_age = data->state.session[0].age; /* zero if unused */ + DEBUGASSERT(ssl_config->primary.cache_session); + (void)ssl_config; + + clone_host = strdup(peer->hostname); + if(!clone_host) + goto out; + + if(cf->conn->bits.conn_to_host) { + clone_conn_to_host = strdup(cf->conn->conn_to_host.name); + if(!clone_conn_to_host) + goto out; + } + + if(cf->conn->bits.conn_to_port) + conn_to_port = cf->conn->conn_to_port; + else + conn_to_port = -1; + + /* Now we should add the session ID and the hostname to the cache, (remove + the oldest if necessary) */ + + /* If using shared SSL session, lock! */ + if(SSLSESSION_SHARED(data)) { + general_age = &data->share->sessionage; + } + else { + general_age = &data->state.sessionage; + } + + /* find an empty slot for us, or find the oldest */ + for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) && + data->state.session[i].sessionid; i++) { + if(data->state.session[i].age < oldest_age) { + oldest_age = data->state.session[i].age; + store = &data->state.session[i]; + } + } + if(i == data->set.general_ssl.max_ssl_sessions) + /* cache is full, we must "kill" the oldest entry! */ + Curl_ssl_kill_session(store); + else + store = &data->state.session[i]; /* use this slot */ + + /* now init the session struct wisely */ + if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) { + Curl_free_primary_ssl_config(&store->ssl_config); + store->sessionid = NULL; /* let caller free sessionid */ + goto out; + } + store->sessionid = ssl_sessionid; + store->idsize = idsize; + store->sessionid_free = sessionid_free_cb; + store->age = *general_age; /* set current age */ + /* free it if there is one already present */ + free(store->name); + free(store->conn_to_host); + store->name = clone_host; /* clone hostname */ + clone_host = NULL; + store->conn_to_host = clone_conn_to_host; /* clone connect to hostname */ + clone_conn_to_host = NULL; + store->conn_to_port = conn_to_port; /* connect to port number */ + /* port number */ + store->remote_port = peer->port; + store->scheme = cf->conn->handler->scheme; + store->transport = peer->transport; + + result = CURLE_OK; + +out: + free(clone_host); + free(clone_conn_to_host); + if(result) { + failf(data, "Failed to add Session ID to cache for %s://%s:%d [%s]", + store->scheme, store->name, store->remote_port, + Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server"); + sessionid_free_cb(ssl_sessionid, idsize); + return result; + } + CURL_TRC_CF(data, cf, "Added Session ID to cache for %s://%s:%d [%s]", + store->scheme, store->name, store->remote_port, + Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server"); + return CURLE_OK; +} + +void Curl_ssl_close_all(struct Curl_easy *data) +{ + /* kill the session ID cache if not shared */ + if(data->state.session && !SSLSESSION_SHARED(data)) { + size_t i; + for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) + /* the single-killer function handles empty table slots */ + Curl_ssl_kill_session(&data->state.session[i]); + + /* free the cache data */ + Curl_safefree(data->state.session); + } + + Curl_ssl->close_all(data); +} + +void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct ssl_connect_data *connssl = cf->ctx; + + if(connssl->io_need) { + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + if(sock != CURL_SOCKET_BAD) { + if(connssl->io_need & CURL_SSL_IO_NEED_SEND) { + Curl_pollset_set_out_only(data, ps, sock); + CURL_TRC_CF(data, cf, "adjust_pollset, POLLOUT fd=%" + CURL_FORMAT_SOCKET_T, sock); + } + else { + Curl_pollset_set_in_only(data, ps, sock); + CURL_TRC_CF(data, cf, "adjust_pollset, POLLIN fd=%" + CURL_FORMAT_SOCKET_T, sock); + } + } + } +} + +/* Selects an SSL crypto engine + */ +CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine) +{ + return Curl_ssl->set_engine(data, engine); +} + +/* Selects the default SSL crypto engine + */ +CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data) +{ + return Curl_ssl->set_engine_default(data); +} + +/* Return list of OpenSSL crypto engine names. */ +struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data) +{ + return Curl_ssl->engines_list(data); +} + +/* + * This sets up a session ID cache to the specified size. Make sure this code + * is agnostic to what underlying SSL technology we use. + */ +CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount) +{ + struct Curl_ssl_session *session; + + if(data->state.session) + /* this is just a precaution to prevent multiple inits */ + return CURLE_OK; + + session = calloc(amount, sizeof(struct Curl_ssl_session)); + if(!session) + return CURLE_OUT_OF_MEMORY; + + /* store the info in the SSL section */ + data->set.general_ssl.max_ssl_sessions = amount; + data->state.session = session; + data->state.sessionage = 1; /* this is brand new */ + return CURLE_OK; +} + +static size_t multissl_version(char *buffer, size_t size); + +void Curl_ssl_version(char *buffer, size_t size) +{ +#ifdef CURL_WITH_MULTI_SSL + (void)multissl_version(buffer, size); +#else + (void)Curl_ssl->version(buffer, size); +#endif +} + +void Curl_ssl_free_certinfo(struct Curl_easy *data) +{ + struct curl_certinfo *ci = &data->info.certs; + + if(ci->num_of_certs) { + /* free all individual lists used */ + int i; + for(i = 0; inum_of_certs; i++) { + curl_slist_free_all(ci->certinfo[i]); + ci->certinfo[i] = NULL; + } + + free(ci->certinfo); /* free the actual array too */ + ci->certinfo = NULL; + ci->num_of_certs = 0; + } +} + +CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num) +{ + struct curl_certinfo *ci = &data->info.certs; + struct curl_slist **table; + + /* Free any previous certificate information structures */ + Curl_ssl_free_certinfo(data); + + /* Allocate the required certificate information structures */ + table = calloc((size_t) num, sizeof(struct curl_slist *)); + if(!table) + return CURLE_OUT_OF_MEMORY; + + ci->num_of_certs = num; + ci->certinfo = table; + + return CURLE_OK; +} + +/* + * 'value' is NOT a null-terminated string + */ +CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, + int certnum, + const char *label, + const char *value, + size_t valuelen) +{ + struct curl_certinfo *ci = &data->info.certs; + struct curl_slist *nl; + CURLcode result = CURLE_OK; + struct dynbuf build; + + Curl_dyn_init(&build, 10000); + + if(Curl_dyn_add(&build, label) || + Curl_dyn_addn(&build, ":", 1) || + Curl_dyn_addn(&build, value, valuelen)) + return CURLE_OUT_OF_MEMORY; + + nl = Curl_slist_append_nodup(ci->certinfo[certnum], + Curl_dyn_ptr(&build)); + if(!nl) { + Curl_dyn_free(&build); + curl_slist_free_all(ci->certinfo[certnum]); + result = CURLE_OUT_OF_MEMORY; + } + + ci->certinfo[certnum] = nl; + return result; +} + +CURLcode Curl_ssl_random(struct Curl_easy *data, + unsigned char *entropy, + size_t length) +{ + return Curl_ssl->random(data, entropy, length); +} + +/* + * Public key pem to der conversion + */ + +static CURLcode pubkey_pem_to_der(const char *pem, + unsigned char **der, size_t *der_len) +{ + char *stripped_pem, *begin_pos, *end_pos; + size_t pem_count, stripped_pem_count = 0, pem_len; + CURLcode result; + + /* if no pem, exit. */ + if(!pem) + return CURLE_BAD_CONTENT_ENCODING; + + begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----"); + if(!begin_pos) + return CURLE_BAD_CONTENT_ENCODING; + + pem_count = begin_pos - pem; + /* Invalid if not at beginning AND not directly following \n */ + if(0 != pem_count && '\n' != pem[pem_count - 1]) + return CURLE_BAD_CONTENT_ENCODING; + + /* 26 is length of "-----BEGIN PUBLIC KEY-----" */ + pem_count += 26; + + /* Invalid if not directly following \n */ + end_pos = strstr(pem + pem_count, "\n-----END PUBLIC KEY-----"); + if(!end_pos) + return CURLE_BAD_CONTENT_ENCODING; + + pem_len = end_pos - pem; + + stripped_pem = malloc(pem_len - pem_count + 1); + if(!stripped_pem) + return CURLE_OUT_OF_MEMORY; + + /* + * Here we loop through the pem array one character at a time between the + * correct indices, and place each character that is not '\n' or '\r' + * into the stripped_pem array, which should represent the raw base64 string + */ + while(pem_count < pem_len) { + if('\n' != pem[pem_count] && '\r' != pem[pem_count]) + stripped_pem[stripped_pem_count++] = pem[pem_count]; + ++pem_count; + } + /* Place the null terminator in the correct place */ + stripped_pem[stripped_pem_count] = '\0'; + + result = Curl_base64_decode(stripped_pem, der, der_len); + + Curl_safefree(stripped_pem); + + return result; +} + +/* + * Generic pinned public key check. + */ + +CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, + const char *pinnedpubkey, + const unsigned char *pubkey, size_t pubkeylen) +{ + FILE *fp; + unsigned char *buf = NULL, *pem_ptr = NULL; + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)data; +#endif + + /* if a path was not specified, do not pin */ + if(!pinnedpubkey) + return CURLE_OK; + if(!pubkey || !pubkeylen) + return result; + + /* only do this if pinnedpubkey starts with "sha256//", length 8 */ + if(strncmp(pinnedpubkey, "sha256//", 8) == 0) { + CURLcode encode; + size_t encodedlen = 0; + char *encoded = NULL, *pinkeycopy, *begin_pos, *end_pos; + unsigned char *sha256sumdigest; + + if(!Curl_ssl->sha256sum) { + /* without sha256 support, this cannot match */ + return result; + } + + /* compute sha256sum of public key */ + sha256sumdigest = malloc(CURL_SHA256_DIGEST_LENGTH); + if(!sha256sumdigest) + return CURLE_OUT_OF_MEMORY; + encode = Curl_ssl->sha256sum(pubkey, pubkeylen, + sha256sumdigest, CURL_SHA256_DIGEST_LENGTH); + + if(!encode) + encode = Curl_base64_encode((char *)sha256sumdigest, + CURL_SHA256_DIGEST_LENGTH, &encoded, + &encodedlen); + Curl_safefree(sha256sumdigest); + + if(encode) + return encode; + + infof(data, " public key hash: sha256//%s", encoded); + + /* it starts with sha256//, copy so we can modify it */ + pinkeycopy = strdup(pinnedpubkey); + if(!pinkeycopy) { + Curl_safefree(encoded); + return CURLE_OUT_OF_MEMORY; + } + /* point begin_pos to the copy, and start extracting keys */ + begin_pos = pinkeycopy; + do { + end_pos = strstr(begin_pos, ";sha256//"); + /* + * if there is an end_pos, null terminate, + * otherwise it will go to the end of the original string + */ + if(end_pos) + end_pos[0] = '\0'; + + /* compare base64 sha256 digests, 8 is the length of "sha256//" */ + if(encodedlen == strlen(begin_pos + 8) && + !memcmp(encoded, begin_pos + 8, encodedlen)) { + result = CURLE_OK; + break; + } + + /* + * change back the null-terminator we changed earlier, + * and look for next begin + */ + if(end_pos) { + end_pos[0] = ';'; + begin_pos = strstr(end_pos, "sha256//"); + } + } while(end_pos && begin_pos); + Curl_safefree(encoded); + Curl_safefree(pinkeycopy); + return result; + } + + fp = fopen(pinnedpubkey, "rb"); + if(!fp) + return result; + + do { + long filesize; + size_t size, pem_len; + CURLcode pem_read; + + /* Determine the file's size */ + if(fseek(fp, 0, SEEK_END)) + break; + filesize = ftell(fp); + if(fseek(fp, 0, SEEK_SET)) + break; + if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE) + break; + + /* + * if the size of our certificate is bigger than the file + * size then it cannot match + */ + size = curlx_sotouz((curl_off_t) filesize); + if(pubkeylen > size) + break; + + /* + * Allocate buffer for the pinned key + * With 1 additional byte for null terminator in case of PEM key + */ + buf = malloc(size + 1); + if(!buf) + break; + + /* Returns number of elements read, which should be 1 */ + if((int) fread(buf, size, 1, fp) != 1) + break; + + /* If the sizes are the same, it cannot be base64 encoded, must be der */ + if(pubkeylen == size) { + if(!memcmp(pubkey, buf, pubkeylen)) + result = CURLE_OK; + break; + } + + /* + * Otherwise we will assume it is PEM and try to decode it + * after placing null terminator + */ + buf[size] = '\0'; + pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len); + /* if it was not read successfully, exit */ + if(pem_read) + break; + + /* + * if the size of our certificate does not match the size of + * the decoded file, they cannot be the same, otherwise compare + */ + if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen)) + result = CURLE_OK; + } while(0); + + Curl_safefree(buf); + Curl_safefree(pem_ptr); + fclose(fp); + + return result; +} + +/* + * Check whether the SSL backend supports the status_request extension. + */ +bool Curl_ssl_cert_status_request(void) +{ + return Curl_ssl->cert_status_request(); +} + +/* + * Check whether the SSL backend supports false start. + */ +bool Curl_ssl_false_start(struct Curl_easy *data) +{ + (void)data; + return Curl_ssl->false_start(); +} + +/* + * Default implementations for unsupported functions. + */ + +int Curl_none_init(void) +{ + return 1; +} + +void Curl_none_cleanup(void) +{ } + +CURLcode Curl_none_shutdown(struct Curl_cfilter *cf UNUSED_PARAM, + struct Curl_easy *data UNUSED_PARAM, + bool send_shutdown UNUSED_PARAM, + bool *done) +{ + (void)data; + (void)cf; + (void)send_shutdown; + /* Every SSL backend should have a shutdown implementation. Until we + * have implemented that, we put this fake in place. */ + *done = TRUE; + return CURLE_OK; +} + +int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + (void)cf; + (void)data; + return -1; +} + +CURLcode Curl_none_random(struct Curl_easy *data UNUSED_PARAM, + unsigned char *entropy UNUSED_PARAM, + size_t length UNUSED_PARAM) +{ + (void)data; + (void)entropy; + (void)length; + return CURLE_NOT_BUILT_IN; +} + +void Curl_none_close_all(struct Curl_easy *data UNUSED_PARAM) +{ + (void)data; +} + +void Curl_none_session_free(void *ptr UNUSED_PARAM) +{ + (void)ptr; +} + +bool Curl_none_data_pending(struct Curl_cfilter *cf UNUSED_PARAM, + const struct Curl_easy *data UNUSED_PARAM) +{ + (void)cf; + (void)data; + return 0; +} + +bool Curl_none_cert_status_request(void) +{ + return FALSE; +} + +CURLcode Curl_none_set_engine(struct Curl_easy *data UNUSED_PARAM, + const char *engine UNUSED_PARAM) +{ + (void)data; + (void)engine; + return CURLE_NOT_BUILT_IN; +} + +CURLcode Curl_none_set_engine_default(struct Curl_easy *data UNUSED_PARAM) +{ + (void)data; + return CURLE_NOT_BUILT_IN; +} + +struct curl_slist *Curl_none_engines_list(struct Curl_easy *data UNUSED_PARAM) +{ + (void)data; + return (struct curl_slist *)NULL; +} + +bool Curl_none_false_start(void) +{ + return FALSE; +} + +static int multissl_init(void) +{ + if(multissl_setup(NULL)) + return 1; + return Curl_ssl->init(); +} + +static CURLcode multissl_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + if(multissl_setup(NULL)) + return CURLE_FAILED_INIT; + return Curl_ssl->connect_blocking(cf, data); +} + +static CURLcode multissl_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + if(multissl_setup(NULL)) + return CURLE_FAILED_INIT; + return Curl_ssl->connect_nonblocking(cf, data, done); +} + +static void multissl_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + if(multissl_setup(NULL)) + return; + Curl_ssl->adjust_pollset(cf, data, ps); +} + +static void *multissl_get_internals(struct ssl_connect_data *connssl, + CURLINFO info) +{ + if(multissl_setup(NULL)) + return NULL; + return Curl_ssl->get_internals(connssl, info); +} + +static void multissl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + if(multissl_setup(NULL)) + return; + Curl_ssl->close(cf, data); +} + +static ssize_t multissl_recv_plain(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, CURLcode *code) +{ + if(multissl_setup(NULL)) + return CURLE_FAILED_INIT; + return Curl_ssl->recv_plain(cf, data, buf, len, code); +} + +static ssize_t multissl_send_plain(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, size_t len, + CURLcode *code) +{ + if(multissl_setup(NULL)) + return CURLE_FAILED_INIT; + return Curl_ssl->send_plain(cf, data, mem, len, code); +} + +static const struct Curl_ssl Curl_ssl_multi = { + { CURLSSLBACKEND_NONE, "multi" }, /* info */ + 0, /* supports nothing */ + (size_t)-1, /* something insanely large to be on the safe side */ + + multissl_init, /* init */ + Curl_none_cleanup, /* cleanup */ + multissl_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + Curl_none_shutdown, /* shutdown */ + Curl_none_data_pending, /* data_pending */ + Curl_none_random, /* random */ + Curl_none_cert_status_request, /* cert_status_request */ + multissl_connect, /* connect */ + multissl_connect_nonblocking, /* connect_nonblocking */ + multissl_adjust_pollset, /* adjust_pollset */ + multissl_get_internals, /* get_internals */ + multissl_close, /* close_one */ + Curl_none_close_all, /* close_all */ + Curl_none_set_engine, /* set_engine */ + Curl_none_set_engine_default, /* set_engine_default */ + Curl_none_engines_list, /* engines_list */ + Curl_none_false_start, /* false_start */ + NULL, /* sha256sum */ + NULL, /* associate_connection */ + NULL, /* disassociate_connection */ + multissl_recv_plain, /* recv decrypted data */ + multissl_send_plain, /* send data to encrypt */ +}; + +const struct Curl_ssl *Curl_ssl = +#if defined(CURL_WITH_MULTI_SSL) + &Curl_ssl_multi; +#elif defined(USE_WOLFSSL) + &Curl_ssl_wolfssl; +#elif defined(USE_GNUTLS) + &Curl_ssl_gnutls; +#elif defined(USE_MBEDTLS) + &Curl_ssl_mbedtls; +#elif defined(USE_RUSTLS) + &Curl_ssl_rustls; +#elif defined(USE_OPENSSL) + &Curl_ssl_openssl; +#elif defined(USE_SECTRANSP) + &Curl_ssl_sectransp; +#elif defined(USE_SCHANNEL) + &Curl_ssl_schannel; +#elif defined(USE_BEARSSL) + &Curl_ssl_bearssl; +#else +#error "Missing struct Curl_ssl for selected SSL backend" +#endif + +static const struct Curl_ssl *available_backends[] = { +#if defined(USE_WOLFSSL) + &Curl_ssl_wolfssl, +#endif +#if defined(USE_GNUTLS) + &Curl_ssl_gnutls, +#endif +#if defined(USE_MBEDTLS) + &Curl_ssl_mbedtls, +#endif +#if defined(USE_OPENSSL) + &Curl_ssl_openssl, +#endif +#if defined(USE_SECTRANSP) + &Curl_ssl_sectransp, +#endif +#if defined(USE_SCHANNEL) + &Curl_ssl_schannel, +#endif +#if defined(USE_BEARSSL) + &Curl_ssl_bearssl, +#endif +#if defined(USE_RUSTLS) + &Curl_ssl_rustls, +#endif + NULL +}; + +/* Global cleanup */ +void Curl_ssl_cleanup(void) +{ + if(init_ssl) { + /* only cleanup if we did a previous init */ + Curl_ssl->cleanup(); +#if defined(CURL_WITH_MULTI_SSL) + Curl_ssl = &Curl_ssl_multi; +#endif + init_ssl = FALSE; + } +} + +static size_t multissl_version(char *buffer, size_t size) +{ + static const struct Curl_ssl *selected; + static char backends[200]; + static size_t backends_len; + const struct Curl_ssl *current; + + current = Curl_ssl == &Curl_ssl_multi ? available_backends[0] : Curl_ssl; + + if(current != selected) { + char *p = backends; + char *end = backends + sizeof(backends); + int i; + + selected = current; + + backends[0] = '\0'; + + for(i = 0; available_backends[i]; ++i) { + char vb[200]; + bool paren = (selected != available_backends[i]); + + if(available_backends[i]->version(vb, sizeof(vb))) { + p += msnprintf(p, end - p, "%s%s%s%s", (p != backends ? " " : ""), + (paren ? "(" : ""), vb, (paren ? ")" : "")); + } + } + + backends_len = p - backends; + } + + if(size) { + if(backends_len < size) + strcpy(buffer, backends); + else + *buffer = 0; /* did not fit */ + } + return 0; +} + +static int multissl_setup(const struct Curl_ssl *backend) +{ + const char *env; + char *env_tmp; + + if(Curl_ssl != &Curl_ssl_multi) + return 1; + + if(backend) { + Curl_ssl = backend; + return 0; + } + + if(!available_backends[0]) + return 1; + + env = env_tmp = curl_getenv("CURL_SSL_BACKEND"); +#ifdef CURL_DEFAULT_SSL_BACKEND + if(!env) + env = CURL_DEFAULT_SSL_BACKEND; +#endif + if(env) { + int i; + for(i = 0; available_backends[i]; i++) { + if(strcasecompare(env, available_backends[i]->info.name)) { + Curl_ssl = available_backends[i]; + free(env_tmp); + return 0; + } + } + } + + /* Fall back to first available backend */ + Curl_ssl = available_backends[0]; + free(env_tmp); + return 0; +} + +/* This function is used to select the SSL backend to use. It is called by + curl_global_sslset (easy.c) which uses the global init lock. */ +CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, + const curl_ssl_backend ***avail) +{ + int i; + + if(avail) + *avail = (const curl_ssl_backend **)&available_backends; + + if(Curl_ssl != &Curl_ssl_multi) + return id == Curl_ssl->info.id || + (name && strcasecompare(name, Curl_ssl->info.name)) ? + CURLSSLSET_OK : +#if defined(CURL_WITH_MULTI_SSL) + CURLSSLSET_TOO_LATE; +#else + CURLSSLSET_UNKNOWN_BACKEND; +#endif + + for(i = 0; available_backends[i]; i++) { + if(available_backends[i]->info.id == id || + (name && strcasecompare(available_backends[i]->info.name, name))) { + multissl_setup(available_backends[i]); + return CURLSSLSET_OK; + } + } + + return CURLSSLSET_UNKNOWN_BACKEND; +} + +#else /* USE_SSL */ +CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, + const curl_ssl_backend ***avail) +{ + (void)id; + (void)name; + (void)avail; + return CURLSSLSET_NO_BACKENDS; +} + +#endif /* !USE_SSL */ + +#ifdef USE_SSL + +void Curl_ssl_peer_cleanup(struct ssl_peer *peer) +{ + if(peer->dispname != peer->hostname) + free(peer->dispname); + free(peer->sni); + free(peer->hostname); + peer->hostname = peer->sni = peer->dispname = NULL; + peer->type = CURL_SSL_PEER_DNS; +} + +static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + if(connssl) { + Curl_ssl->close(cf, data); + connssl->state = ssl_connection_none; + Curl_ssl_peer_cleanup(&connssl->peer); + } + cf->connected = FALSE; +} + +static ssl_peer_type get_peer_type(const char *hostname) +{ + if(hostname && hostname[0]) { +#ifdef USE_IPV6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + if(Curl_inet_pton(AF_INET, hostname, &addr)) + return CURL_SSL_PEER_IPV4; +#ifdef USE_IPV6 + else if(Curl_inet_pton(AF_INET6, hostname, &addr)) { + return CURL_SSL_PEER_IPV6; + } +#endif + } + return CURL_SSL_PEER_DNS; +} + +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, + int transport) +{ + const char *ehostname, *edispname; + int eport; + + /* We need the hostname for SNI negotiation. Once handshaked, this remains + * the SNI hostname for the TLS connection. When the connection is reused, + * the settings in cf->conn might change. We keep a copy of the hostname we + * use for SNI. + */ +#ifndef CURL_DISABLE_PROXY + if(Curl_ssl_cf_is_proxy(cf)) { + ehostname = cf->conn->http_proxy.host.name; + edispname = cf->conn->http_proxy.host.dispname; + eport = cf->conn->http_proxy.port; + } + else +#endif + { + ehostname = cf->conn->host.name; + edispname = cf->conn->host.dispname; + eport = cf->conn->remote_port; + } + + /* change if ehostname changed */ + DEBUGASSERT(!ehostname || ehostname[0]); + if(ehostname && (!peer->hostname + || strcmp(ehostname, peer->hostname))) { + Curl_ssl_peer_cleanup(peer); + peer->hostname = strdup(ehostname); + if(!peer->hostname) { + Curl_ssl_peer_cleanup(peer); + return CURLE_OUT_OF_MEMORY; + } + if(!edispname || !strcmp(ehostname, edispname)) + peer->dispname = peer->hostname; + else { + peer->dispname = strdup(edispname); + if(!peer->dispname) { + Curl_ssl_peer_cleanup(peer); + return CURLE_OUT_OF_MEMORY; + } + } + + peer->type = get_peer_type(peer->hostname); + if(peer->type == CURL_SSL_PEER_DNS && peer->hostname[0]) { + /* not an IP address, normalize according to RCC 6066 ch. 3, + * max len of SNI is 2^16-1, no trailing dot */ + size_t len = strlen(peer->hostname); + if(len && (peer->hostname[len-1] == '.')) + len--; + if(len < USHRT_MAX) { + peer->sni = calloc(1, len + 1); + if(!peer->sni) { + Curl_ssl_peer_cleanup(peer); + return CURLE_OUT_OF_MEMORY; + } + Curl_strntolower(peer->sni, peer->hostname, len); + peer->sni[len] = 0; + } + } + + } + peer->port = eport; + peer->transport = transport; + return CURLE_OK; +} + +static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + cf_close(cf, data); + CF_DATA_RESTORE(cf, save); + cf_ctx_free(cf->ctx); + cf->ctx = NULL; +} + +static void ssl_cf_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + cf_close(cf, data); + if(cf->next) + cf->next->cft->do_close(cf->next, data); + CF_DATA_RESTORE(cf, save); +} + +static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct cf_call_data save; + CURLcode result; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + CURL_TRC_CF(data, cf, "cf_connect()"); + (void)connssl; + DEBUGASSERT(data->conn); + DEBUGASSERT(data->conn == cf->conn); + DEBUGASSERT(connssl); + DEBUGASSERT(cf->conn->host.name); + + result = cf->next->cft->do_connect(cf->next, data, blocking, done); + if(result || !*done) + goto out; + + *done = FALSE; + result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP); + if(result) + goto out; + + if(blocking) { + result = ssl_connect(cf, data); + *done = (result == CURLE_OK); + } + else { + result = ssl_connect_nonblocking(cf, data, done); + } + + if(!result && *done) { + cf->connected = TRUE; + connssl->handshake_done = Curl_now(); + DEBUGASSERT(connssl->state == ssl_connection_complete); + } +out: + CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done); + CF_DATA_RESTORE(cf, save); + return result; +} + +static bool ssl_cf_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_call_data save; + bool result; + + CF_DATA_SAVE(save, cf, data); + if(Curl_ssl->data_pending(cf, data)) + result = TRUE; + else + result = cf->next->cft->has_data_pending(cf->next, data); + CF_DATA_RESTORE(cf, save); + return result; +} + +static ssize_t ssl_cf_send(struct Curl_cfilter *cf, + struct Curl_easy *data, const void *buf, size_t len, + CURLcode *err) +{ + struct cf_call_data save; + ssize_t nwritten; + + CF_DATA_SAVE(save, cf, data); + *err = CURLE_OK; + nwritten = Curl_ssl->send_plain(cf, data, buf, len, err); + CF_DATA_RESTORE(cf, save); + return nwritten; +} + +static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, char *buf, size_t len, + CURLcode *err) +{ + struct cf_call_data save; + ssize_t nread; + + CF_DATA_SAVE(save, cf, data); + *err = CURLE_OK; + nread = Curl_ssl->recv_plain(cf, data, buf, len, err); + if(nread > 0) { + DEBUGASSERT((size_t)nread <= len); + } + else if(nread == 0) { + /* eof */ + *err = CURLE_OK; + } + CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, + nread, *err); + CF_DATA_RESTORE(cf, save); + return nread; +} + +static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + CURLcode result = CURLE_OK; + + *done = TRUE; + if(!cf->shutdown) { + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + result = Curl_ssl->shut_down(cf, data, TRUE, done); + CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done); + CF_DATA_RESTORE(cf, save); + cf->shutdown = (result || *done); + } + return result; +} + +static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + Curl_ssl->adjust_pollset(cf, data, ps); + CF_DATA_RESTORE(cf, save); +} + +static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_call_data save; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_ATTACH: + if(Curl_ssl->attach_data) { + CF_DATA_SAVE(save, cf, data); + Curl_ssl->attach_data(cf, data); + CF_DATA_RESTORE(cf, save); + } + break; + case CF_CTRL_DATA_DETACH: + if(Curl_ssl->detach_data) { + CF_DATA_SAVE(save, cf, data); + Curl_ssl->detach_data(cf, data); + CF_DATA_RESTORE(cf, save); + } + break; + default: + break; + } + return CURLE_OK; +} + +static CURLcode ssl_cf_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct ssl_connect_data *connssl = cf->ctx; + + switch(query) { + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected && !Curl_ssl_cf_is_proxy(cf)) + *when = connssl->handshake_done; + return CURLE_OK; + } + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, + bool *input_pending) +{ + struct cf_call_data save; + int result; + /* + * This function tries to determine connection status. + * + * Return codes: + * 1 means the connection is still in place + * 0 means the connection has been closed + * -1 means the connection status is unknown + */ + CF_DATA_SAVE(save, cf, data); + result = Curl_ssl->check_cxn(cf, data); + CF_DATA_RESTORE(cf, save); + if(result > 0) { + *input_pending = TRUE; + return TRUE; + } + if(result == 0) { + *input_pending = FALSE; + return FALSE; + } + /* ssl backend does not know */ + return cf->next? + cf->next->cft->is_alive(cf->next, data, input_pending) : + FALSE; /* pessimistic in absence of data */ +} + +struct Curl_cftype Curl_cft_ssl = { + "SSL", + CF_TYPE_SSL, + CURL_LOG_LVL_NONE, + ssl_cf_destroy, + ssl_cf_connect, + ssl_cf_close, + ssl_cf_shutdown, + Curl_cf_def_get_host, + ssl_cf_adjust_pollset, + ssl_cf_data_pending, + ssl_cf_send, + ssl_cf_recv, + ssl_cf_cntrl, + cf_ssl_is_alive, + Curl_cf_def_conn_keep_alive, + ssl_cf_query, +}; + +#ifndef CURL_DISABLE_PROXY + +struct Curl_cftype Curl_cft_ssl_proxy = { + "SSL-PROXY", + CF_TYPE_SSL|CF_TYPE_PROXY, + CURL_LOG_LVL_NONE, + ssl_cf_destroy, + ssl_cf_connect, + ssl_cf_close, + ssl_cf_shutdown, + Curl_cf_def_get_host, + ssl_cf_adjust_pollset, + ssl_cf_data_pending, + ssl_cf_send, + ssl_cf_recv, + ssl_cf_cntrl, + cf_ssl_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +#endif /* !CURL_DISABLE_PROXY */ + +static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn) +{ + struct Curl_cfilter *cf = NULL; + struct ssl_connect_data *ctx; + CURLcode result; + + DEBUGASSERT(data->conn); + + ctx = cf_ctx_new(data, alpn_get_spec(data->state.httpwant, + conn->bits.tls_enable_alpn)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = Curl_cf_create(&cf, &Curl_cft_ssl, ctx); + +out: + if(result) + cf_ctx_free(ctx); + *pcf = result? NULL : cf; + return result; +} + +CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_ssl_create(&cf, data, conn); + if(!result) + Curl_conn_cf_add(data, conn, sockindex, cf); + return result; +} + +CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_ssl_create(&cf, data, cf_at->conn); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); + return result; +} + +#ifndef CURL_DISABLE_PROXY + +static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn) +{ + struct Curl_cfilter *cf = NULL; + struct ssl_connect_data *ctx; + CURLcode result; + bool use_alpn = conn->bits.tls_enable_alpn; + int httpwant = CURL_HTTP_VERSION_1_1; + +#ifdef USE_HTTP2 + if(conn->http_proxy.proxytype == CURLPROXY_HTTPS2) { + use_alpn = TRUE; + httpwant = CURL_HTTP_VERSION_2; + } +#endif + + ctx = cf_ctx_new(data, alpn_get_spec(httpwant, use_alpn)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = Curl_cf_create(&cf, &Curl_cft_ssl_proxy, ctx); + +out: + if(result) + cf_ctx_free(ctx); + *pcf = result? NULL : cf; + return result; +} + +CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_ssl_proxy_create(&cf, data, cf_at->conn); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); + return result; +} + +#endif /* !CURL_DISABLE_PROXY */ + +bool Curl_ssl_supports(struct Curl_easy *data, unsigned int ssl_option) +{ + (void)data; + return (Curl_ssl->supports & ssl_option)? TRUE : FALSE; +} + +static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf) +{ + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_ssl) + return cf; +#ifndef CURL_DISABLE_PROXY + if(cf->cft == &Curl_cft_ssl_proxy) + return cf; +#endif + } + return NULL; +} + + +void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, + CURLINFO info, int n) +{ + void *result = NULL; + (void)n; + if(data->conn) { + struct Curl_cfilter *cf; + /* get first SSL filter in chain, if any is present */ + cf = get_ssl_filter(data->conn->cfilter[sockindex]); + if(cf) { + struct cf_call_data save; + CF_DATA_SAVE(save, cf, data); + result = Curl_ssl->get_internals(cf->ctx, info); + CF_DATA_RESTORE(cf, save); + } + } + return result; +} + +static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct cf_call_data save; + CURLcode result = CURLE_OK; + timediff_t timeout_ms; + int what, loop = 10; + + if(cf->shutdown) { + *done = TRUE; + return CURLE_OK; + } + CF_DATA_SAVE(save, cf, data); + + *done = FALSE; + while(!result && !*done && loop--) { + timeout_ms = Curl_shutdown_timeleft(cf->conn, cf->sockindex, NULL); + + if(timeout_ms < 0) { + /* no need to continue if time is already up */ + failf(data, "SSL shutdown timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + result = Curl_ssl->shut_down(cf, data, send_shutdown, done); + if(result ||*done) + goto out; + + if(connssl->io_need) { + what = Curl_conn_cf_poll(cf, data, timeout_ms); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + result = CURLE_RECV_ERROR; + goto out; + } + else if(0 == what) { + /* timeout */ + failf(data, "SSL shutdown timeout"); + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + /* socket is readable or writable */ + } + } +out: + CF_DATA_RESTORE(cf, save); + cf->shutdown = (result || *done); + return result; +} + +CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, + int sockindex, bool send_shutdown) +{ + struct Curl_cfilter *cf, *head; + CURLcode result = CURLE_OK; + + head = data->conn? data->conn->cfilter[sockindex] : NULL; + for(cf = head; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_ssl) { + bool done; + CURL_TRC_CF(data, cf, "shutdown and remove SSL, start"); + Curl_shutdown_start(data, sockindex, NULL); + result = vtls_shutdown_blocking(cf, data, send_shutdown, &done); + Curl_shutdown_clear(data, sockindex); + if(!result && !done) /* blocking failed? */ + result = CURLE_SSL_SHUTDOWN_FAILED; + Curl_conn_cf_discard_sub(head, cf, data, FALSE); + CURL_TRC_CF(data, cf, "shutdown and remove SSL, done -> %d", result); + break; + } + } + return result; +} + +bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf) +{ + return (cf->cft->flags & CF_TYPE_SSL) && (cf->cft->flags & CF_TYPE_PROXY); +} + +struct ssl_config_data * +Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) +{ +#ifdef CURL_DISABLE_PROXY + (void)cf; + return &data->set.ssl; +#else + return Curl_ssl_cf_is_proxy(cf)? &data->set.proxy_ssl : &data->set.ssl; +#endif +} + +struct ssl_primary_config * +Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf) +{ +#ifdef CURL_DISABLE_PROXY + return &cf->conn->ssl_config; +#else + return Curl_ssl_cf_is_proxy(cf)? + &cf->conn->proxy_ssl_config : &cf->conn->ssl_config; +#endif +} + +CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf, + const struct alpn_spec *spec) +{ + size_t i, len; + int off = 0; + unsigned char blen; + + memset(buf, 0, sizeof(*buf)); + for(i = 0; spec && i < spec->count; ++i) { + len = strlen(spec->entries[i]); + if(len >= ALPN_NAME_MAX) + return CURLE_FAILED_INIT; + blen = (unsigned char)len; + if(off + blen + 1 >= (int)sizeof(buf->data)) + return CURLE_FAILED_INIT; + buf->data[off++] = blen; + memcpy(buf->data + off, spec->entries[i], blen); + off += blen; + } + buf->len = off; + return CURLE_OK; +} + +CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf, + const struct alpn_spec *spec) +{ + size_t i, len; + size_t off = 0; + + memset(buf, 0, sizeof(*buf)); + for(i = 0; spec && i < spec->count; ++i) { + len = strlen(spec->entries[i]); + if(len >= ALPN_NAME_MAX) + return CURLE_FAILED_INIT; + if(off + len + 2 >= sizeof(buf->data)) + return CURLE_FAILED_INIT; + if(off) + buf->data[off++] = ','; + memcpy(buf->data + off, spec->entries[i], len); + off += len; + } + buf->data[off] = '\0'; + buf->len = (int)off; + return CURLE_OK; +} + +CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, + struct Curl_easy *data, + const unsigned char *proto, + size_t proto_len) +{ + int can_multi = 0; + unsigned char *palpn = +#ifndef CURL_DISABLE_PROXY + (cf->conn->bits.tunnel_proxy && Curl_ssl_cf_is_proxy(cf))? + &cf->conn->proxy_alpn : &cf->conn->alpn +#else + &cf->conn->alpn +#endif + ; + + if(proto && proto_len) { + if(proto_len == ALPN_HTTP_1_1_LENGTH && + !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) { + *palpn = CURL_HTTP_VERSION_1_1; + } +#ifdef USE_HTTP2 + else if(proto_len == ALPN_H2_LENGTH && + !memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) { + *palpn = CURL_HTTP_VERSION_2; + can_multi = 1; + } +#endif +#ifdef USE_HTTP3 + else if(proto_len == ALPN_H3_LENGTH && + !memcmp(ALPN_H3, proto, ALPN_H3_LENGTH)) { + *palpn = CURL_HTTP_VERSION_3; + can_multi = 1; + } +#endif + else { + *palpn = CURL_HTTP_VERSION_NONE; + failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto); + /* TODO: do we want to fail this? Previous code just ignored it and + * some vtls backends even ignore the return code of this function. */ + /* return CURLE_NOT_BUILT_IN; */ + goto out; + } + infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, (int)proto_len, proto); + } + else { + *palpn = CURL_HTTP_VERSION_NONE; + infof(data, VTLS_INFOF_NO_ALPN); + } + +out: + if(!Curl_ssl_cf_is_proxy(cf)) + Curl_multiuse_state(data, can_multi? + BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + return CURLE_OK; +} + +#endif /* USE_SSL */ diff --git a/third_party/curl/lib/vtls/vtls.h b/third_party/curl/lib/vtls/vtls.h new file mode 100644 index 0000000000..8fe2bc7cea --- /dev/null +++ b/third_party/curl/lib/vtls/vtls.h @@ -0,0 +1,258 @@ +#ifndef HEADER_CURL_VTLS_H +#define HEADER_CURL_VTLS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +struct connectdata; +struct ssl_config_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 */ +#define SSLSUPP_PINNEDPUBKEY (1<<2) /* supports CURLOPT_PINNEDPUBLICKEY */ +#define SSLSUPP_SSL_CTX (1<<3) /* supports CURLOPT_SSL_CTX */ +#define SSLSUPP_HTTPS_PROXY (1<<4) /* supports access via HTTPS proxies */ +#define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */ +#define SSLSUPP_CAINFO_BLOB (1<<6) +#define SSLSUPP_ECH (1<<7) +#define SSLSUPP_CA_CACHE (1<<8) + +#define ALPN_ACCEPTED "ALPN: server accepted " + +#define VTLS_INFOF_NO_ALPN \ + "ALPN: server did not agree on a protocol. Uses default." +#define VTLS_INFOF_ALPN_OFFER_1STR \ + "ALPN: curl offers %s" +#define VTLS_INFOF_ALPN_ACCEPTED_1STR \ + ALPN_ACCEPTED "%s" +#define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \ + ALPN_ACCEPTED "%.*s" + +/* Curl_multi SSL backend-specific data; declared differently by each SSL + backend */ +struct Curl_cfilter; + +CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, + const curl_ssl_backend ***avail); + +#ifndef MAX_PINNED_PUBKEY_SIZE +#define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */ +#endif + +#ifndef CURL_SHA256_DIGEST_LENGTH +#define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ +#endif + +curl_sslbackend Curl_ssl_backend(void); + +/** + * Init ssl config for a new easy handle. + */ +void Curl_ssl_easy_config_init(struct Curl_easy *data); + +/** + * Init the `data->set.ssl` and `data->set.proxy_ssl` for + * connection matching use. + */ +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data); + +/** + * Init SSL configs (main + proxy) for a new connection from the easy handle. + */ +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn); + +/** + * Free allocated resources in SSL configs (main + proxy) for + * the given connection. + */ +void Curl_ssl_conn_config_cleanup(struct connectdata *conn); + +/** + * Return TRUE iff SSL configuration from `conn` is functionally the + * same as the one on `candidate`. + * @param proxy match the proxy SSL config or the main one + */ +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy); + +/* Update certain connection SSL config flags after they have + * been changed on the easy handle. Will work for `verifypeer`, + * `verifyhost` and `verifystatus`. */ +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); + +/** + * Init SSL peer information for filter. Can be called repeatedly. + */ +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, + struct Curl_cfilter *cf, int transport); +/** + * Free all allocated data and reset peer information. + */ +void Curl_ssl_peer_cleanup(struct ssl_peer *peer); + +#ifdef USE_SSL +int Curl_ssl_init(void); +void Curl_ssl_cleanup(void); +/* 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); +CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine); +/* Sets engine as default for all SSL operations */ +CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data); +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); + +/* Certificate information list handling. */ + +void Curl_ssl_free_certinfo(struct Curl_easy *data); +CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num); +CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, int certnum, + const char *label, const char *value, + size_t valuelen); +CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum, + const char *label, const char *value); + +/* Functions to be used by SSL library adaptation functions */ + +/* Lock session cache mutex. + * Call this before calling other Curl_ssl_*session* functions + * Caller should unlock this mutex as soon as possible, as it may block + * other SSL connection from making progress. + * The purpose of explicitly locking SSL session cache data is to allow + * individual SSL engines to manage session lifetime in their specific way. + */ +void Curl_ssl_sessionid_lock(struct Curl_easy *data); + +/* Unlock session cache mutex */ +void Curl_ssl_sessionid_unlock(struct Curl_easy *data); + +/* Kill a single session ID entry in the cache + * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). + * This will call engine-specific curlssl_session_free function, which must + * take sessionid object ownership from sessionid cache + * (e.g. decrement refcount). + */ +void Curl_ssl_kill_session(struct Curl_ssl_session *session); +/* delete a session from the cache + * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). + * This will call engine-specific curlssl_session_free function, which must + * take sessionid object ownership from sessionid cache + * (e.g. decrement refcount). + */ +void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid); + +/* get N random bytes into the buffer */ +CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer, + size_t length); +/* Check pinned public key. */ +CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, + const char *pinnedpubkey, + const unsigned char *pubkey, size_t pubkeylen); + +bool Curl_ssl_cert_status_request(void); + +bool Curl_ssl_false_start(struct Curl_easy *data); + +#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ + +CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + +CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + +CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, + int sockindex, bool send_shutdown); + +#ifndef CURL_DISABLE_PROXY +CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); +#endif /* !CURL_DISABLE_PROXY */ + +/** + * 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, unsigned 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); + +/** + * Get the ssl_config_data in `data` that is relevant for cfilter `cf`. + */ +struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, + struct Curl_easy *data); + +/** + * Get the primary config relevant for the filter from its connection. + */ +struct ssl_primary_config * + Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf); + +extern struct Curl_cftype Curl_cft_ssl; +#ifndef CURL_DISABLE_PROXY +extern struct Curl_cftype Curl_cft_ssl_proxy; +#endif + +#else /* if not USE_SSL */ + +/* 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_close_all(x) Curl_nop_stmt +#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_initsessions(x,y) CURLE_OK +#define Curl_ssl_free_certinfo(x) Curl_nop_stmt +#define Curl_ssl_kill_session(x) Curl_nop_stmt +#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN) +#define Curl_ssl_cert_status_request() FALSE +#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_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN +#define Curl_ssl_cfilter_remove(a,b,c) CURLE_OK +#define Curl_ssl_cf_get_config(a,b) NULL +#define Curl_ssl_cf_get_primary_config(a) NULL +#endif + +#endif /* HEADER_CURL_VTLS_H */ diff --git a/third_party/curl/lib/vtls/vtls_int.h b/third_party/curl/lib/vtls/vtls_int.h new file mode 100644 index 0000000000..1472a0ca5c --- /dev/null +++ b/third_party/curl/lib/vtls/vtls_int.h @@ -0,0 +1,229 @@ +#ifndef HEADER_CURL_VTLS_INT_H +#define HEADER_CURL_VTLS_INT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include "cfilters.h" +#include "urldata.h" + +#ifdef USE_SSL + +/* see https://www.iana.org/assignments/tls-extensiontype-values/ */ +#define ALPN_HTTP_1_1_LENGTH 8 +#define ALPN_HTTP_1_1 "http/1.1" +#define ALPN_H2_LENGTH 2 +#define ALPN_H2 "h2" +#define ALPN_H3_LENGTH 2 +#define ALPN_H3 "h3" + +/* conservative sizes on the ALPN entries and count we are handling, + * we can increase these if we ever feel the need or have to accommodate + * ALPN strings from the "outside". */ +#define ALPN_NAME_MAX 10 +#define ALPN_ENTRIES_MAX 3 +#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1)) + +struct alpn_spec { + const char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX]; + size_t count; /* number of entries */ +}; + +struct alpn_proto_buf { + unsigned char data[ALPN_PROTO_BUF_MAX]; + int len; +}; + +CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf, + const struct alpn_spec *spec); +CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf, + const struct alpn_spec *spec); + +CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, + struct Curl_easy *data, + const unsigned char *proto, + size_t proto_len); + +/* enum for the nonblocking SSL connection state machine */ +typedef enum { + ssl_connect_1, + ssl_connect_2, + ssl_connect_3, + ssl_connect_done +} ssl_connect_state; + +typedef enum { + ssl_connection_none, + ssl_connection_negotiating, + ssl_connection_complete +} ssl_connection_state; + +#define CURL_SSL_IO_NEED_NONE (0) +#define CURL_SSL_IO_NEED_RECV (1<<0) +#define CURL_SSL_IO_NEED_SEND (1<<1) + +/* Information in each SSL cfilter context: cf->ctx */ +struct ssl_connect_data { + struct ssl_peer peer; + const struct alpn_spec *alpn; /* ALPN to use or NULL for none */ + void *backend; /* vtls backend specific props */ + struct cf_call_data call_data; /* data handle used in current call */ + struct curltime handshake_done; /* time when handshake finished */ + ssl_connection_state state; + ssl_connect_state connecting_state; + int io_need; /* TLS signals special SEND/RECV needs */ + BIT(use_alpn); /* if ALPN shall be used in handshake */ + BIT(peer_closed); /* peer has closed connection */ +}; + + +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) \ + ((struct ssl_connect_data *)(cf)->ctx)->call_data + + +/* Definitions for SSL Implementations */ + +struct Curl_ssl { + /* + * 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 Curl_cfilter *cf, struct Curl_easy *data); + CURLcode (*shut_down)(struct Curl_cfilter *cf, struct Curl_easy *data, + bool send_shutdown, bool *done); + bool (*data_pending)(struct Curl_cfilter *cf, + const struct Curl_easy *data); + + /* 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_cfilter *cf, + struct Curl_easy *data); + CURLcode (*connect_nonblocking)(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done); + + /* During handshake/shutdown, adjust the pollset to include the socket + * for POLLOUT or POLLIN as needed. Mandatory. */ + void (*adjust_pollset)(struct Curl_cfilter *cf, struct Curl_easy *data, + struct easy_pollset *ps); + void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); + void (*close)(struct Curl_cfilter *cf, struct Curl_easy *data); + void (*close_all)(struct Curl_easy *data); + + 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 (*attach_data)(struct Curl_cfilter *cf, struct Curl_easy *data); + void (*detach_data)(struct Curl_cfilter *cf, struct Curl_easy *data); + + ssize_t (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *code); + ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, + 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); +CURLcode Curl_none_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, + bool send_shutdown, bool *done); +int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data); +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(struct Curl_cfilter *cf, + const struct Curl_easy *data); +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); +void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, + struct easy_pollset *ps); + +/** + * Get the SSL filter below the given one or NULL if there is none. + */ +bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf); + +/* extract a session ID + * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). + * Caller must make sure that the ownership of returned sessionid object + * is properly taken (e.g. its refcount is incremented + * under sessionid mutex). + */ +bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + void **ssl_sessionid, + size_t *idsize); /* set 0 if unknown */ + +/* Set a TLS session ID for `peer`. Replaces an existing session ID if + * not already the very same. + * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). + * Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb` + * to deallocate it. Is called in all outcomes, either right away or + * later when the session cache is cleaned up. + * Caller must ensure that it has properly shared ownership of this sessionid + * object with cache (e.g. incrementing refcount on success) + */ +CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + void *sessionid, + size_t sessionid_size, + Curl_ssl_sessionid_dtor *sessionid_free_cb); + +#include "openssl.h" /* OpenSSL versions */ +#include "gtls.h" /* GnuTLS 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 /* USE_SSL */ + +#endif /* HEADER_CURL_VTLS_INT_H */ diff --git a/third_party/curl/lib/vtls/wolfssl.c b/third_party/curl/lib/vtls/wolfssl.c new file mode 100644 index 0000000000..2b467c9350 --- /dev/null +++ b/third_party/curl/lib/vtls/wolfssl.c @@ -0,0 +1,1803 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * Source file for all wolfSSL specific code for the TLS/SSL layer. No code + * but vtls.c should ever call or use these functions. + * + */ + +#include "curl_setup.h" + +#ifdef USE_WOLFSSL + +#define WOLFSSL_OPTIONS_IGNORE_SYS +#include +#include + +/* To determine what functions are available we rely on one or both of: + - the user's options.h generated by wolfSSL + - the symbols detected by curl's configure + Since they are markedly different from one another, and one or the other may + not be available, we do some checking below to bring things in sync. */ + +/* HAVE_ALPN is wolfSSL's build time symbol for enabling ALPN in options.h. */ +#ifndef HAVE_ALPN +#ifdef HAVE_WOLFSSL_USEALPN +#define HAVE_ALPN +#endif +#endif + +#include + +#include "urldata.h" +#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 */ +#include "select.h" +#include "strcase.h" +#include "x509asn1.h" +#include "curl_printf.h" +#include "multiif.h" + +#include +#include +#include +#include "wolfssl.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +#ifdef USE_ECH +# include "curl_base64.h" +# define ECH_ENABLED(__data__) \ + (__data__->set.tls_ech && \ + !(__data__->set.tls_ech & CURLECH_DISABLE)\ + ) +#endif /* USE_ECH */ + +/* KEEP_PEER_CERT is a product of the presence of build time symbol + OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is + in wolfSSL's settings.h, and the latter two are build time symbols in + options.h. */ +#ifndef KEEP_PEER_CERT +#if defined(HAVE_WOLFSSL_GET_PEER_CERTIFICATE) || \ + (defined(OPENSSL_EXTRA) && !defined(NO_CERTS)) +#define KEEP_PEER_CERT +#endif +#endif + +#if defined(HAVE_WOLFSSL_FULL_BIO) && HAVE_WOLFSSL_FULL_BIO +#define USE_BIO_CHAIN +#else +#undef USE_BIO_CHAIN +#endif + +#ifdef OPENSSL_EXTRA +/* + * Availability note: + * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in + * wolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that + * option is not set, then TLS 1.3 will not be logged. + * For TLS 1.2 and before, we use wolfSSL_get_keys(). + * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA + * (--enable-opensslextra or --enable-all). + */ +#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) +static int +wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret, + int secretSz, void *ctx) +{ + const char *label; + unsigned char client_random[SSL3_RANDOM_SIZE]; + (void)ctx; + + if(!ssl || !Curl_tls_keylog_enabled()) { + return 0; + } + + switch(id) { + case CLIENT_EARLY_TRAFFIC_SECRET: + label = "CLIENT_EARLY_TRAFFIC_SECRET"; + break; + case CLIENT_HANDSHAKE_TRAFFIC_SECRET: + label = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"; + break; + case SERVER_HANDSHAKE_TRAFFIC_SECRET: + label = "SERVER_HANDSHAKE_TRAFFIC_SECRET"; + break; + case CLIENT_TRAFFIC_SECRET: + label = "CLIENT_TRAFFIC_SECRET_0"; + break; + case SERVER_TRAFFIC_SECRET: + label = "SERVER_TRAFFIC_SECRET_0"; + break; + case EARLY_EXPORTER_SECRET: + label = "EARLY_EXPORTER_SECRET"; + break; + case EXPORTER_SECRET: + label = "EXPORTER_SECRET"; + break; + default: + return 0; + } + + if(SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) { + /* Should never happen as wolfSSL_KeepArrays() was called before. */ + return 0; + } + + Curl_tls_keylog_write(label, client_random, secret, secretSz); + return 0; +} +#endif /* defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) */ + +static void +wolfssl_log_tls12_secret(SSL *ssl) +{ + unsigned char *ms, *sr, *cr; + unsigned int msLen, srLen, crLen, i, x = 0; + +#if LIBWOLFSSL_VERSION_HEX >= 0x0300d000 /* >= 3.13.0 */ + /* wolfSSL_GetVersion is available since 3.13, we use it instead of + * SSL_version since the latter relies on OPENSSL_ALL (--enable-opensslall or + * --enable-all). Failing to perform this check could result in an unusable + * key log line when TLS 1.3 is actually negotiated. */ + switch(wolfSSL_GetVersion(ssl)) { + case WOLFSSL_SSLV3: + case WOLFSSL_TLSV1: + case WOLFSSL_TLSV1_1: + case WOLFSSL_TLSV1_2: + break; + default: + /* TLS 1.3 does not use this mechanism, the "master secret" returned below + * is not directly usable. */ + return; + } +#endif + + if(wolfSSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != + SSL_SUCCESS) { + return; + } + + /* Check for a missing master secret and skip logging. That can happen if + * curl rejects the server certificate and aborts the handshake. + */ + for(i = 0; i < msLen; i++) { + x |= ms[i]; + } + if(x == 0) { + return; + } + + Curl_tls_keylog_write("CLIENT_RANDOM", cr, ms, msLen); +} +#endif /* OPENSSL_EXTRA */ + +static int do_file_type(const char *type) +{ + if(!type || !type[0]) + return SSL_FILETYPE_PEM; + if(strcasecompare(type, "PEM")) + return SSL_FILETYPE_PEM; + if(strcasecompare(type, "DER")) + return SSL_FILETYPE_ASN1; + return -1; +} + +#ifdef WOLFSSL_HAVE_KYBER +struct group_name_map { + const word16 group; + const char *name; +}; + +static const struct group_name_map gnm[] = { + { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" }, + { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" }, + { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" }, + { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" }, + { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" }, + { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" }, + { 0, NULL } +}; +#endif + +#ifdef USE_BIO_CHAIN + +static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio) +{ + wolfSSL_BIO_set_shutdown(bio, 1); + wolfSSL_BIO_set_data(bio, NULL); + return 1; +} + +static int wolfssl_bio_cf_destroy(WOLFSSL_BIO *bio) +{ + if(!bio) + return 0; + return 1; +} + +static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr) +{ + struct Curl_cfilter *cf = BIO_get_data(bio); + long ret = 1; + + (void)cf; + (void)ptr; + switch(cmd) { + case BIO_CTRL_GET_CLOSE: + ret = (long)wolfSSL_BIO_get_shutdown(bio); + break; + case BIO_CTRL_SET_CLOSE: + wolfSSL_BIO_set_shutdown(bio, (int)num); + break; + case BIO_CTRL_FLUSH: + /* we do no delayed writes, but if we ever would, this + * needs to trigger it. */ + ret = 1; + break; + case BIO_CTRL_DUP: + ret = 1; + break; +#ifdef BIO_CTRL_EOF + case BIO_CTRL_EOF: + /* EOF has been reached on input? */ + return (!cf->next || !cf->next->connected); +#endif + default: + ret = 0; + break; + } + return ret; +} + +static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio, + const char *buf, int blen) +{ + struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nwritten; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + backend->io_result = result; + CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d", + blen, nwritten, result); + wolfSSL_BIO_clear_retry_flags(bio); + if(nwritten < 0 && CURLE_AGAIN == result) + BIO_set_retry_write(bio); + return (int)nwritten; +} + +static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) +{ + struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nread; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + /* OpenSSL catches this case, so should we. */ + if(!buf) + return 0; + + nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); + backend->io_result = result; + CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %zd, %d", blen, nread, result); + wolfSSL_BIO_clear_retry_flags(bio); + if(nread < 0 && CURLE_AGAIN == result) + BIO_set_retry_read(bio); + else if(nread == 0) + connssl->peer_closed = TRUE; + return (int)nread; +} + +static WOLFSSL_BIO_METHOD *wolfssl_bio_cf_method = NULL; + +static void wolfssl_bio_cf_init_methods(void) +{ + wolfssl_bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO"); + wolfSSL_BIO_meth_set_write(wolfssl_bio_cf_method, &wolfssl_bio_cf_out_write); + wolfSSL_BIO_meth_set_read(wolfssl_bio_cf_method, &wolfssl_bio_cf_in_read); + wolfSSL_BIO_meth_set_ctrl(wolfssl_bio_cf_method, &wolfssl_bio_cf_ctrl); + wolfSSL_BIO_meth_set_create(wolfssl_bio_cf_method, &wolfssl_bio_cf_create); + wolfSSL_BIO_meth_set_destroy(wolfssl_bio_cf_method, &wolfssl_bio_cf_destroy); +} + +static void wolfssl_bio_cf_free_methods(void) +{ + wolfSSL_BIO_meth_free(wolfssl_bio_cf_method); +} + +#else /* USE_BIO_CHAIN */ + +#define wolfssl_bio_cf_init_methods() Curl_nop_stmt +#define wolfssl_bio_cf_free_methods() Curl_nop_stmt + +#endif /* !USE_BIO_CHAIN */ + +static CURLcode populate_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + X509_STORE *store, + struct wolfssl_ctx *wssl) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : conn_config->CAfile); + const char * const ssl_capath = conn_config->CApath; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + bool imported_native_ca = false; + +#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS) + /* load native CA certificates */ + if(ssl_config->native_ca_store) { + if(wolfSSL_CTX_load_system_CA_certs(wssl->ctx) != WOLFSSL_SUCCESS) { + infof(data, "error importing native CA store, continuing anyway"); + } + else { + imported_native_ca = true; + infof(data, "successfully imported native CA store"); + wssl->x509_store_setup = TRUE; + } + } +#endif /* !NO_FILESYSTEM */ + + /* load certificate blob */ + if(ca_info_blob) { + if(wolfSSL_CTX_load_verify_buffer(wssl->ctx, ca_info_blob->data, + (long)ca_info_blob->len, + SSL_FILETYPE_PEM) != SSL_SUCCESS) { + if(imported_native_ca) { + infof(data, "error importing CA certificate blob, continuing anyway"); + } + else { + failf(data, "error importing CA certificate blob"); + return CURLE_SSL_CACERT_BADFILE; + } + } + else { + infof(data, "successfully imported CA certificate blob"); + wssl->x509_store_setup = TRUE; + } + } + +#ifndef NO_FILESYSTEM + /* load trusted cacert from file if not blob */ + + CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d", + ssl_cafile? ssl_cafile : "none", !!ca_info_blob); + if(!store) + return CURLE_OUT_OF_MEMORY; + + if((ssl_cafile || ssl_capath) && (!wssl->x509_store_setup)) { + int rc = + wolfSSL_CTX_load_verify_locations_ex(wssl->ctx, + ssl_cafile, + ssl_capath, + WOLFSSL_LOAD_FLAG_IGNORE_ERR); + if(SSL_SUCCESS != rc) { + if(conn_config->verifypeer) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + else { + /* Just continue with a warning if no strict certificate + verification is required. */ + infof(data, "error setting certificate verify locations," + " continuing anyway:"); + } + } + else { + /* Everything is fine. */ + infof(data, "successfully set certificate verify locations:"); + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } +#endif + (void)store; + wssl->x509_store_setup = TRUE; + return CURLE_OK; +} + +/* key to use at `multi->proto_hash` */ +#define MPROTO_WSSL_X509_KEY "tls:wssl:x509:share" + +struct wssl_x509_share { + char *CAfile; /* CAfile path used to generate X509 store */ + WOLFSSL_X509_STORE *store; /* cached X509 store or NULL if none */ + struct curltime time; /* when the cached store was created */ +}; + +static void wssl_x509_share_free(void *key, size_t key_len, void *p) +{ + struct wssl_x509_share *share = p; + DEBUGASSERT(key_len == (sizeof(MPROTO_WSSL_X509_KEY)-1)); + DEBUGASSERT(!memcmp(MPROTO_WSSL_X509_KEY, key, key_len)); + (void)key; + (void)key_len; + if(share->store) { + wolfSSL_X509_STORE_free(share->store); + } + free(share->CAfile); + free(share); +} + +static bool +cached_x509_store_expired(const struct Curl_easy *data, + const struct wssl_x509_share *mb) +{ + const struct ssl_general_config *cfg = &data->set.general_ssl; + struct curltime now = Curl_now(); + timediff_t elapsed_ms = Curl_timediff(now, mb->time); + timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + + if(timeout_ms < 0) + return false; + + return elapsed_ms >= timeout_ms; +} + +static bool +cached_x509_store_different(struct Curl_cfilter *cf, + const struct wssl_x509_share *mb) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!mb->CAfile || !conn_config->CAfile) + return mb->CAfile != conn_config->CAfile; + + return strcmp(mb->CAfile, conn_config->CAfile); +} + +static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct Curl_multi *multi = data->multi; + struct wssl_x509_share *share; + WOLFSSL_X509_STORE *store = NULL; + + DEBUGASSERT(multi); + share = multi? Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_WSSL_X509_KEY, + sizeof(MPROTO_WSSL_X509_KEY)-1) : NULL; + if(share && share->store && + !cached_x509_store_expired(data, share) && + !cached_x509_store_different(cf, share)) { + store = share->store; + } + + return store; +} + +static void set_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + X509_STORE *store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi; + struct wssl_x509_share *share; + + DEBUGASSERT(multi); + if(!multi) + return; + share = Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_WSSL_X509_KEY, + sizeof(MPROTO_WSSL_X509_KEY)-1); + + if(!share) { + share = calloc(1, sizeof(*share)); + if(!share) + return; + if(!Curl_hash_add2(&multi->proto_hash, + (void *)MPROTO_WSSL_X509_KEY, + sizeof(MPROTO_WSSL_X509_KEY)-1, + share, wssl_x509_share_free)) { + free(share); + return; + } + } + + if(wolfSSL_X509_STORE_up_ref(store)) { + char *CAfile = NULL; + + if(conn_config->CAfile) { + CAfile = strdup(conn_config->CAfile); + if(!CAfile) { + X509_STORE_free(store); + return; + } + } + + if(share->store) { + X509_STORE_free(share->store); + free(share->CAfile); + } + + share->time = Curl_now(); + share->store = store; + share->CAfile = CAfile; + } +} + +CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wssl) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_OK; + WOLFSSL_X509_STORE *cached_store; + bool cache_criteria_met; + + /* Consider the X509 store cacheable if it comes exclusively from a CAfile, + or no source is provided and we are falling back to wolfSSL's built-in + default. */ + cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) && + conn_config->verifypeer && + !conn_config->CApath && + !conn_config->ca_info_blob && + !ssl_config->primary.CRLfile && + !ssl_config->native_ca_store; + + cached_store = cache_criteria_met ? get_cached_x509_store(cf, data) : NULL; + if(cached_store && wolfSSL_X509_STORE_up_ref(cached_store)) { + wolfSSL_CTX_set_cert_store(wssl->ctx, cached_store); + } + else if(cache_criteria_met) { + /* wolfSSL's initial store in CTX is not shareable by default. + * Make a new one, suitable for adding to the cache. See #14278 */ + X509_STORE *store = wolfSSL_X509_STORE_new(); + if(!store) { + failf(data, "SSL: could not create a X509 store"); + return CURLE_OUT_OF_MEMORY; + } + wolfSSL_CTX_set_cert_store(wssl->ctx, store); + + result = populate_x509_store(cf, data, store, wssl); + if(!result) { + set_cached_x509_store(cf, data, store); + } + } + else { + /* We never share the CTX's store, use it. */ + X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx); + result = populate_x509_store(cf, data, store, wssl); + } + + return result; +} + +/* + * This function loads all the client/CA certificates and CRLs. Setup the TLS + * layer and do all necessary magic. + */ +static CURLcode +wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + char *ciphers, *curves; + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + WOLFSSL_METHOD* req_method = NULL; +#ifdef WOLFSSL_HAVE_KYBER + word16 pqkem = 0; + size_t idx = 0; +#endif +#ifdef HAVE_SNI + bool sni = FALSE; +#define use_sni(x) sni = (x) +#else +#define use_sni(x) Curl_nop_stmt +#endif + + DEBUGASSERT(backend); + + if(connssl->state == ssl_connection_complete) + return CURLE_OK; + + if(conn_config->version_max != CURL_SSLVERSION_MAX_NONE) { + failf(data, "wolfSSL does not support to set maximum SSL/TLS version"); + return CURLE_SSL_CONNECT_ERROR; + } + + /* check to see if we have been told to use an explicit SSL/TLS version */ + switch(conn_config->version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: +#if LIBWOLFSSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ + /* minimum protocol version is set later after the CTX object is created */ + req_method = SSLv23_client_method(); +#else + infof(data, "wolfSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " + "TLS 1.0 is used exclusively"); + req_method = TLSv1_client_method(); +#endif + use_sni(TRUE); + break; + case CURL_SSLVERSION_TLSv1_0: +#if defined(WOLFSSL_ALLOW_TLSV10) && !defined(NO_OLD_TLS) + req_method = TLSv1_client_method(); + use_sni(TRUE); + break; +#else + failf(data, "wolfSSL does not support TLS 1.0"); + return CURLE_NOT_BUILT_IN; +#endif + case CURL_SSLVERSION_TLSv1_1: +#ifndef NO_OLD_TLS + req_method = TLSv1_1_client_method(); + use_sni(TRUE); + break; +#else + failf(data, "wolfSSL does not support TLS 1.1"); + return CURLE_NOT_BUILT_IN; +#endif + case CURL_SSLVERSION_TLSv1_2: +#ifndef WOLFSSL_NO_TLS12 + req_method = TLSv1_2_client_method(); + use_sni(TRUE); +#else + failf(data, "wolfSSL does not support TLS 1.2"); + return CURLE_NOT_BUILT_IN; +#endif + break; + case CURL_SSLVERSION_TLSv1_3: +#ifdef WOLFSSL_TLS13 + req_method = wolfTLSv1_3_client_method(); + use_sni(TRUE); + break; +#else + failf(data, "wolfSSL: TLS 1.3 is not yet supported"); + return CURLE_SSL_CONNECT_ERROR; +#endif + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + + if(!req_method) { + failf(data, "SSL: could not create a method"); + return CURLE_OUT_OF_MEMORY; + } + + if(backend->ctx) + wolfSSL_CTX_free(backend->ctx); + backend->ctx = wolfSSL_CTX_new(req_method); + + if(!backend->ctx) { + failf(data, "SSL: could not create a context"); + return CURLE_OUT_OF_MEMORY; + } + + switch(conn_config->version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: +#if LIBWOLFSSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ + /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is + * whatever minimum version of TLS was built in and at least TLS 1.0. For + * later library versions that could change (eg TLS 1.0 built in but + * defaults to TLS 1.1) so we have this short circuit evaluation to find + * the minimum supported TLS version. + */ + if((wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1) != 1) && + (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_1) != 1) && + (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_2) != 1) +#ifdef WOLFSSL_TLS13 + && (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_3) != 1) +#endif + ) { + failf(data, "SSL: could not set the minimum protocol version"); + return CURLE_SSL_CONNECT_ERROR; + } +#endif + FALLTHROUGH(); + default: + break; + } + + ciphers = conn_config->cipher_list; + if(ciphers) { + if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { + failf(data, "failed setting cipher list: %s", ciphers); + return CURLE_SSL_CIPHER; + } + infof(data, "Cipher selection: %s", ciphers); + } + + curves = conn_config->curves; + if(curves) { + +#ifdef WOLFSSL_HAVE_KYBER + for(idx = 0; gnm[idx].name != NULL; idx++) { + if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) { + pqkem = gnm[idx].group; + break; + } + } + + if(pqkem == 0) +#endif + { + if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { + failf(data, "failed setting curves list: '%s'", curves); + return CURLE_SSL_CIPHER; + } + } + } + +#ifndef NO_FILESYSTEM + /* Load the client certificate, and private key */ + if(ssl_config->primary.clientcert) { + char *key_file = ssl_config->key; + int file_type = do_file_type(ssl_config->cert_type); + + if(file_type == WOLFSSL_FILETYPE_PEM) { + if(wolfSSL_CTX_use_certificate_chain_file(backend->ctx, + ssl_config->primary.clientcert) + != 1) { + failf(data, "unable to use client certificate"); + return CURLE_SSL_CONNECT_ERROR; + } + } + else if(file_type == WOLFSSL_FILETYPE_ASN1) { + if(wolfSSL_CTX_use_certificate_file(backend->ctx, + ssl_config->primary.clientcert, + file_type) != 1) { + failf(data, "unable to use client certificate"); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { + failf(data, "unknown cert type"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + if(!key_file) + key_file = ssl_config->primary.clientcert; + else + file_type = do_file_type(ssl_config->key_type); + + if(wolfSSL_CTX_use_PrivateKey_file(backend->ctx, key_file, + file_type) != 1) { + failf(data, "unable to set private key"); + return CURLE_SSL_CONNECT_ERROR; + } + } +#endif /* !NO_FILESYSTEM */ + + /* SSL always tries to verify the peer, this only says whether it should + * fail to connect if the verification fails, or if it should continue + * anyway. In the latter case the result of the verification is checked with + * SSL_get_verify_result() below. */ + wolfSSL_CTX_set_verify(backend->ctx, + conn_config->verifypeer?SSL_VERIFY_PEER: + SSL_VERIFY_NONE, NULL); + +#ifdef HAVE_SNI + if(sni && connssl->peer.sni) { + size_t sni_len = strlen(connssl->peer.sni); + if((sni_len < USHRT_MAX)) { + if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, + connssl->peer.sni, + (unsigned short)sni_len) != 1) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + } + } +#endif + + /* give application a chance to interfere with SSL set up. */ + if(data->set.ssl.fsslctx) { + CURLcode result; + if(!backend->x509_store_setup) { + result = Curl_wssl_setup_x509_store(cf, data, backend); + if(result) + return result; + } + result = (*data->set.ssl.fsslctx)(data, backend->ctx, + data->set.ssl.fsslctxp); + if(result) { + failf(data, "error signaled by ssl ctx callback"); + return result; + } + } +#ifdef NO_FILESYSTEM + else if(conn_config->verifypeer) { + failf(data, "SSL: Certificates cannot be loaded because wolfSSL was built" + " with \"no filesystem\". Either disable peer verification" + " (insecure) or if you are building an application with libcurl you" + " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); + return CURLE_SSL_CONNECT_ERROR; + } +#endif + + /* Let's make an SSL structure */ + if(backend->handle) + wolfSSL_free(backend->handle); + backend->handle = wolfSSL_new(backend->ctx); + if(!backend->handle) { + failf(data, "SSL: could not create a handle"); + return CURLE_OUT_OF_MEMORY; + } + +#ifdef WOLFSSL_HAVE_KYBER + if(pqkem) { + if(wolfSSL_UseKeyShare(backend->handle, pqkem) != WOLFSSL_SUCCESS) { + failf(data, "unable to use PQ KEM"); + } + } +#endif + +#ifdef HAVE_ALPN + if(connssl->alpn) { + struct alpn_proto_buf proto; + CURLcode result; + + result = Curl_alpn_to_proto_str(&proto, connssl->alpn); + if(result || + wolfSSL_UseALPN(backend->handle, + (char *)proto.data, (unsigned int)proto.len, + WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { + failf(data, "SSL: failed setting ALPN protocols"); + return CURLE_SSL_CONNECT_ERROR; + } + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } +#endif /* HAVE_ALPN */ + +#ifdef OPENSSL_EXTRA + if(Curl_tls_keylog_enabled()) { + /* Ensure the Client Random is preserved. */ + wolfSSL_KeepArrays(backend->handle); +#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) + wolfSSL_set_tls13_secret_cb(backend->handle, + wolfssl_tls13_secret_callback, NULL); +#endif + } +#endif /* OPENSSL_EXTRA */ + +#ifdef HAVE_SECURE_RENEGOTIATION + if(wolfSSL_UseSecureRenegotiation(backend->handle) != SSL_SUCCESS) { + failf(data, "SSL: failed setting secure renegotiation"); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* HAVE_SECURE_RENEGOTIATION */ + + /* Check if there is a cached ID we can/should use here! */ + if(ssl_config->primary.cache_session) { + void *ssl_sessionid = NULL; + + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + &ssl_sessionid, NULL)) { + /* we got a session id, use it! */ + if(!SSL_set_session(backend->handle, ssl_sessionid)) { + Curl_ssl_delsessionid(data, ssl_sessionid); + infof(data, "cannot use session ID, going on without"); + } + else + infof(data, "SSL reusing session ID"); + } + Curl_ssl_sessionid_unlock(data); + } + +#ifdef USE_ECH + if(ECH_ENABLED(data)) { + int trying_ech_now = 0; + + if(data->set.str[STRING_ECH_PUBLIC]) { + infof(data, "ECH: outername not (yet) supported with wolfSSL"); + return CURLE_SSL_CONNECT_ERROR; + } + if(data->set.tls_ech == CURLECH_GREASE) { + infof(data, "ECH: GREASE'd ECH not yet supported for wolfSSL"); + return CURLE_SSL_CONNECT_ERROR; + } + if(data->set.tls_ech & CURLECH_CLA_CFG + && data->set.str[STRING_ECH_CONFIG]) { + char *b64val = data->set.str[STRING_ECH_CONFIG]; + word32 b64len = 0; + + b64len = (word32) strlen(b64val); + if(b64len + && wolfSSL_SetEchConfigsBase64(backend->handle, b64val, b64len) + != WOLFSSL_SUCCESS) { + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else { + trying_ech_now = 1; + infof(data, "ECH: ECHConfig from command line"); + } + } + else { + struct Curl_dns_entry *dns = NULL; + + dns = Curl_fetch_addr(data, connssl->peer.hostname, connssl->peer.port); + if(!dns) { + infof(data, "ECH: requested but no DNS info available"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else { + struct Curl_https_rrinfo *rinfo = NULL; + + rinfo = dns->hinfo; + if(rinfo && rinfo->echconfiglist) { + unsigned char *ecl = rinfo->echconfiglist; + size_t elen = rinfo->echconfiglist_len; + + infof(data, "ECH: ECHConfig from DoH HTTPS RR"); + if(wolfSSL_SetEchConfigs(backend->handle, ecl, (word32) elen) != + WOLFSSL_SUCCESS) { + infof(data, "ECH: wolfSSL_SetEchConfigs failed"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else { + trying_ech_now = 1; + infof(data, "ECH: imported ECHConfigList of length %ld", elen); + } + } + else { + infof(data, "ECH: requested but no ECHConfig available"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + Curl_resolv_unlock(data, dns); + } + } + + if(trying_ech_now + && SSL_set_min_proto_version(backend->handle, TLS1_3_VERSION) != 1) { + infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); + return CURLE_SSL_CONNECT_ERROR; + } + + } +#endif /* USE_ECH */ + +#ifdef USE_BIO_CHAIN + { + WOLFSSL_BIO *bio; + + bio = BIO_new(wolfssl_bio_cf_method); + if(!bio) + return CURLE_OUT_OF_MEMORY; + + wolfSSL_BIO_set_data(bio, cf); + wolfSSL_set_bio(backend->handle, bio, bio); + } +#else /* USE_BIO_CHAIN */ + /* pass the raw socket into the SSL layer */ + if(!wolfSSL_set_fd(backend->handle, + (int)Curl_conn_cf_get_socket(cf, data))) { + failf(data, "SSL: SSL_set_fd failed"); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* !USE_BIO_CHAIN */ + + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; +} + + +static char *wolfssl_strerror(unsigned long error, char *buf, + unsigned long size) +{ + DEBUGASSERT(size); + *buf = '\0'; + + wolfSSL_ERR_error_string_n(error, buf, size); + + if(!*buf) { + const char *msg = error ? "Unknown error" : "No error"; + strncpy(buf, msg, size - 1); + buf[size - 1] = '\0'; + } + + return buf; +} + + +static CURLcode +wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + int ret = -1; + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); +#ifndef CURL_DISABLE_PROXY + const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: + data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif + + DEBUGASSERT(backend); + + wolfSSL_ERR_clear_error(); + + /* Enable RFC2818 checks */ + if(conn_config->verifyhost) { + char *snihost = connssl->peer.sni? + connssl->peer.sni : connssl->peer.hostname; + if(wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE) + return CURLE_SSL_CONNECT_ERROR; + } + + if(!backend->x509_store_setup) { + /* After having send off the ClientHello, we prepare the x509 + * store to verify the coming certificate from the server */ + CURLcode result; + result = Curl_wssl_setup_x509_store(cf, data, backend); + if(result) + return result; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + ret = wolfSSL_connect(backend->handle); + +#ifdef OPENSSL_EXTRA + if(Curl_tls_keylog_enabled()) { + /* If key logging is enabled, wait for the handshake to complete and then + * proceed with logging secrets (for TLS 1.2 or older). + * + * During the handshake (ret==-1), wolfSSL_want_read() is true as it waits + * for the server response. At that point the master secret is not yet + * available, so we must not try to read it. + * To log the secret on completion with a handshake failure, detect + * completion via the observation that there is nothing to read or write. + * Note that OpenSSL SSL_want_read() is always true here. If wolfSSL ever + * changes, the worst case is that no key is logged on error. + */ + if(ret == SSL_SUCCESS || + (!wolfSSL_want_read(backend->handle) && + !wolfSSL_want_write(backend->handle))) { + wolfssl_log_tls12_secret(backend->handle); + /* Client Random and master secrets are no longer needed, erase these. + * Ignored while the handshake is still in progress. */ + wolfSSL_FreeArrays(backend->handle); + } + } +#endif /* OPENSSL_EXTRA */ + + if(ret != 1) { + int detail = wolfSSL_get_error(backend->handle, ret); + + if(SSL_ERROR_WANT_READ == detail) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + return CURLE_OK; + } + else if(SSL_ERROR_WANT_WRITE == detail) { + connssl->io_need = CURL_SSL_IO_NEED_SEND; + return CURLE_OK; + } + /* There is no easy way to override only the CN matching. + * This will enable the override of both mismatching SubjectAltNames + * as also mismatching CN fields */ + else if(DOMAIN_NAME_MISMATCH == detail) { +#if 1 + failf(data, " subject alt name(s) or common name do not match \"%s\"", + connssl->peer.dispname); + return CURLE_PEER_FAILED_VERIFICATION; +#else + /* When the wolfssl_check_domain_name() is used and you desire to + * continue on a DOMAIN_NAME_MISMATCH, i.e. 'ssl_config.verifyhost + * == 0', CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA + * error. The only way to do this is currently to switch the + * Wolfssl_check_domain_name() in and out based on the + * 'ssl_config.verifyhost' value. */ + if(conn_config->verifyhost) { + failf(data, + " subject alt name(s) or common name do not match \"%s\"\n", + connssl->dispname); + return CURLE_PEER_FAILED_VERIFICATION; + } + else { + infof(data, + " subject alt name(s) and/or common name do not match \"%s\"", + connssl->dispname); + return CURLE_OK; + } +#endif + } +#if LIBWOLFSSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */ + else if(ASN_NO_SIGNER_E == detail) { + if(conn_config->verifypeer) { + failf(data, " CA signer not available for verification"); + return CURLE_SSL_CACERT_BADFILE; + } + else { + /* Just continue with a warning if no strict certificate + verification is required. */ + infof(data, "CA signer not available for verification, " + "continuing anyway"); + } + } +#endif +#ifdef USE_ECH + else if(-1 == detail) { + /* try access a retry_config ECHConfigList for tracing */ + byte echConfigs[1000]; + word32 echConfigsLen = 1000; + int rv = 0; + + /* this currently does not produce the retry_configs */ + rv = wolfSSL_GetEchConfigs(backend->handle, echConfigs, + &echConfigsLen); + if(rv != WOLFSSL_SUCCESS) { + infof(data, "Failed to get ECHConfigs"); + } + else { + char *b64str = NULL; + size_t blen = 0; + + rv = Curl_base64_encode((const char *)echConfigs, echConfigsLen, + &b64str, &blen); + if(!rv && b64str) + infof(data, "ECH: (not yet) retry_configs %s", b64str); + free(b64str); + } + } +#endif + else if(backend->io_result == CURLE_AGAIN) { + return CURLE_OK; + } + else { + char error_buffer[256]; + failf(data, "SSL_connect failed with error %d: %s", detail, + wolfssl_strerror((unsigned long)detail, error_buffer, + sizeof(error_buffer))); + return CURLE_SSL_CONNECT_ERROR; + } + } + + if(pinnedpubkey) { +#ifdef KEEP_PEER_CERT + X509 *x509; + const char *x509_der; + int x509_der_len; + struct Curl_X509certificate x509_parsed; + struct Curl_asn1Element *pubkey; + CURLcode result; + + x509 = wolfSSL_get_peer_certificate(backend->handle); + if(!x509) { + failf(data, "SSL: failed retrieving server certificate"); + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + } + + x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len); + if(!x509_der) { + failf(data, "SSL: failed retrieving ASN.1 server certificate"); + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + } + + memset(&x509_parsed, 0, sizeof(x509_parsed)); + if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + + pubkey = &x509_parsed.subjectPublicKeyInfo; + if(!pubkey->header || pubkey->end <= pubkey->header) { + failf(data, "SSL: failed retrieving public key from server certificate"); + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + } + + result = Curl_pin_peer_pubkey(data, + pinnedpubkey, + (const unsigned char *)pubkey->header, + (size_t)(pubkey->end - pubkey->header)); + wolfSSL_FreeX509(x509); + if(result) { + failf(data, "SSL: public key does not match pinned public key"); + return result; + } +#else + failf(data, "Library lacks pinning support built-in"); + return CURLE_NOT_BUILT_IN; +#endif + } + +#ifdef HAVE_ALPN + if(connssl->alpn) { + int rc; + char *protocol = NULL; + unsigned short protocol_len = 0; + + rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len); + + if(rc == SSL_SUCCESS) { + Curl_alpn_set_negotiated(cf, data, (const unsigned char *)protocol, + protocol_len); + } + else if(rc == SSL_ALPN_NOT_FOUND) + Curl_alpn_set_negotiated(cf, data, NULL, 0); + else { + failf(data, "ALPN, failure getting protocol, error %d", rc); + return CURLE_SSL_CONNECT_ERROR; + } + } +#endif /* HAVE_ALPN */ + + connssl->connecting_state = ssl_connect_3; +#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010) + infof(data, "SSL connection using %s / %s", + wolfSSL_get_version(backend->handle), + wolfSSL_get_cipher_name(backend->handle)); +#else + infof(data, "SSL connected"); +#endif + + return CURLE_OK; +} + + +static void wolfssl_session_free(void *sessionid, size_t idsize) +{ + (void)idsize; + wolfSSL_SESSION_free(sessionid); +} + + +static CURLcode +wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; + const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + + DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); + + if(ssl_config->primary.cache_session) { + /* wolfSSL_get1_session allocates memory that has to be freed. */ + WOLFSSL_SESSION *our_ssl_sessionid = wolfSSL_get1_session(backend->handle); + + if(our_ssl_sessionid) { + Curl_ssl_sessionid_lock(data); + /* call takes ownership of `our_ssl_sessionid` */ + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, + our_ssl_sessionid, 0, + wolfssl_session_free); + Curl_ssl_sessionid_unlock(data); + if(result) { + failf(data, "failed to store ssl session"); + return result; + } + } + } + + connssl->connecting_state = ssl_connect_done; + + return result; +} + + +static ssize_t wolfssl_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, + size_t len, + CURLcode *curlcode) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; + int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; + int rc; + + DEBUGASSERT(backend); + + wolfSSL_ERR_clear_error(); + + rc = wolfSSL_write(backend->handle, mem, memlen); + if(rc <= 0) { + int err = wolfSSL_get_error(backend->handle, rc); + + switch(err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* there is data pending, re-invoke SSL_write() */ + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len); + *curlcode = CURLE_AGAIN; + return -1; + default: + if(backend->io_result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len); + *curlcode = CURLE_AGAIN; + return -1; + } + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", len, rc, err); + { + char error_buffer[256]; + failf(data, "SSL write: %s, errno %d", + wolfssl_strerror((unsigned long)err, error_buffer, + sizeof(error_buffer)), + SOCKERRNO); + } + *curlcode = CURLE_SEND_ERROR; + return -1; + } + } + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc); + return rc; +} + +static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *wctx = (struct wolfssl_ctx *)connssl->backend; + CURLcode result = CURLE_OK; + char buf[1024]; + int nread, err; + + DEBUGASSERT(wctx); + if(!wctx->handle || cf->shutdown) { + *done = TRUE; + goto out; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + if(!(wolfSSL_get_shutdown(wctx->handle) & SSL_SENT_SHUTDOWN)) { + /* We have not started the shutdown from our side yet. Check + * if the server already sent us one. */ + ERR_clear_error(); + nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf)); + err = wolfSSL_get_error(wctx->handle, nread); + if(!nread && err == SSL_ERROR_ZERO_RETURN) { + bool input_pending; + /* Yes, it did. */ + if(!send_shutdown) { + CURL_TRC_CF(data, cf, "SSL shutdown received, not sending"); + *done = TRUE; + goto out; + } + else if(!cf->next->cft->is_alive(cf->next, data, &input_pending)) { + /* Server closed the connection after its closy notify. It + * seems not interested to see our close notify, so do not + * send it. We are done. */ + CURL_TRC_CF(data, cf, "peer closed connection"); + connssl->peer_closed = TRUE; + *done = TRUE; + goto out; + } + } + } + + if(send_shutdown && wolfSSL_shutdown(wctx->handle) == 1) { + CURL_TRC_CF(data, cf, "SSL shutdown finished"); + *done = TRUE; + goto out; + } + else { + size_t i; + /* SSL should now have started the shutdown from our side. Since it + * was not complete, we are lacking the close notify from the server. */ + for(i = 0; i < 10; ++i) { + ERR_clear_error(); + nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf)); + if(nread <= 0) + break; + } + err = wolfSSL_get_error(wctx->handle, nread); + switch(err) { + case SSL_ERROR_ZERO_RETURN: /* no more data */ + CURL_TRC_CF(data, cf, "SSL shutdown received"); + *done = TRUE; + break; + case SSL_ERROR_NONE: /* just did not get anything */ + case SSL_ERROR_WANT_READ: + /* SSL has send its notify and now wants to read the reply + * from the server. We are not really interested in that. */ + CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive"); + connssl->io_need = CURL_SSL_IO_NEED_RECV; + break; + case SSL_ERROR_WANT_WRITE: + CURL_TRC_CF(data, cf, "SSL shutdown send blocked"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; + break; + default: { + char error_buffer[256]; + int detail = wolfSSL_get_error(wctx->handle, err); + CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)", + wolfssl_strerror((unsigned long)err, error_buffer, + sizeof(error_buffer)), + detail); + result = CURLE_RECV_ERROR; + break; + } + } + } + +out: + cf->shutdown = (result || *done); + return result; +} + +static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; + + (void) data; + + DEBUGASSERT(backend); + + if(backend->handle) { + wolfSSL_free(backend->handle); + backend->handle = NULL; + } + if(backend->ctx) { + wolfSSL_CTX_free(backend->ctx); + backend->ctx = NULL; + } +} + +static ssize_t wolfssl_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t blen, + CURLcode *curlcode) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; + int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen; + int nread; + + DEBUGASSERT(backend); + + wolfSSL_ERR_clear_error(); + *curlcode = CURLE_OK; + + nread = wolfSSL_read(backend->handle, buf, buffsize); + + if(nread <= 0) { + int err = wolfSSL_get_error(backend->handle, nread); + + switch(err) { + case SSL_ERROR_ZERO_RETURN: /* no more data */ + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen); + *curlcode = CURLE_OK; + return 0; + case SSL_ERROR_NONE: + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* there is data pending, re-invoke wolfSSL_read() */ + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen); + *curlcode = CURLE_AGAIN; + return -1; + default: + if(backend->io_result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen); + *curlcode = CURLE_AGAIN; + return -1; + } + { + char error_buffer[256]; + failf(data, "SSL read: %s, errno %d", + wolfssl_strerror((unsigned long)err, error_buffer, + sizeof(error_buffer)), + SOCKERRNO); + } + *curlcode = CURLE_RECV_ERROR; + return -1; + } + } + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread); + return nread; +} + + +static size_t wolfssl_version(char *buffer, size_t size) +{ +#if LIBWOLFSSL_VERSION_HEX >= 0x03006000 + return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version()); +#elif defined(WOLFSSL_VERSION) + return msnprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION); +#endif +} + + +static int wolfssl_init(void) +{ + int ret; + +#ifdef OPENSSL_EXTRA + Curl_tls_keylog_open(); +#endif + ret = (wolfSSL_Init() == SSL_SUCCESS); + wolfssl_bio_cf_init_methods(); + return ret; +} + + +static void wolfssl_cleanup(void) +{ + wolfssl_bio_cf_free_methods(); + wolfSSL_Cleanup(); +#ifdef OPENSSL_EXTRA + Curl_tls_keylog_close(); +#endif +} + + +static bool wolfssl_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct ssl_connect_data *ctx = cf->ctx; + struct wolfssl_ctx *backend; + + (void)data; + DEBUGASSERT(ctx && ctx->backend); + + backend = (struct wolfssl_ctx *)ctx->backend; + if(backend->handle) /* SSL is in use */ + return (0 != wolfSSL_pending(backend->handle)) ? TRUE : FALSE; + else + return FALSE; +} + +static CURLcode +wolfssl_connect_common(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool nonblocking, + bool *done) +{ + CURLcode result; + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + int what; + + /* check if the connection has already been established */ + if(ssl_connection_complete == connssl->state) { + *done = TRUE; + return CURLE_OK; + } + + if(ssl_connect_1 == connssl->connecting_state) { + /* Find out how much more time we are allowed */ + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + result = wolfssl_connect_step1(cf, data); + if(result) + return result; + } + + while(ssl_connect_2 == connssl->connecting_state) { + + /* check allowed time left */ + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { + + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; + + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + nonblocking?0:timeout_ms); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + return CURLE_SSL_CONNECT_ERROR; + } + else if(0 == what) { + if(nonblocking) { + *done = FALSE; + return CURLE_OK; + } + else { + /* timeout */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + } + /* socket is readable or writable */ + } + + /* Run transaction, and return to the caller if it failed or if + * this connection is part of a multi handle and this loop would + * execute again. This permits the owner of a multi handle to + * abort a connection attempt before step2 has completed while + * ensuring that a client using select() or epoll() will always + * have a valid fdset to wait on. + */ + result = wolfssl_connect_step2(cf, data); + if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) + return result; + } /* repeat step2 until all transactions are done. */ + + if(ssl_connect_3 == connssl->connecting_state) { + result = wolfssl_connect_step3(cf, data); + if(result) + return result; + } + + if(ssl_connect_done == connssl->connecting_state) { + connssl->state = ssl_connection_complete; + *done = TRUE; + } + else + *done = FALSE; + + /* Reset our connect state machine */ + connssl->connecting_state = ssl_connect_1; + + return CURLE_OK; +} + + +static CURLcode wolfssl_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + return wolfssl_connect_common(cf, data, TRUE, done); +} + + +static CURLcode wolfssl_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode result; + bool done = FALSE; + + result = wolfssl_connect_common(cf, data, FALSE, &done); + if(result) + return result; + + DEBUGASSERT(done); + + return CURLE_OK; +} + +static CURLcode wolfssl_random(struct Curl_easy *data, + unsigned char *entropy, size_t length) +{ + WC_RNG rng; + (void)data; + if(wc_InitRng(&rng)) + return CURLE_FAILED_INIT; + if(length > UINT_MAX) + return CURLE_FAILED_INIT; + if(wc_RNG_GenerateBlock(&rng, entropy, (unsigned)length)) + return CURLE_FAILED_INIT; + if(wc_FreeRng(&rng)) + return CURLE_FAILED_INIT; + return CURLE_OK; +} + +static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum /* output */, + size_t unused) +{ + wc_Sha256 SHA256pw; + (void)unused; + if(wc_InitSha256(&SHA256pw)) + return CURLE_FAILED_INIT; + wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen); + wc_Sha256Final(&SHA256pw, sha256sum); + return CURLE_OK; +} + +static void *wolfssl_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) +{ + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; + (void)info; + DEBUGASSERT(backend); + return backend->handle; +} + +const struct Curl_ssl Curl_ssl_wolfssl = { + { CURLSSLBACKEND_WOLFSSL, "wolfssl" }, /* info */ + +#ifdef KEEP_PEER_CERT + SSLSUPP_PINNEDPUBKEY | +#endif +#ifdef USE_BIO_CHAIN + SSLSUPP_HTTPS_PROXY | +#endif + SSLSUPP_CA_PATH | + SSLSUPP_CAINFO_BLOB | +#ifdef USE_ECH + SSLSUPP_ECH | +#endif + SSLSUPP_SSL_CTX | + SSLSUPP_CA_CACHE, + + sizeof(struct wolfssl_ctx), + + wolfssl_init, /* init */ + wolfssl_cleanup, /* cleanup */ + wolfssl_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + wolfssl_shutdown, /* shutdown */ + wolfssl_data_pending, /* data_pending */ + wolfssl_random, /* random */ + Curl_none_cert_status_request, /* cert_status_request */ + wolfssl_connect, /* connect */ + wolfssl_connect_nonblocking, /* connect_nonblocking */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ + wolfssl_get_internals, /* get_internals */ + wolfssl_close, /* close_one */ + Curl_none_close_all, /* close_all */ + Curl_none_set_engine, /* set_engine */ + Curl_none_set_engine_default, /* set_engine_default */ + Curl_none_engines_list, /* engines_list */ + Curl_none_false_start, /* false_start */ + wolfssl_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ + NULL, /* disassociate_connection */ + wolfssl_recv, /* recv decrypted data */ + wolfssl_send, /* send data to encrypt */ +}; + +#endif diff --git a/third_party/curl/lib/vtls/wolfssl.h b/third_party/curl/lib/vtls/wolfssl.h new file mode 100644 index 0000000000..d75bdaa1e0 --- /dev/null +++ b/third_party/curl/lib/vtls/wolfssl.h @@ -0,0 +1,50 @@ +#ifndef HEADER_CURL_WOLFSSL_H +#define HEADER_CURL_WOLFSSL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_WOLFSSL +#include +#include +#include +#include + +#include "urldata.h" + +extern const struct Curl_ssl Curl_ssl_wolfssl; + +struct wolfssl_ctx { + WOLFSSL_CTX *ctx; + WOLFSSL *handle; + CURLcode io_result; /* result of last BIO cfilter operation */ + BIT(x509_store_setup); /* x509 store has been set up */ +}; + +CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wssl); + +#endif /* USE_WOLFSSL */ +#endif /* HEADER_CURL_WOLFSSL_H */ diff --git a/third_party/curl/lib/vtls/x509asn1.c b/third_party/curl/lib/vtls/x509asn1.c new file mode 100644 index 0000000000..7f04af3b97 --- /dev/null +++ b/third_party/curl/lib/vtls/x509asn1.c @@ -0,0 +1,1263 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ + defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_MBEDTLS) + +#if defined(USE_WOLFSSL) || defined(USE_SCHANNEL) +#define WANT_PARSEX509 /* uses Curl_parseX509() */ +#endif + +#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_MBEDTLS) +#define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ +#define WANT_PARSEX509 /* ... uses Curl_parseX509() */ +#endif + +#include +#include "urldata.h" +#include "strcase.h" +#include "curl_ctype.h" +#include "hostcheck.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "sendf.h" +#include "inet_pton.h" +#include "curl_base64.h" +#include "x509asn1.h" +#include "dynbuf.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Constants. + */ + +/* Largest supported ASN.1 structure. */ +#define CURL_ASN1_MAX ((size_t) 0x40000) /* 256K */ + +/* ASN.1 classes. */ +#define CURL_ASN1_UNIVERSAL 0 +#define CURL_ASN1_APPLICATION 1 +#define CURL_ASN1_CONTEXT_SPECIFIC 2 +#define CURL_ASN1_PRIVATE 3 + +/* ASN.1 types. */ +#define CURL_ASN1_BOOLEAN 1 +#define CURL_ASN1_INTEGER 2 +#define CURL_ASN1_BIT_STRING 3 +#define CURL_ASN1_OCTET_STRING 4 +#define CURL_ASN1_NULL 5 +#define CURL_ASN1_OBJECT_IDENTIFIER 6 +#define CURL_ASN1_OBJECT_DESCRIPTOR 7 +#define CURL_ASN1_INSTANCE_OF 8 +#define CURL_ASN1_REAL 9 +#define CURL_ASN1_ENUMERATED 10 +#define CURL_ASN1_EMBEDDED 11 +#define CURL_ASN1_UTF8_STRING 12 +#define CURL_ASN1_RELATIVE_OID 13 +#define CURL_ASN1_SEQUENCE 16 +#define CURL_ASN1_SET 17 +#define CURL_ASN1_NUMERIC_STRING 18 +#define CURL_ASN1_PRINTABLE_STRING 19 +#define CURL_ASN1_TELETEX_STRING 20 +#define CURL_ASN1_VIDEOTEX_STRING 21 +#define CURL_ASN1_IA5_STRING 22 +#define CURL_ASN1_UTC_TIME 23 +#define CURL_ASN1_GENERALIZED_TIME 24 +#define CURL_ASN1_GRAPHIC_STRING 25 +#define CURL_ASN1_VISIBLE_STRING 26 +#define CURL_ASN1_GENERAL_STRING 27 +#define CURL_ASN1_UNIVERSAL_STRING 28 +#define CURL_ASN1_CHARACTER_STRING 29 +#define CURL_ASN1_BMP_STRING 30 + +/* Max sixes */ + +#define MAX_X509_STR 10000 +#define MAX_X509_CERT 100000 + +#ifdef WANT_EXTRACT_CERTINFO +/* ASN.1 OID table entry. */ +struct Curl_OID { + const char *numoid; /* Dotted-numeric OID. */ + const char *textoid; /* OID name. */ +}; + +/* ASN.1 OIDs. */ +static const struct Curl_OID OIDtable[] = { + { "1.2.840.10040.4.1", "dsa" }, + { "1.2.840.10040.4.3", "dsa-with-sha1" }, + { "1.2.840.10045.2.1", "ecPublicKey" }, + { "1.2.840.10045.3.0.1", "c2pnb163v1" }, + { "1.2.840.10045.4.1", "ecdsa-with-SHA1" }, + { "1.2.840.10045.4.3.1", "ecdsa-with-SHA224" }, + { "1.2.840.10045.4.3.2", "ecdsa-with-SHA256" }, + { "1.2.840.10045.4.3.3", "ecdsa-with-SHA384" }, + { "1.2.840.10045.4.3.4", "ecdsa-with-SHA512" }, + { "1.2.840.10046.2.1", "dhpublicnumber" }, + { "1.2.840.113549.1.1.1", "rsaEncryption" }, + { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, + { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" }, + { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" }, + { "1.2.840.113549.1.1.10", "RSASSA-PSS" }, + { "1.2.840.113549.1.1.14", "sha224WithRSAEncryption" }, + { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" }, + { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" }, + { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" }, + { "1.2.840.113549.2.2", "md2" }, + { "1.2.840.113549.2.5", "md5" }, + { "1.3.14.3.2.26", "sha1" }, + { "2.5.4.3", "CN" }, + { "2.5.4.4", "SN" }, + { "2.5.4.5", "serialNumber" }, + { "2.5.4.6", "C" }, + { "2.5.4.7", "L" }, + { "2.5.4.8", "ST" }, + { "2.5.4.9", "streetAddress" }, + { "2.5.4.10", "O" }, + { "2.5.4.11", "OU" }, + { "2.5.4.12", "title" }, + { "2.5.4.13", "description" }, + { "2.5.4.17", "postalCode" }, + { "2.5.4.41", "name" }, + { "2.5.4.42", "givenName" }, + { "2.5.4.43", "initials" }, + { "2.5.4.44", "generationQualifier" }, + { "2.5.4.45", "X500UniqueIdentifier" }, + { "2.5.4.46", "dnQualifier" }, + { "2.5.4.65", "pseudonym" }, + { "1.2.840.113549.1.9.1", "emailAddress" }, + { "2.5.4.72", "role" }, + { "2.5.29.17", "subjectAltName" }, + { "2.5.29.18", "issuerAltName" }, + { "2.5.29.19", "basicConstraints" }, + { "2.16.840.1.101.3.4.2.4", "sha224" }, + { "2.16.840.1.101.3.4.2.1", "sha256" }, + { "2.16.840.1.101.3.4.2.2", "sha384" }, + { "2.16.840.1.101.3.4.2.3", "sha512" }, + { "1.2.840.113549.1.9.2", "unstructuredName" }, + { (const char *) NULL, (const char *) NULL } +}; + +#endif /* WANT_EXTRACT_CERTINFO */ + +/* + * Lightweight ASN.1 parser. + * In particular, it does not check for syntactic/lexical errors. + * It is intended to support certificate information gathering for SSL backends + * that offer a mean to get certificates as a whole, but do not supply + * entry points to get particular certificate sub-fields. + * Please note there is no pretension here to rewrite a full SSL library. + */ + +static const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) + WARN_UNUSED_RESULT; + +static const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) +{ + unsigned char b; + size_t len; + struct Curl_asn1Element lelem; + + /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg' + ending at `end'. + Returns a pointer in source string after the parsed element, or NULL + if an error occurs. */ + if(!beg || !end || beg >= end || !*beg || + (size_t)(end - beg) > CURL_ASN1_MAX) + return NULL; + + /* Process header byte. */ + elem->header = beg; + b = (unsigned char) *beg++; + elem->constructed = (b & 0x20) != 0; + elem->class = (b >> 6) & 3; + b &= 0x1F; + if(b == 0x1F) + return NULL; /* Long tag values not supported here. */ + elem->tag = b; + + /* Process length. */ + if(beg >= end) + return NULL; + b = (unsigned char) *beg++; + if(!(b & 0x80)) + len = b; + else if(!(b &= 0x7F)) { + /* Unspecified length. Since we have all the data, we can determine the + effective length by skipping element until an end element is found. */ + if(!elem->constructed) + return NULL; + elem->beg = beg; + while(beg < end && *beg) { + beg = getASN1Element(&lelem, beg, end); + if(!beg) + return NULL; + } + if(beg >= end) + return NULL; + elem->end = beg; + return beg + 1; + } + else if((unsigned)b > (size_t)(end - beg)) + return NULL; /* Does not fit in source. */ + else { + /* Get long length. */ + len = 0; + do { + if(len & 0xFF000000L) + return NULL; /* Lengths > 32 bits are not supported. */ + len = (len << 8) | (unsigned char) *beg++; + } while(--b); + } + if(len > (size_t)(end - beg)) + return NULL; /* Element data does not fit in source. */ + elem->beg = beg; + elem->end = beg + len; + return elem->end; +} + +#ifdef WANT_EXTRACT_CERTINFO + +/* + * Search the null terminated OID or OID identifier in local table. + * Return the table entry pointer or NULL if not found. + */ +static const struct Curl_OID *searchOID(const char *oid) +{ + const struct Curl_OID *op; + for(op = OIDtable; op->numoid; op++) + if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid)) + return op; + + return NULL; +} + +/* + * Convert an ASN.1 Boolean value into its string representation. + * + * Return error code. + */ + +static CURLcode bool2str(struct dynbuf *store, + const char *beg, const char *end) +{ + if(end - beg != 1) + return CURLE_BAD_FUNCTION_ARGUMENT; + return Curl_dyn_add(store, *beg? "TRUE": "FALSE"); +} + +/* + * Convert an ASN.1 octet string to a printable string. + * + * Return error code. + */ +static CURLcode octet2str(struct dynbuf *store, + const char *beg, const char *end) +{ + CURLcode result = CURLE_OK; + + while(!result && beg < end) + result = Curl_dyn_addf(store, "%02x:", (unsigned char) *beg++); + + return result; +} + +static CURLcode bit2str(struct dynbuf *store, + const char *beg, const char *end) +{ + /* Convert an ASN.1 bit string to a printable string. */ + + if(++beg > end) + return CURLE_BAD_FUNCTION_ARGUMENT; + return octet2str(store, beg, end); +} + +/* + * Convert an ASN.1 integer value into its string representation. + * + * Returns error. + */ +static CURLcode int2str(struct dynbuf *store, + const char *beg, const char *end) +{ + unsigned int val = 0; + size_t n = end - beg; + + if(!n) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(n > 4) + return octet2str(store, beg, end); + + /* Represent integers <= 32-bit as a single value. */ + if(*beg & 0x80) + val = ~val; + + do + val = (val << 8) | *(const unsigned char *) beg++; + while(beg < end); + return Curl_dyn_addf(store, "%s%x", val >= 10? "0x": "", val); +} + +/* + * Convert from an ASN.1 typed string to UTF8. + * + * The result is stored in a dynbuf that is inited by the user of this + * function. + * + * Returns error. + */ +static CURLcode +utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end) +{ + size_t inlength = end - from; + int size = 1; + CURLcode result = CURLE_OK; + + switch(type) { + case CURL_ASN1_BMP_STRING: + size = 2; + break; + case CURL_ASN1_UNIVERSAL_STRING: + size = 4; + break; + case CURL_ASN1_NUMERIC_STRING: + case CURL_ASN1_PRINTABLE_STRING: + case CURL_ASN1_TELETEX_STRING: + case CURL_ASN1_IA5_STRING: + case CURL_ASN1_VISIBLE_STRING: + case CURL_ASN1_UTF8_STRING: + break; + default: + return CURLE_BAD_FUNCTION_ARGUMENT; /* Conversion not supported. */ + } + + if(inlength % size) + /* Length inconsistent with character size. */ + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(type == CURL_ASN1_UTF8_STRING) { + /* Just copy. */ + if(inlength) + result = Curl_dyn_addn(to, from, inlength); + } + else { + while(!result && (from < end)) { + char buf[4]; /* decode buffer */ + size_t charsize = 1; + unsigned int wc = 0; + + switch(size) { + case 4: + wc = (wc << 8) | *(const unsigned char *) from++; + wc = (wc << 8) | *(const unsigned char *) from++; + FALLTHROUGH(); + case 2: + wc = (wc << 8) | *(const unsigned char *) from++; + FALLTHROUGH(); + default: /* case 1: */ + wc = (wc << 8) | *(const unsigned char *) from++; + } + if(wc >= 0x00000080) { + if(wc >= 0x00000800) { + if(wc >= 0x00010000) { + if(wc >= 0x00200000) { + /* Invalid char. size for target encoding. */ + return CURLE_WEIRD_SERVER_REPLY; + } + buf[3] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x00010000; + charsize++; + } + buf[2] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x00000800; + charsize++; + } + buf[1] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x000000C0; + charsize++; + } + buf[0] = (char) wc; + result = Curl_dyn_addn(to, buf, charsize); + } + } + return result; +} + +/* + * Convert an ASN.1 OID into its dotted string representation. + * + * Return error code. + */ +static CURLcode encodeOID(struct dynbuf *store, + const char *beg, const char *end) +{ + unsigned int x; + unsigned int y; + CURLcode result = CURLE_OK; + + /* Process the first two numbers. */ + y = *(const unsigned char *) beg++; + x = y / 40; + y -= x * 40; + + result = Curl_dyn_addf(store, "%u.%u", x, y); + if(result) + return result; + + /* Process the trailing numbers. */ + while(beg < end) { + x = 0; + do { + if(x & 0xFF000000) + return 0; + y = *(const unsigned char *) beg++; + x = (x << 7) | (y & 0x7F); + } while(y & 0x80); + result = Curl_dyn_addf(store, ".%u", x); + } + return result; +} + +/* + * Convert an ASN.1 OID into its dotted or symbolic string representation. + * + * Return error code. + */ + +static CURLcode OID2str(struct dynbuf *store, + const char *beg, const char *end, bool symbolic) +{ + CURLcode result = CURLE_OK; + if(beg < end) { + if(symbolic) { + struct dynbuf buf; + Curl_dyn_init(&buf, MAX_X509_STR); + result = encodeOID(&buf, beg, end); + + if(!result) { + const struct Curl_OID *op = searchOID(Curl_dyn_ptr(&buf)); + if(op) + result = Curl_dyn_add(store, op->textoid); + else + result = Curl_dyn_add(store, Curl_dyn_ptr(&buf)); + Curl_dyn_free(&buf); + } + } + else + result = encodeOID(store, beg, end); + } + return result; +} + +static CURLcode GTime2str(struct dynbuf *store, + const char *beg, const char *end) +{ + const char *tzp; + const char *fracp; + char sec1, sec2; + size_t fracl; + size_t tzl; + const char *sep = ""; + + /* Convert an ASN.1 Generalized time to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + for(fracp = beg; fracp < end && ISDIGIT(*fracp); fracp++) + ; + + /* Get seconds digits. */ + sec1 = '0'; + switch(fracp - beg - 12) { + case 0: + sec2 = '0'; + break; + case 2: + sec1 = fracp[-2]; + FALLTHROUGH(); + case 1: + sec2 = fracp[-1]; + break; + default: + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + /* timezone follows optional fractional seconds. */ + tzp = fracp; + fracl = 0; /* no fractional seconds detected so far */ + if(fracp < end && (*fracp == '.' || *fracp == ',')) { + /* Have fractional seconds, e.g. "[.,]\d+". How many? */ + fracp++; /* should be a digit char or BAD ARGUMENT */ + tzp = fracp; + while(tzp < end && ISDIGIT(*tzp)) + tzp++; + if(tzp == fracp) /* never looped, no digit after [.,] */ + return CURLE_BAD_FUNCTION_ARGUMENT; + fracl = tzp - fracp; /* number of fractional sec digits */ + DEBUGASSERT(fracl > 0); + /* Strip trailing zeroes in fractional seconds. + * May reduce fracl to 0 if only '0's are present. */ + while(fracl && fracp[fracl - 1] == '0') + fracl--; + } + + /* Process timezone. */ + if(tzp >= end) { + tzp = ""; + tzl = 0; + } + else if(*tzp == 'Z') { + sep = " "; + tzp = "GMT"; + tzl = 3; + } + else if((*tzp == '+') || (*tzp == '-')) { + sep = " UTC"; + tzl = end - tzp; + } + else { + sep = " "; + tzl = end - tzp; + } + + return Curl_dyn_addf(store, + "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", + beg, beg + 4, beg + 6, + beg + 8, beg + 10, sec1, sec2, + fracl? ".": "", (int)fracl, fracp, + sep, (int)tzl, tzp); +} + +#ifdef UNITTESTS +/* used by unit1656.c */ +CURLcode Curl_x509_GTime2str(struct dynbuf *store, + const char *beg, const char *end) +{ + return GTime2str(store, beg, end); +} +#endif + +/* + * Convert an ASN.1 UTC time to a printable string. + * + * Return error code. + */ +static CURLcode UTime2str(struct dynbuf *store, + const char *beg, const char *end) +{ + const char *tzp; + size_t tzl; + const char *sec; + + for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++) + ; + /* Get the seconds. */ + sec = beg + 10; + switch(tzp - sec) { + case 0: + sec = "00"; + FALLTHROUGH(); + case 2: + break; + default: + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + /* Process timezone. */ + if(tzp >= end) + return CURLE_BAD_FUNCTION_ARGUMENT; + if(*tzp == 'Z') { + tzp = "GMT"; + end = tzp + 3; + } + else + tzp++; + + tzl = end - tzp; + return Curl_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", + 20 - (*beg >= '5'), beg, beg + 2, beg + 4, + beg + 6, beg + 8, sec, + (int)tzl, tzp); +} + +/* + * Convert an ASN.1 element to a printable string. + * + * Return error + */ +static CURLcode ASN1tostr(struct dynbuf *store, + struct Curl_asn1Element *elem, int type) +{ + CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; + if(elem->constructed) + return result; /* No conversion of structured elements. */ + + if(!type) + type = elem->tag; /* Type not forced: use element tag as type. */ + + switch(type) { + case CURL_ASN1_BOOLEAN: + result = bool2str(store, elem->beg, elem->end); + break; + case CURL_ASN1_INTEGER: + case CURL_ASN1_ENUMERATED: + result = int2str(store, elem->beg, elem->end); + break; + case CURL_ASN1_BIT_STRING: + result = bit2str(store, elem->beg, elem->end); + break; + case CURL_ASN1_OCTET_STRING: + result = octet2str(store, elem->beg, elem->end); + break; + case CURL_ASN1_NULL: + result = Curl_dyn_addn(store, "", 1); + break; + case CURL_ASN1_OBJECT_IDENTIFIER: + result = OID2str(store, elem->beg, elem->end, TRUE); + break; + case CURL_ASN1_UTC_TIME: + result = UTime2str(store, elem->beg, elem->end); + break; + case CURL_ASN1_GENERALIZED_TIME: + result = GTime2str(store, elem->beg, elem->end); + break; + case CURL_ASN1_UTF8_STRING: + case CURL_ASN1_NUMERIC_STRING: + case CURL_ASN1_PRINTABLE_STRING: + case CURL_ASN1_TELETEX_STRING: + case CURL_ASN1_IA5_STRING: + case CURL_ASN1_VISIBLE_STRING: + case CURL_ASN1_UNIVERSAL_STRING: + case CURL_ASN1_BMP_STRING: + result = utf8asn1str(store, type, elem->beg, elem->end); + break; + } + + return result; +} + +/* + * ASCII encode distinguished name at `dn' into the store dynbuf. + * + * Returns error. + */ +static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn) +{ + struct Curl_asn1Element rdn; + struct Curl_asn1Element atv; + struct Curl_asn1Element oid; + struct Curl_asn1Element value; + const char *p1; + const char *p2; + const char *p3; + const char *str; + CURLcode result = CURLE_OK; + bool added = FALSE; + struct dynbuf temp; + Curl_dyn_init(&temp, MAX_X509_STR); + + for(p1 = dn->beg; p1 < dn->end;) { + p1 = getASN1Element(&rdn, p1, dn->end); + if(!p1) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto error; + } + for(p2 = rdn.beg; p2 < rdn.end;) { + p2 = getASN1Element(&atv, p2, rdn.end); + if(!p2) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto error; + } + p3 = getASN1Element(&oid, atv.beg, atv.end); + if(!p3) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto error; + } + if(!getASN1Element(&value, p3, atv.end)) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto error; + } + Curl_dyn_reset(&temp); + result = ASN1tostr(&temp, &oid, 0); + if(result) + goto error; + + str = Curl_dyn_ptr(&temp); + + if(!str) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto error; + } + + /* Encode delimiter. + If attribute has a short uppercase name, delimiter is ", ". */ + for(p3 = str; ISUPPER(*p3); p3++) + ; + if(added) { + if(p3 - str > 2) + result = Curl_dyn_addn(store, "/", 1); + else + result = Curl_dyn_addn(store, ", ", 2); + if(result) + goto error; + } + + /* Encode attribute name. */ + result = Curl_dyn_add(store, str); + if(result) + goto error; + + /* Generate equal sign. */ + result = Curl_dyn_addn(store, "=", 1); + if(result) + goto error; + + /* Generate value. */ + result = ASN1tostr(store, &value, 0); + if(result) + goto error; + Curl_dyn_reset(&temp); + added = TRUE; /* use separator for next */ + } + } +error: + Curl_dyn_free(&temp); + + return result; +} + +#endif /* WANT_EXTRACT_CERTINFO */ + +#ifdef WANT_PARSEX509 +/* + * ASN.1 parse an X509 certificate into structure subfields. + * Syntax is assumed to have already been checked by the SSL backend. + * See RFC 5280. + */ +int Curl_parseX509(struct Curl_X509certificate *cert, + const char *beg, const char *end) +{ + struct Curl_asn1Element elem; + struct Curl_asn1Element tbsCertificate; + const char *ccp; + static const char defaultVersion = 0; /* v1. */ + + cert->certificate.header = NULL; + cert->certificate.beg = beg; + cert->certificate.end = end; + + /* Get the sequence content. */ + if(!getASN1Element(&elem, beg, end)) + return -1; /* Invalid bounds/size. */ + beg = elem.beg; + end = elem.end; + + /* Get tbsCertificate. */ + beg = getASN1Element(&tbsCertificate, beg, end); + if(!beg) + return -1; + /* Skip the signatureAlgorithm. */ + beg = getASN1Element(&cert->signatureAlgorithm, beg, end); + if(!beg) + return -1; + /* Get the signatureValue. */ + if(!getASN1Element(&cert->signature, beg, end)) + return -1; + + /* Parse TBSCertificate. */ + beg = tbsCertificate.beg; + end = tbsCertificate.end; + /* Get optional version, get serialNumber. */ + cert->version.header = NULL; + cert->version.beg = &defaultVersion; + cert->version.end = &defaultVersion + sizeof(defaultVersion); + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + if(elem.tag == 0) { + if(!getASN1Element(&cert->version, elem.beg, elem.end)) + return -1; + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + cert->serialNumber = elem; + /* Get signature algorithm. */ + beg = getASN1Element(&cert->signatureAlgorithm, beg, end); + /* Get issuer. */ + beg = getASN1Element(&cert->issuer, beg, end); + if(!beg) + return -1; + /* Get notBefore and notAfter. */ + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + ccp = getASN1Element(&cert->notBefore, elem.beg, elem.end); + if(!ccp) + return -1; + if(!getASN1Element(&cert->notAfter, ccp, elem.end)) + return -1; + /* Get subject. */ + beg = getASN1Element(&cert->subject, beg, end); + if(!beg) + return -1; + /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */ + beg = getASN1Element(&cert->subjectPublicKeyInfo, beg, end); + if(!beg) + return -1; + ccp = getASN1Element(&cert->subjectPublicKeyAlgorithm, + cert->subjectPublicKeyInfo.beg, + cert->subjectPublicKeyInfo.end); + if(!ccp) + return -1; + if(!getASN1Element(&cert->subjectPublicKey, ccp, + cert->subjectPublicKeyInfo.end)) + return -1; + /* Get optional issuerUiqueID, subjectUniqueID and extensions. */ + cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0; + cert->extensions.tag = elem.tag = 0; + cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL; + cert->issuerUniqueID.beg = cert->issuerUniqueID.end = ""; + cert->subjectUniqueID.beg = cert->subjectUniqueID.end = ""; + cert->extensions.header = NULL; + cert->extensions.beg = cert->extensions.end = ""; + if(beg < end) { + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + if(elem.tag == 1) { + cert->issuerUniqueID = elem; + if(beg < end) { + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + } + if(elem.tag == 2) { + cert->subjectUniqueID = elem; + if(beg < end) { + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + } + if(elem.tag == 3) + if(!getASN1Element(&cert->extensions, elem.beg, elem.end)) + return -1; + return 0; +} + +#endif /* WANT_PARSEX509 */ + +#ifdef WANT_EXTRACT_CERTINFO + +static CURLcode dumpAlgo(struct dynbuf *store, + struct Curl_asn1Element *param, + const char *beg, const char *end) +{ + struct Curl_asn1Element oid; + + /* Get algorithm parameters and return algorithm name. */ + + beg = getASN1Element(&oid, beg, end); + if(!beg) + return CURLE_BAD_FUNCTION_ARGUMENT; + param->header = NULL; + param->tag = 0; + param->beg = param->end = end; + if(beg < end) { + const char *p = getASN1Element(param, beg, end); + if(!p) + return CURLE_BAD_FUNCTION_ARGUMENT; + } + return OID2str(store, oid.beg, oid.end, TRUE); +} + +/* + * This is a convenience function for push_certinfo_len that takes a zero + * terminated value. + */ +static CURLcode ssl_push_certinfo(struct Curl_easy *data, + int certnum, + const char *label, + const char *value) +{ + size_t valuelen = strlen(value); + + return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen); +} + +/* + * This is a convenience function for push_certinfo_len that takes a + * dynbuf value. + * + * It also does the verbose output if !certnum. + */ +static CURLcode ssl_push_certinfo_dyn(struct Curl_easy *data, + int certnum, + const char *label, + struct dynbuf *ptr) +{ + size_t valuelen = Curl_dyn_len(ptr); + char *value = Curl_dyn_ptr(ptr); + + CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label, + value, valuelen); + + if(!certnum && !result) + infof(data, " %s: %s", label, value); + + return result; +} + +static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum, + const char *label, + struct Curl_asn1Element *elem) +{ + CURLcode result; + struct dynbuf out; + + Curl_dyn_init(&out, MAX_X509_STR); + + /* Generate a certificate information record for the public key. */ + + result = ASN1tostr(&out, elem, 0); + if(!result) { + if(data->set.ssl.certinfo) + result = ssl_push_certinfo_dyn(data, certnum, label, &out); + Curl_dyn_free(&out); + } + return result; +} + +/* return 0 on success, 1 on error */ +static int do_pubkey(struct Curl_easy *data, int certnum, + const char *algo, struct Curl_asn1Element *param, + struct Curl_asn1Element *pubkey) +{ + struct Curl_asn1Element elem; + struct Curl_asn1Element pk; + const char *p; + + /* Generate all information records for the public key. */ + + if(strcasecompare(algo, "ecPublicKey")) { + /* + * ECC public key is all the data, a value of type BIT STRING mapped to + * OCTET STRING and should not be parsed as an ASN.1 value. + */ + const size_t len = ((pubkey->end - pubkey->beg - 2) * 4); + if(!certnum) + infof(data, " ECC Public Key (%zu bits)", len); + if(data->set.ssl.certinfo) { + char q[sizeof(len) * 8 / 3 + 1]; + (void)msnprintf(q, sizeof(q), "%zu", len); + if(ssl_push_certinfo(data, certnum, "ECC Public Key", q)) + return 1; + } + return do_pubkey_field(data, certnum, "ecPublicKey", pubkey) == CURLE_OK + ? 0 : 1; + } + + /* Get the public key (single element). */ + if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end)) + return 1; + + if(strcasecompare(algo, "rsaEncryption")) { + const char *q; + size_t len; + + p = getASN1Element(&elem, pk.beg, pk.end); + if(!p) + return 1; + + /* Compute key length. */ + for(q = elem.beg; !*q && q < elem.end; q++) + ; + len = ((elem.end - q) * 8); + if(len) { + unsigned int i; + for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1) + len--; + } + if(len > 32) + elem.beg = q; /* Strip leading zero bytes. */ + if(!certnum) + infof(data, " RSA Public Key (%zu bits)", len); + if(data->set.ssl.certinfo) { + char r[sizeof(len) * 8 / 3 + 1]; + msnprintf(r, sizeof(r), "%zu", len); + if(ssl_push_certinfo(data, certnum, "RSA Public Key", r)) + return 1; + } + /* Generate coefficients. */ + if(do_pubkey_field(data, certnum, "rsa(n)", &elem)) + return 1; + if(!getASN1Element(&elem, p, pk.end)) + return 1; + if(do_pubkey_field(data, certnum, "rsa(e)", &elem)) + return 1; + } + else if(strcasecompare(algo, "dsa")) { + p = getASN1Element(&elem, param->beg, param->end); + if(p) { + if(do_pubkey_field(data, certnum, "dsa(p)", &elem)) + return 1; + p = getASN1Element(&elem, p, param->end); + if(p) { + if(do_pubkey_field(data, certnum, "dsa(q)", &elem)) + return 1; + if(getASN1Element(&elem, p, param->end)) { + if(do_pubkey_field(data, certnum, "dsa(g)", &elem)) + return 1; + if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk)) + return 1; + } + } + } + } + else if(strcasecompare(algo, "dhpublicnumber")) { + p = getASN1Element(&elem, param->beg, param->end); + if(p) { + if(do_pubkey_field(data, certnum, "dh(p)", &elem)) + return 1; + if(getASN1Element(&elem, param->beg, param->end)) { + if(do_pubkey_field(data, certnum, "dh(g)", &elem)) + return 1; + if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk)) + return 1; + } + } + } + return 0; +} + +/* + * Convert an ASN.1 distinguished name into a printable string. + * Return error. + */ +static CURLcode DNtostr(struct dynbuf *store, + struct Curl_asn1Element *dn) +{ + return encodeDN(store, dn); +} + +CURLcode Curl_extract_certinfo(struct Curl_easy *data, + int certnum, + const char *beg, + const char *end) +{ + struct Curl_X509certificate cert; + struct Curl_asn1Element param; + char *certptr; + size_t clen; + struct dynbuf out; + CURLcode result = CURLE_OK; + unsigned int version; + const char *ptr; + int rc; + + if(!data->set.ssl.certinfo) + if(certnum) + return CURLE_OK; + + Curl_dyn_init(&out, MAX_X509_STR); + /* Prepare the certificate information for curl_easy_getinfo(). */ + + /* Extract the certificate ASN.1 elements. */ + if(Curl_parseX509(&cert, beg, end)) + return CURLE_PEER_FAILED_VERIFICATION; + + /* Subject. */ + result = DNtostr(&out, &cert.subject); + if(result) + goto done; + if(data->set.ssl.certinfo) { + result = ssl_push_certinfo_dyn(data, certnum, "Subject", &out); + if(result) + goto done; + } + Curl_dyn_reset(&out); + + /* Issuer. */ + result = DNtostr(&out, &cert.issuer); + if(result) + goto done; + if(data->set.ssl.certinfo) { + result = ssl_push_certinfo_dyn(data, certnum, "Issuer", &out); + if(result) + goto done; + } + Curl_dyn_reset(&out); + + /* Version (always fits in less than 32 bits). */ + version = 0; + for(ptr = cert.version.beg; ptr < cert.version.end; ptr++) + version = (version << 8) | *(const unsigned char *) ptr; + if(data->set.ssl.certinfo) { + result = Curl_dyn_addf(&out, "%x", version); + if(result) + goto done; + result = ssl_push_certinfo_dyn(data, certnum, "Version", &out); + if(result) + goto done; + Curl_dyn_reset(&out); + } + + /* Serial number. */ + result = ASN1tostr(&out, &cert.serialNumber, 0); + if(result) + goto done; + if(data->set.ssl.certinfo) { + result = ssl_push_certinfo_dyn(data, certnum, "Serial Number", &out); + if(result) + goto done; + } + Curl_dyn_reset(&out); + + /* Signature algorithm .*/ + result = dumpAlgo(&out, ¶m, cert.signatureAlgorithm.beg, + cert.signatureAlgorithm.end); + if(result) + goto done; + if(data->set.ssl.certinfo) { + result = ssl_push_certinfo_dyn(data, certnum, "Signature Algorithm", + &out); + if(result) + goto done; + } + Curl_dyn_reset(&out); + + /* Start Date. */ + result = ASN1tostr(&out, &cert.notBefore, 0); + if(result) + goto done; + if(data->set.ssl.certinfo) { + result = ssl_push_certinfo_dyn(data, certnum, "Start Date", &out); + if(result) + goto done; + } + Curl_dyn_reset(&out); + + /* Expire Date. */ + result = ASN1tostr(&out, &cert.notAfter, 0); + if(result) + goto done; + if(data->set.ssl.certinfo) { + result = ssl_push_certinfo_dyn(data, certnum, "Expire Date", &out); + if(result) + goto done; + } + Curl_dyn_reset(&out); + + /* Public Key Algorithm. */ + result = dumpAlgo(&out, ¶m, cert.subjectPublicKeyAlgorithm.beg, + cert.subjectPublicKeyAlgorithm.end); + if(result) + goto done; + if(data->set.ssl.certinfo) { + result = ssl_push_certinfo_dyn(data, certnum, "Public Key Algorithm", + &out); + if(result) + goto done; + } + + rc = do_pubkey(data, certnum, Curl_dyn_ptr(&out), + ¶m, &cert.subjectPublicKey); + if(rc) { + result = CURLE_OUT_OF_MEMORY; /* the most likely error */ + goto done; + } + Curl_dyn_reset(&out); + + /* Signature. */ + result = ASN1tostr(&out, &cert.signature, 0); + if(result) + goto done; + if(data->set.ssl.certinfo) { + result = ssl_push_certinfo_dyn(data, certnum, "Signature", &out); + if(result) + goto done; + } + Curl_dyn_reset(&out); + + /* Generate PEM certificate. */ + result = Curl_base64_encode(cert.certificate.beg, + cert.certificate.end - cert.certificate.beg, + &certptr, &clen); + if(result) + goto done; + + /* Generate the final output certificate string. Format is: + -----BEGIN CERTIFICATE-----\n + \n + . + . + . + -----END CERTIFICATE-----\n + */ + + Curl_dyn_reset(&out); + + /* Build the certificate string. */ + result = Curl_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n"); + if(!result) { + size_t j = 0; + + while(!result && (j < clen)) { + size_t chunksize = (clen - j) > 64 ? 64 : (clen - j); + result = Curl_dyn_addn(&out, &certptr[j], chunksize); + if(!result) + result = Curl_dyn_addn(&out, "\n", 1); + j += chunksize; + } + if(!result) + result = Curl_dyn_add(&out, "-----END CERTIFICATE-----\n"); + } + free(certptr); + if(!result) + if(data->set.ssl.certinfo) + result = ssl_push_certinfo_dyn(data, certnum, "Cert", &out); + +done: + if(result) + failf(data, "Failed extracting certificate chain"); + Curl_dyn_free(&out); + return result; +} + +#endif /* WANT_EXTRACT_CERTINFO */ + +#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */ diff --git a/third_party/curl/lib/vtls/x509asn1.h b/third_party/curl/lib/vtls/x509asn1.h new file mode 100644 index 0000000000..5b48596c75 --- /dev/null +++ b/third_party/curl/lib/vtls/x509asn1.h @@ -0,0 +1,92 @@ +#ifndef HEADER_CURL_X509ASN1_H +#define HEADER_CURL_X509ASN1_H + +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ + defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_MBEDTLS) + +#include "cfilters.h" +#include "urldata.h" + +/* + * Types. + */ + +/* ASN.1 parsed element. */ +struct Curl_asn1Element { + const char *header; /* Pointer to header byte. */ + const char *beg; /* Pointer to element data. */ + const char *end; /* Pointer to 1st byte after element. */ + unsigned char class; /* ASN.1 element class. */ + unsigned char tag; /* ASN.1 element tag. */ + bool constructed; /* Element is constructed. */ +}; + +/* X509 certificate: RFC 5280. */ +struct Curl_X509certificate { + struct Curl_asn1Element certificate; + struct Curl_asn1Element version; + struct Curl_asn1Element serialNumber; + struct Curl_asn1Element signatureAlgorithm; + struct Curl_asn1Element signature; + struct Curl_asn1Element issuer; + struct Curl_asn1Element notBefore; + struct Curl_asn1Element notAfter; + struct Curl_asn1Element subject; + struct Curl_asn1Element subjectPublicKeyInfo; + struct Curl_asn1Element subjectPublicKeyAlgorithm; + struct Curl_asn1Element subjectPublicKey; + struct Curl_asn1Element issuerUniqueID; + struct Curl_asn1Element subjectUniqueID; + struct Curl_asn1Element extensions; +}; + +/* + * Prototypes. + */ + +int Curl_parseX509(struct Curl_X509certificate *cert, + const char *beg, const char *end); +CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum, + const char *beg, const char *end); +CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, + const char *beg, const char *end); + +#ifdef UNITTESTS +#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_MBEDTLS) + +/* used by unit1656.c */ +CURLcode Curl_x509_GTime2str(struct dynbuf *store, + const char *beg, const char *end); +#endif +#endif + +#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */ +#endif /* HEADER_CURL_X509ASN1_H */ diff --git a/third_party/curl/lib/warnless.c b/third_party/curl/lib/warnless.c new file mode 100644 index 0000000000..c80937b843 --- /dev/null +++ b/third_party/curl/lib/warnless.c @@ -0,0 +1,386 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(__INTEL_COMPILER) && defined(__unix__) + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#endif /* __INTEL_COMPILER && __unix__ */ + +#include "warnless.h" + +#ifdef _WIN32 +#undef read +#undef write +#endif + +#include + +#define CURL_MASK_UCHAR ((unsigned char)~0) +#define CURL_MASK_SCHAR (CURL_MASK_UCHAR >> 1) + +#define CURL_MASK_USHORT ((unsigned short)~0) +#define CURL_MASK_SSHORT (CURL_MASK_USHORT >> 1) + +#define CURL_MASK_UINT ((unsigned int)~0) +#define CURL_MASK_SINT (CURL_MASK_UINT >> 1) + +#define CURL_MASK_ULONG ((unsigned long)~0) +#define CURL_MASK_SLONG (CURL_MASK_ULONG >> 1) + +#define CURL_MASK_UCOFFT ((unsigned CURL_TYPEOF_CURL_OFF_T)~0) +#define CURL_MASK_SCOFFT (CURL_MASK_UCOFFT >> 1) + +#define CURL_MASK_USIZE_T ((size_t)~0) +#define CURL_MASK_SSIZE_T (CURL_MASK_USIZE_T >> 1) + +/* +** unsigned long to unsigned short +*/ + +unsigned short curlx_ultous(unsigned long ulnum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_USHORT); + return (unsigned short)(ulnum & (unsigned long) CURL_MASK_USHORT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** unsigned long to unsigned char +*/ + +unsigned char curlx_ultouc(unsigned long ulnum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_UCHAR); + return (unsigned char)(ulnum & (unsigned long) CURL_MASK_UCHAR); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** unsigned size_t to signed curl_off_t +*/ + +curl_off_t curlx_uztoso(size_t uznum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable:4310) /* cast truncates constant value */ +#endif + + DEBUGASSERT(uznum <= (size_t) CURL_MASK_SCOFFT); + return (curl_off_t)(uznum & (size_t) CURL_MASK_SCOFFT); + +#if defined(__INTEL_COMPILER) || defined(_MSC_VER) +# pragma warning(pop) +#endif +} + +/* +** unsigned size_t to signed int +*/ + +int curlx_uztosi(size_t uznum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(uznum <= (size_t) CURL_MASK_SINT); + return (int)(uznum & (size_t) CURL_MASK_SINT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** unsigned size_t to unsigned long +*/ + +unsigned long curlx_uztoul(size_t uznum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + +#if ULONG_MAX < SIZE_T_MAX + DEBUGASSERT(uznum <= (size_t) CURL_MASK_ULONG); +#endif + return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** unsigned size_t to unsigned int +*/ + +unsigned int curlx_uztoui(size_t uznum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + +#if UINT_MAX < SIZE_T_MAX + DEBUGASSERT(uznum <= (size_t) CURL_MASK_UINT); +#endif + return (unsigned int)(uznum & (size_t) CURL_MASK_UINT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** signed long to signed int +*/ + +int curlx_sltosi(long slnum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(slnum >= 0); +#if INT_MAX < LONG_MAX + DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_SINT); +#endif + return (int)(slnum & (long) CURL_MASK_SINT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** signed long to unsigned int +*/ + +unsigned int curlx_sltoui(long slnum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(slnum >= 0); +#if UINT_MAX < LONG_MAX + DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_UINT); +#endif + return (unsigned int)(slnum & (long) CURL_MASK_UINT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** signed long to unsigned short +*/ + +unsigned short curlx_sltous(long slnum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(slnum >= 0); + DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_USHORT); + return (unsigned short)(slnum & (long) CURL_MASK_USHORT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** unsigned size_t to signed ssize_t +*/ + +ssize_t curlx_uztosz(size_t uznum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(uznum <= (size_t) CURL_MASK_SSIZE_T); + return (ssize_t)(uznum & (size_t) CURL_MASK_SSIZE_T); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** signed curl_off_t to unsigned size_t +*/ + +size_t curlx_sotouz(curl_off_t sonum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(sonum >= 0); + return (size_t)(sonum & (curl_off_t) CURL_MASK_USIZE_T); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** signed ssize_t to signed int +*/ + +int curlx_sztosi(ssize_t sznum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(sznum >= 0); +#if INT_MAX < SSIZE_T_MAX + DEBUGASSERT((size_t) sznum <= (size_t) CURL_MASK_SINT); +#endif + return (int)(sznum & (ssize_t) CURL_MASK_SINT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** unsigned int to unsigned short +*/ + +unsigned short curlx_uitous(unsigned int uinum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(uinum <= (unsigned int) CURL_MASK_USHORT); + return (unsigned short) (uinum & (unsigned int) CURL_MASK_USHORT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** signed int to unsigned size_t +*/ + +size_t curlx_sitouz(int sinum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(sinum >= 0); + return (size_t) sinum; + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +#ifdef USE_WINSOCK + +/* +** curl_socket_t to signed int +*/ + +int curlx_sktosi(curl_socket_t s) +{ + return (int)((ssize_t) s); +} + +/* +** signed int to curl_socket_t +*/ + +curl_socket_t curlx_sitosk(int i) +{ + return (curl_socket_t)((ssize_t) i); +} + +#endif /* USE_WINSOCK */ + +#if defined(_WIN32) + +ssize_t curlx_read(int fd, void *buf, size_t count) +{ + return (ssize_t)read(fd, buf, curlx_uztoui(count)); +} + +ssize_t curlx_write(int fd, const void *buf, size_t count) +{ + return (ssize_t)write(fd, buf, curlx_uztoui(count)); +} + +#endif /* _WIN32 */ + +/* Ensure that warnless.h redefinitions continue to have an effect + in "unity" builds. */ +#undef HEADER_CURL_WARNLESS_H_REDEFS diff --git a/third_party/curl/lib/warnless.h b/third_party/curl/lib/warnless.h new file mode 100644 index 0000000000..6adf63a793 --- /dev/null +++ b/third_party/curl/lib/warnless.h @@ -0,0 +1,92 @@ +#ifndef HEADER_CURL_WARNLESS_H +#define HEADER_CURL_WARNLESS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_WINSOCK +#include /* for curl_socket_t */ +#endif + +#define CURLX_FUNCTION_CAST(target_type, func) \ + (target_type)(void (*) (void))(func) + +unsigned short curlx_ultous(unsigned long ulnum); + +unsigned char curlx_ultouc(unsigned long ulnum); + +int curlx_uztosi(size_t uznum); + +curl_off_t curlx_uztoso(size_t uznum); + +unsigned long curlx_uztoul(size_t uznum); + +unsigned int curlx_uztoui(size_t uznum); + +int curlx_sltosi(long slnum); + +unsigned int curlx_sltoui(long slnum); + +unsigned short curlx_sltous(long slnum); + +ssize_t curlx_uztosz(size_t uznum); + +size_t curlx_sotouz(curl_off_t sonum); + +int curlx_sztosi(ssize_t sznum); + +unsigned short curlx_uitous(unsigned int uinum); + +size_t curlx_sitouz(int sinum); + +#ifdef USE_WINSOCK + +int curlx_sktosi(curl_socket_t s); + +curl_socket_t curlx_sitosk(int i); + +#endif /* USE_WINSOCK */ + +#if defined(_WIN32) + +ssize_t curlx_read(int fd, void *buf, size_t count); + +ssize_t curlx_write(int fd, const void *buf, size_t count); + +#endif /* _WIN32 */ + +#endif /* HEADER_CURL_WARNLESS_H */ + +#ifndef HEADER_CURL_WARNLESS_H_REDEFS +#define HEADER_CURL_WARNLESS_H_REDEFS + +#if defined(_WIN32) +#undef read +#define read(fd, buf, count) curlx_read(fd, buf, count) +#undef write +#define write(fd, buf, count) curlx_write(fd, buf, count) +#endif + +#endif /* HEADER_CURL_WARNLESS_H_REDEFS */ diff --git a/third_party/curl/lib/ws.c b/third_party/curl/lib/ws.c new file mode 100644 index 0000000000..3c2d94950c --- /dev/null +++ b/third_party/curl/lib/ws.c @@ -0,0 +1,1271 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include + +#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) + +#include "urldata.h" +#include "bufq.h" +#include "dynbuf.h" +#include "rand.h" +#include "curl_base64.h" +#include "connect.h" +#include "sendf.h" +#include "multiif.h" +#include "ws.h" +#include "easyif.h" +#include "transfer.h" +#include "nonblock.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +#define WSBIT_FIN 0x80 +#define WSBIT_OPCODE_CONT 0 +#define WSBIT_OPCODE_TEXT (1) +#define WSBIT_OPCODE_BIN (2) +#define WSBIT_OPCODE_CLOSE (8) +#define WSBIT_OPCODE_PING (9) +#define WSBIT_OPCODE_PONG (0xa) +#define WSBIT_OPCODE_MASK (0xf) + +#define WSBIT_MASK 0x80 + +/* buffer dimensioning */ +#define WS_CHUNK_SIZE 65535 +#define WS_CHUNK_COUNT 2 + +struct ws_frame_meta { + char proto_opcode; + int flags; + const char *name; +}; + +static struct ws_frame_meta WS_FRAMES[] = { + { WSBIT_OPCODE_CONT, CURLWS_CONT, "CONT" }, + { WSBIT_OPCODE_TEXT, CURLWS_TEXT, "TEXT" }, + { WSBIT_OPCODE_BIN, CURLWS_BINARY, "BIN" }, + { WSBIT_OPCODE_CLOSE, CURLWS_CLOSE, "CLOSE" }, + { WSBIT_OPCODE_PING, CURLWS_PING, "PING" }, + { WSBIT_OPCODE_PONG, CURLWS_PONG, "PONG" }, +}; + +static const char *ws_frame_name_of_op(unsigned char proto_opcode) +{ + unsigned char opcode = proto_opcode & WSBIT_OPCODE_MASK; + size_t i; + for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) { + if(WS_FRAMES[i].proto_opcode == opcode) + return WS_FRAMES[i].name; + } + return "???"; +} + +static int ws_frame_op2flags(unsigned char proto_opcode) +{ + unsigned char opcode = proto_opcode & WSBIT_OPCODE_MASK; + size_t i; + for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) { + if(WS_FRAMES[i].proto_opcode == opcode) + return WS_FRAMES[i].flags; + } + return 0; +} + +static unsigned char ws_frame_flags2op(int flags) +{ + size_t i; + for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) { + if(WS_FRAMES[i].flags & flags) + return (unsigned char)WS_FRAMES[i].proto_opcode; + } + return 0; +} + +static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, + const char *msg) +{ + switch(dec->head_len) { + case 0: + break; + case 1: + CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s]", msg, + ws_frame_name_of_op(dec->head[0]), + (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL"); + break; + default: + if(dec->head_len < dec->head_total) { + CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s](%d/%d)", msg, + ws_frame_name_of_op(dec->head[0]), + (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", + dec->head_len, dec->head_total); + } + else { + CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s payload=%" + CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T "]", + msg, ws_frame_name_of_op(dec->head[0]), + (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", + dec->payload_offset, dec->payload_len); + } + break; + } +} + +typedef ssize_t ws_write_payload(const unsigned char *buf, size_t buflen, + int frame_age, int frame_flags, + curl_off_t payload_offset, + curl_off_t payload_len, + void *userp, + CURLcode *err); + + +static void ws_dec_reset(struct ws_decoder *dec) +{ + dec->frame_age = 0; + dec->frame_flags = 0; + dec->payload_offset = 0; + dec->payload_len = 0; + dec->head_len = dec->head_total = 0; + dec->state = WS_DEC_INIT; +} + +static void ws_dec_init(struct ws_decoder *dec) +{ + ws_dec_reset(dec); +} + +static CURLcode ws_dec_read_head(struct ws_decoder *dec, + struct Curl_easy *data, + struct bufq *inraw) +{ + const unsigned char *inbuf; + size_t inlen; + + while(Curl_bufq_peek(inraw, &inbuf, &inlen)) { + if(dec->head_len == 0) { + dec->head[0] = *inbuf; + Curl_bufq_skip(inraw, 1); + + dec->frame_flags = ws_frame_op2flags(dec->head[0]); + if(!dec->frame_flags) { + failf(data, "WS: unknown opcode: %x", dec->head[0]); + ws_dec_reset(dec); + return CURLE_RECV_ERROR; + } + dec->head_len = 1; + /* ws_dec_info(dec, data, "seeing opcode"); */ + continue; + } + else if(dec->head_len == 1) { + dec->head[1] = *inbuf; + Curl_bufq_skip(inraw, 1); + dec->head_len = 2; + + if(dec->head[1] & WSBIT_MASK) { + /* A client MUST close a connection if it detects a masked frame. */ + failf(data, "WS: masked input frame"); + ws_dec_reset(dec); + return CURLE_RECV_ERROR; + } + /* How long is the frame head? */ + if(dec->head[1] == 126) { + dec->head_total = 4; + continue; + } + else if(dec->head[1] == 127) { + dec->head_total = 10; + continue; + } + else { + dec->head_total = 2; + } + } + + if(dec->head_len < dec->head_total) { + dec->head[dec->head_len] = *inbuf; + Curl_bufq_skip(inraw, 1); + ++dec->head_len; + if(dec->head_len < dec->head_total) { + /* ws_dec_info(dec, data, "decoding head"); */ + continue; + } + } + /* got the complete frame head */ + DEBUGASSERT(dec->head_len == dec->head_total); + switch(dec->head_total) { + case 2: + dec->payload_len = dec->head[1]; + break; + case 4: + dec->payload_len = (dec->head[2] << 8) | dec->head[3]; + break; + case 10: + if(dec->head[2] > 127) { + failf(data, "WS: frame length longer than 64 signed not supported"); + return CURLE_RECV_ERROR; + } + dec->payload_len = ((curl_off_t)dec->head[2] << 56) | + (curl_off_t)dec->head[3] << 48 | + (curl_off_t)dec->head[4] << 40 | + (curl_off_t)dec->head[5] << 32 | + (curl_off_t)dec->head[6] << 24 | + (curl_off_t)dec->head[7] << 16 | + (curl_off_t)dec->head[8] << 8 | + dec->head[9]; + break; + default: + /* this should never happen */ + DEBUGASSERT(0); + failf(data, "WS: unexpected frame header length"); + return CURLE_RECV_ERROR; + } + + dec->frame_age = 0; + dec->payload_offset = 0; + ws_dec_info(dec, data, "decoded"); + return CURLE_OK; + } + return CURLE_AGAIN; +} + +static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, + struct Curl_easy *data, + struct bufq *inraw, + ws_write_payload *write_payload, + void *write_ctx) +{ + const unsigned char *inbuf; + size_t inlen; + ssize_t nwritten; + CURLcode result; + curl_off_t remain = dec->payload_len - dec->payload_offset; + + (void)data; + while(remain && Curl_bufq_peek(inraw, &inbuf, &inlen)) { + if((curl_off_t)inlen > remain) + inlen = (size_t)remain; + nwritten = write_payload(inbuf, inlen, dec->frame_age, dec->frame_flags, + dec->payload_offset, dec->payload_len, + write_ctx, &result); + if(nwritten < 0) + return result; + Curl_bufq_skip(inraw, (size_t)nwritten); + dec->payload_offset += (curl_off_t)nwritten; + remain = dec->payload_len - dec->payload_offset; + CURL_TRC_WRITE(data, "websocket, passed %zd bytes payload, %" + CURL_FORMAT_CURL_OFF_T " remain", nwritten, remain); + } + + return remain? CURLE_AGAIN : CURLE_OK; +} + +static CURLcode ws_dec_pass(struct ws_decoder *dec, + struct Curl_easy *data, + struct bufq *inraw, + ws_write_payload *write_payload, + void *write_ctx) +{ + CURLcode result; + + if(Curl_bufq_is_empty(inraw)) + return CURLE_AGAIN; + + switch(dec->state) { + case WS_DEC_INIT: + ws_dec_reset(dec); + dec->state = WS_DEC_HEAD; + FALLTHROUGH(); + case WS_DEC_HEAD: + result = ws_dec_read_head(dec, data, inraw); + if(result) { + if(result != CURLE_AGAIN) { + infof(data, "WS: decode error %d", (int)result); + break; /* real error */ + } + /* incomplete ws frame head */ + DEBUGASSERT(Curl_bufq_is_empty(inraw)); + break; + } + /* head parsing done */ + dec->state = WS_DEC_PAYLOAD; + if(dec->payload_len == 0) { + ssize_t nwritten; + const unsigned char tmp = '\0'; + /* special case of a 0 length frame, need to write once */ + nwritten = write_payload(&tmp, 0, dec->frame_age, dec->frame_flags, + 0, 0, write_ctx, &result); + if(nwritten < 0) + return result; + dec->state = WS_DEC_INIT; + break; + } + FALLTHROUGH(); + case WS_DEC_PAYLOAD: + result = ws_dec_pass_payload(dec, data, inraw, write_payload, write_ctx); + ws_dec_info(dec, data, "passing"); + if(result) + return result; + /* paylod parsing done */ + dec->state = WS_DEC_INIT; + break; + default: + /* we covered all enums above, but some code analyzers are whimps */ + result = CURLE_FAILED_INIT; + } + return result; +} + +static void update_meta(struct websocket *ws, + int frame_age, int frame_flags, + curl_off_t payload_offset, + curl_off_t payload_len, + size_t cur_len) +{ + ws->frame.age = frame_age; + ws->frame.flags = frame_flags; + ws->frame.offset = payload_offset; + ws->frame.len = cur_len; + ws->frame.bytesleft = (payload_len - payload_offset - cur_len); +} + +/* WebSockets decoding client writer */ +struct ws_cw_ctx { + struct Curl_cwriter super; + struct bufq buf; +}; + +static CURLcode ws_cw_init(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct ws_cw_ctx *ctx = writer->ctx; + (void)data; + Curl_bufq_init2(&ctx->buf, WS_CHUNK_SIZE, 1, BUFQ_OPT_SOFT_LIMIT); + return CURLE_OK; +} + +static void ws_cw_close(struct Curl_easy *data, struct Curl_cwriter *writer) +{ + struct ws_cw_ctx *ctx = writer->ctx; + (void) data; + Curl_bufq_free(&ctx->buf); +} + +struct ws_cw_dec_ctx { + struct Curl_easy *data; + struct websocket *ws; + struct Curl_cwriter *next_writer; + int cw_type; +}; + +static ssize_t ws_cw_dec_next(const unsigned char *buf, size_t buflen, + int frame_age, int frame_flags, + curl_off_t payload_offset, + curl_off_t payload_len, + void *user_data, + CURLcode *err) +{ + struct ws_cw_dec_ctx *ctx = user_data; + struct Curl_easy *data = ctx->data; + struct websocket *ws = ctx->ws; + curl_off_t remain = (payload_len - (payload_offset + buflen)); + + (void)frame_age; + if((frame_flags & CURLWS_PING) && !remain) { + /* auto-respond to PINGs, only works for single-frame payloads atm */ + size_t bytes; + infof(data, "WS: auto-respond to PING with a PONG"); + /* send back the exact same content as a PONG */ + *err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG); + if(*err) + return -1; + } + else if(buflen || !remain) { + /* forward the decoded frame to the next client writer. */ + update_meta(ws, frame_age, frame_flags, payload_offset, + payload_len, buflen); + + *err = Curl_cwriter_write(data, ctx->next_writer, ctx->cw_type, + (const char *)buf, buflen); + if(*err) + return -1; + } + *err = CURLE_OK; + return (ssize_t)buflen; +} + +static CURLcode ws_cw_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + struct ws_cw_ctx *ctx = writer->ctx; + struct websocket *ws; + CURLcode result; + + if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + + ws = data->conn->proto.ws; + if(!ws) { + failf(data, "WS: not a websocket transfer"); + return CURLE_FAILED_INIT; + } + + if(nbytes) { + ssize_t nwritten; + nwritten = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf, + nbytes, &result); + if(nwritten < 0) { + infof(data, "WS: error adding data to buffer %d", result); + return result; + } + } + + while(!Curl_bufq_is_empty(&ctx->buf)) { + struct ws_cw_dec_ctx pass_ctx; + pass_ctx.data = data; + pass_ctx.ws = ws; + pass_ctx.next_writer = writer->next; + pass_ctx.cw_type = type; + result = ws_dec_pass(&ws->dec, data, &ctx->buf, + ws_cw_dec_next, &pass_ctx); + if(result == CURLE_AGAIN) { + /* insufficient amount of data, keep it for later. + * we pretend to have written all since we have a copy */ + CURL_TRC_WRITE(data, "websocket, buffered incomplete frame head"); + return CURLE_OK; + } + else if(result) { + infof(data, "WS: decode error %d", (int)result); + return result; + } + } + + if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) { + infof(data, "WS: decode ending with %zd frame bytes remaining", + Curl_bufq_len(&ctx->buf)); + return CURLE_RECV_ERROR; + } + + return CURLE_OK; +} + +/* WebSocket payload decoding client writer. */ +static const struct Curl_cwtype ws_cw_decode = { + "ws-decode", + NULL, + ws_cw_init, + ws_cw_write, + ws_cw_close, + sizeof(struct ws_cw_ctx) +}; + + +static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data, + const char *msg) +{ + infof(data, "WS-ENC: %s [%s%s%s payload=%" CURL_FORMAT_CURL_OFF_T + "/%" CURL_FORMAT_CURL_OFF_T "]", + msg, ws_frame_name_of_op(enc->firstbyte), + (enc->firstbyte & WSBIT_OPCODE_MASK) == WSBIT_OPCODE_CONT ? + " CONT" : "", + (enc->firstbyte & WSBIT_FIN)? "" : " NON-FIN", + enc->payload_len - enc->payload_remain, enc->payload_len); +} + +static void ws_enc_reset(struct ws_encoder *enc) +{ + enc->payload_remain = 0; + enc->xori = 0; + enc->contfragment = FALSE; +} + +static void ws_enc_init(struct ws_encoder *enc) +{ + ws_enc_reset(enc); +} + +/*** + RFC 6455 Section 5.2 + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) | + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Payload Data continued ... | + +---------------------------------------------------------------+ +*/ + +static ssize_t ws_enc_write_head(struct Curl_easy *data, + struct ws_encoder *enc, + unsigned int flags, + curl_off_t payload_len, + struct bufq *out, + CURLcode *err) +{ + unsigned char firstbyte = 0; + unsigned char opcode; + unsigned char head[14]; + size_t hlen; + ssize_t n; + + if(payload_len < 0) { + failf(data, "WS: starting new frame with negative payload length %" + CURL_FORMAT_CURL_OFF_T, payload_len); + *err = CURLE_SEND_ERROR; + return -1; + } + + if(enc->payload_remain > 0) { + /* trying to write a new frame before the previous one is finished */ + failf(data, "WS: starting new frame with %zd bytes from last one" + "remaining to be sent", (ssize_t)enc->payload_remain); + *err = CURLE_SEND_ERROR; + return -1; + } + + opcode = ws_frame_flags2op((int)flags); + if(!opcode) { + failf(data, "WS: provided flags not recognized '%x'", flags); + *err = CURLE_SEND_ERROR; + return -1; + } + + if(!(flags & CURLWS_CONT)) { + if(!enc->contfragment) + /* not marked as continuing, this is the final fragment */ + firstbyte |= WSBIT_FIN | opcode; + else + /* marked as continuing, this is the final fragment; set CONT + opcode and FIN bit */ + firstbyte |= WSBIT_FIN | WSBIT_OPCODE_CONT; + + enc->contfragment = FALSE; + } + else if(enc->contfragment) { + /* the previous fragment was not a final one and this is not either, keep a + CONT opcode and no FIN bit */ + firstbyte |= WSBIT_OPCODE_CONT; + } + else { + firstbyte = opcode; + enc->contfragment = TRUE; + } + + head[0] = enc->firstbyte = firstbyte; + if(payload_len > 65535) { + head[1] = 127 | WSBIT_MASK; + head[2] = (unsigned char)((payload_len >> 56) & 0xff); + head[3] = (unsigned char)((payload_len >> 48) & 0xff); + head[4] = (unsigned char)((payload_len >> 40) & 0xff); + head[5] = (unsigned char)((payload_len >> 32) & 0xff); + head[6] = (unsigned char)((payload_len >> 24) & 0xff); + head[7] = (unsigned char)((payload_len >> 16) & 0xff); + head[8] = (unsigned char)((payload_len >> 8) & 0xff); + head[9] = (unsigned char)(payload_len & 0xff); + hlen = 10; + } + else if(payload_len >= 126) { + head[1] = 126 | WSBIT_MASK; + head[2] = (unsigned char)((payload_len >> 8) & 0xff); + head[3] = (unsigned char)(payload_len & 0xff); + hlen = 4; + } + else { + head[1] = (unsigned char)payload_len | WSBIT_MASK; + hlen = 2; + } + + enc->payload_remain = enc->payload_len = payload_len; + ws_enc_info(enc, data, "sending"); + + /* add 4 bytes mask */ + memcpy(&head[hlen], &enc->mask, 4); + hlen += 4; + /* reset for payload to come */ + enc->xori = 0; + + n = Curl_bufq_write(out, head, hlen, err); + if(n < 0) + return -1; + if((size_t)n != hlen) { + /* We use a bufq with SOFT_LIMIT, writing should always succeed */ + DEBUGASSERT(0); + *err = CURLE_SEND_ERROR; + return -1; + } + return n; +} + +static ssize_t ws_enc_write_payload(struct ws_encoder *enc, + struct Curl_easy *data, + const unsigned char *buf, size_t buflen, + struct bufq *out, CURLcode *err) +{ + ssize_t n; + size_t i, len; + + if(Curl_bufq_is_full(out)) { + *err = CURLE_AGAIN; + return -1; + } + + /* not the most performant way to do this */ + len = buflen; + if((curl_off_t)len > enc->payload_remain) + len = (size_t)enc->payload_remain; + + for(i = 0; i < len; ++i) { + unsigned char c = buf[i] ^ enc->mask[enc->xori]; + n = Curl_bufq_write(out, &c, 1, err); + if(n < 0) { + if((*err != CURLE_AGAIN) || !i) + return -1; + break; + } + enc->xori++; + enc->xori &= 3; + } + enc->payload_remain -= (curl_off_t)i; + ws_enc_info(enc, data, "buffered"); + return (ssize_t)i; +} + + +struct wsfield { + const char *name; + const char *val; +}; + +CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req) +{ + unsigned int i; + CURLcode result = CURLE_OK; + unsigned char rand[16]; + char *randstr; + size_t randlen; + char keyval[40]; + struct SingleRequest *k = &data->req; + struct wsfield heads[]= { + { + /* The request MUST contain an |Upgrade| header field whose value + MUST include the "websocket" keyword. */ + "Upgrade:", "websocket" + }, + { + /* The request MUST contain a |Connection| header field whose value + MUST include the "Upgrade" token. */ + "Connection:", "Upgrade", + }, + { + /* The request MUST include a header field with the name + |Sec-WebSocket-Version|. The value of this header field MUST be + 13. */ + "Sec-WebSocket-Version:", "13", + }, + { + /* The request MUST include a header field with the name + |Sec-WebSocket-Key|. The value of this header field MUST be a nonce + consisting of a randomly selected 16-byte value that has been + base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be + selected randomly for each connection. */ + "Sec-WebSocket-Key:", NULL, + } + }; + heads[3].val = &keyval[0]; + + /* 16 bytes random */ + result = Curl_rand(data, (unsigned char *)rand, sizeof(rand)); + if(result) + return result; + result = Curl_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen); + if(result) + return result; + DEBUGASSERT(randlen < sizeof(keyval)); + if(randlen >= sizeof(keyval)) { + free(randstr); + return CURLE_FAILED_INIT; + } + strcpy(keyval, randstr); + free(randstr); + for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) { + if(!Curl_checkheaders(data, STRCONST(heads[i].name))) { +#ifdef USE_HYPER + char field[128]; + msnprintf(field, sizeof(field), "%s %s", heads[i].name, + heads[i].val); + result = Curl_hyper_header(data, req, field); +#else + (void)data; + result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name, + heads[i].val); +#endif + } + } + k->upgr101 = UPGR101_WS; + return result; +} + +/* + * 'nread' is number of bytes of websocket data already in the buffer at + * 'mem'. + */ +CURLcode Curl_ws_accept(struct Curl_easy *data, + const char *mem, size_t nread) +{ + struct SingleRequest *k = &data->req; + struct websocket *ws; + struct Curl_cwriter *ws_dec_writer; + CURLcode result; + + DEBUGASSERT(data->conn); + ws = data->conn->proto.ws; + if(!ws) { + size_t chunk_size = WS_CHUNK_SIZE; + ws = calloc(1, sizeof(*ws)); + if(!ws) + return CURLE_OUT_OF_MEMORY; + data->conn->proto.ws = ws; +#ifdef DEBUGBUILD + { + char *p = getenv("CURL_WS_CHUNK_SIZE"); + if(p) { + long l = strtol(p, NULL, 10); + if(l > 0 && l <= (1*1024*1024)) { + chunk_size = (size_t)l; + } + } + } +#endif + DEBUGF(infof(data, "WS, using chunk size %zu", chunk_size)); + Curl_bufq_init2(&ws->recvbuf, chunk_size, WS_CHUNK_COUNT, + BUFQ_OPT_SOFT_LIMIT); + Curl_bufq_init2(&ws->sendbuf, chunk_size, WS_CHUNK_COUNT, + BUFQ_OPT_SOFT_LIMIT); + ws_dec_init(&ws->dec); + ws_enc_init(&ws->enc); + } + else { + Curl_bufq_reset(&ws->recvbuf); + ws_dec_reset(&ws->dec); + ws_enc_reset(&ws->enc); + } + /* Verify the Sec-WebSocket-Accept response. + + The sent value is the base64 encoded version of a SHA-1 hash done on the + |Sec-WebSocket-Key| header field concatenated with + the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11". + */ + + /* If the response includes a |Sec-WebSocket-Extensions| header field and + this header field indicates the use of an extension that was not present + in the client's handshake (the server has indicated an extension not + requested by the client), the client MUST Fail the WebSocket Connection. + */ + + /* If the response includes a |Sec-WebSocket-Protocol| header field + and this header field indicates the use of a subprotocol that was + not present in the client's handshake (the server has indicated a + subprotocol not requested by the client), the client MUST Fail + the WebSocket Connection. */ + + /* 4 bytes random */ + + result = Curl_rand(data, (unsigned char *)&ws->enc.mask, + sizeof(ws->enc.mask)); + if(result) + return result; + infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x", + ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]); + + /* Install our client writer that decodes WS frames payload */ + result = Curl_cwriter_create(&ws_dec_writer, data, &ws_cw_decode, + CURL_CW_CONTENT_DECODE); + if(result) + return result; + + result = Curl_cwriter_add(data, ws_dec_writer); + if(result) { + Curl_cwriter_free(data, ws_dec_writer); + return result; + } + + if(data->set.connect_only) { + ssize_t nwritten; + /* In CONNECT_ONLY setup, the payloads from `mem` need to be received + * when using `curl_ws_recv` later on after this transfer is already + * marked as DONE. */ + nwritten = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)mem, + nread, &result); + if(nwritten < 0) + return result; + infof(data, "%zu bytes websocket payload", nread); + } + else { /* !connect_only */ + /* And pass any additional data to the writers */ + if(nread) { + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)mem, nread); + } + } + k->upgr101 = UPGR101_RECEIVED; + + return result; +} + +struct ws_collect { + struct Curl_easy *data; + unsigned char *buffer; + size_t buflen; + size_t bufidx; + int frame_age; + int frame_flags; + curl_off_t payload_offset; + curl_off_t payload_len; + bool written; +}; + +static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen, + int frame_age, int frame_flags, + curl_off_t payload_offset, + curl_off_t payload_len, + void *userp, + CURLcode *err) +{ + struct ws_collect *ctx = userp; + size_t nwritten; + curl_off_t remain = (payload_len - (payload_offset + buflen)); + + if(!ctx->bufidx) { + /* first write */ + ctx->frame_age = frame_age; + ctx->frame_flags = frame_flags; + ctx->payload_offset = payload_offset; + ctx->payload_len = payload_len; + } + + if((frame_flags & CURLWS_PING) && !remain) { + /* auto-respond to PINGs, only works for single-frame payloads atm */ + size_t bytes; + infof(ctx->data, "WS: auto-respond to PING with a PONG"); + /* send back the exact same content as a PONG */ + *err = curl_ws_send(ctx->data, buf, buflen, &bytes, 0, CURLWS_PONG); + if(*err) + return -1; + nwritten = bytes; + } + else { + ctx->written = TRUE; + DEBUGASSERT(ctx->buflen >= ctx->bufidx); + nwritten = CURLMIN(buflen, ctx->buflen - ctx->bufidx); + if(!nwritten) { + if(!buflen) { /* 0 length write, we accept that */ + *err = CURLE_OK; + return 0; + } + *err = CURLE_AGAIN; /* no more space */ + return -1; + } + *err = CURLE_OK; + memcpy(ctx->buffer + ctx->bufidx, buf, nwritten); + ctx->bufidx += nwritten; + } + return nwritten; +} + +static ssize_t nw_in_recv(void *reader_ctx, + unsigned char *buf, size_t buflen, + CURLcode *err) +{ + struct Curl_easy *data = reader_ctx; + size_t nread; + + *err = curl_easy_recv(data, buf, buflen, &nread); + if(*err) + return -1; + return (ssize_t)nread; +} + +CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, + size_t buflen, size_t *nread, + const struct curl_ws_frame **metap) +{ + struct connectdata *conn = data->conn; + struct websocket *ws; + bool done = FALSE; /* not filled passed buffer yet */ + struct ws_collect ctx; + CURLcode result; + + if(!conn) { + /* Unhappy hack with lifetimes of transfers and connection */ + if(!data->set.connect_only) { + failf(data, "CONNECT_ONLY is required"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + + Curl_getconnectinfo(data, &conn); + if(!conn) { + failf(data, "connection not found"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + } + ws = conn->proto.ws; + if(!ws) { + failf(data, "connection is not setup for websocket"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + *nread = 0; + *metap = NULL; + + memset(&ctx, 0, sizeof(ctx)); + ctx.data = data; + ctx.buffer = buffer; + ctx.buflen = buflen; + + while(!done) { + /* receive more when our buffer is empty */ + if(Curl_bufq_is_empty(&ws->recvbuf)) { + ssize_t n = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &result); + if(n < 0) { + return result; + } + else if(n == 0) { + /* connection closed */ + infof(data, "connection expectedly closed?"); + return CURLE_GOT_NOTHING; + } + DEBUGF(infof(data, "curl_ws_recv, added %zu bytes from network", + Curl_bufq_len(&ws->recvbuf))); + } + + result = ws_dec_pass(&ws->dec, data, &ws->recvbuf, + ws_client_collect, &ctx); + if(result == CURLE_AGAIN) { + if(!ctx.written) { + ws_dec_info(&ws->dec, data, "need more input"); + continue; /* nothing written, try more input */ + } + done = TRUE; + break; + } + else if(result) { + return result; + } + else if(ctx.written) { + /* The decoded frame is passed back to our caller. + * There are frames like PING were we auto-respond to and + * that we do not return. For these `ctx.written` is not set. */ + done = TRUE; + break; + } + } + + /* update frame information to be passed back */ + update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, + ctx.payload_len, ctx.bufidx); + *metap = &ws->frame; + *nread = ws->frame.len; + /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" + CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)", + buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */ + return CURLE_OK; +} + +static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, + bool complete) +{ + if(!Curl_bufq_is_empty(&ws->sendbuf)) { + CURLcode result; + const unsigned char *out; + size_t outlen, n; + + while(Curl_bufq_peek(&ws->sendbuf, &out, &outlen)) { + if(data->set.connect_only) + result = Curl_senddata(data, out, outlen, &n); + else { + result = Curl_xfer_send(data, out, outlen, &n); + if(!result && !n && outlen) + result = CURLE_AGAIN; + } + + if(result) { + if(result == CURLE_AGAIN) { + if(!complete) { + infof(data, "WS: flush EAGAIN, %zu bytes remain in buffer", + Curl_bufq_len(&ws->sendbuf)); + return result; + } + /* TODO: the current design does not allow for buffered writes. + * We need to flush the buffer now. There is no ws_flush() later */ + n = 0; + continue; + } + else if(result) { + failf(data, "WS: flush, write error %d", result); + return result; + } + } + else { + infof(data, "WS: flushed %zu bytes", n); + Curl_bufq_skip(&ws->sendbuf, n); + } + } + } + return CURLE_OK; +} + +CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer, + size_t buflen, size_t *sent, + curl_off_t fragsize, + unsigned int flags) +{ + struct websocket *ws; + ssize_t n; + size_t nwritten, space; + CURLcode result; + + *sent = 0; + if(!data->conn && data->set.connect_only) { + result = Curl_connect_only_attach(data); + if(result) + return result; + } + if(!data->conn) { + failf(data, "No associated connection"); + return CURLE_SEND_ERROR; + } + if(!data->conn->proto.ws) { + failf(data, "Not a websocket transfer"); + return CURLE_SEND_ERROR; + } + ws = data->conn->proto.ws; + + if(data->set.ws_raw_mode) { + if(fragsize || flags) { + DEBUGF(infof(data, "ws_send: " + "fragsize and flags cannot be non-zero in raw mode")); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if(!buflen) + /* nothing to do */ + return CURLE_OK; + /* raw mode sends exactly what was requested, and this is from within + the write callback */ + if(Curl_is_in_callback(data)) { + result = Curl_xfer_send(data, buffer, buflen, &nwritten); + } + else + result = Curl_senddata(data, buffer, buflen, &nwritten); + + infof(data, "WS: wanted to send %zu bytes, sent %zu bytes", + buflen, nwritten); + *sent = nwritten; + return result; + } + + /* Not RAW mode, buf we do the frame encoding */ + result = ws_flush(data, ws, FALSE); + if(result) + return result; + + /* TODO: the current design does not allow partial writes, afaict. + * It is not clear how the application is supposed to react. */ + space = Curl_bufq_space(&ws->sendbuf); + DEBUGF(infof(data, "curl_ws_send(len=%zu), sendbuf len=%zu space %zu", + buflen, Curl_bufq_len(&ws->sendbuf), space)); + if(space < 14) + return CURLE_AGAIN; + + if(flags & CURLWS_OFFSET) { + if(fragsize) { + /* a frame series 'fragsize' bytes big, this is the first */ + n = ws_enc_write_head(data, &ws->enc, flags, fragsize, + &ws->sendbuf, &result); + if(n < 0) + return result; + } + else { + if((curl_off_t)buflen > ws->enc.payload_remain) { + infof(data, "WS: unaligned frame size (sending %zu instead of %" + CURL_FORMAT_CURL_OFF_T ")", + buflen, ws->enc.payload_remain); + } + } + } + else if(!ws->enc.payload_remain) { + n = ws_enc_write_head(data, &ws->enc, flags, (curl_off_t)buflen, + &ws->sendbuf, &result); + if(n < 0) + return result; + } + + n = ws_enc_write_payload(&ws->enc, data, + buffer, buflen, &ws->sendbuf, &result); + if(n < 0) + return result; + + *sent = (size_t)n; + return ws_flush(data, ws, TRUE); +} + +static void ws_free(struct connectdata *conn) +{ + if(conn && conn->proto.ws) { + Curl_bufq_free(&conn->proto.ws->recvbuf); + Curl_bufq_free(&conn->proto.ws->sendbuf); + Curl_safefree(conn->proto.ws); + } +} + +static CURLcode ws_setup_conn(struct Curl_easy *data, + struct connectdata *conn) +{ + /* websockets is 1.1 only (for now) */ + data->state.httpwant = CURL_HTTP_VERSION_1_1; + return Curl_http_setup_conn(data, conn); +} + + +static CURLcode ws_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + (void)data; + (void)dead_connection; + ws_free(conn); + return CURLE_OK; +} + +CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) +{ + /* we only return something for websocket, called from within the callback + when not using raw mode */ + if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->conn && + data->conn->proto.ws && !data->set.ws_raw_mode) + return &data->conn->proto.ws->frame; + return NULL; +} + +const struct Curl_handler Curl_handler_ws = { + "WS", /* scheme */ + ws_setup_conn, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + Curl_http_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + Curl_http_getsock_do, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ws_disconnect, /* disconnect */ + Curl_http_write_resp, /* write_resp */ + Curl_http_write_resp_hd, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTP, /* defport */ + CURLPROTO_WS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL +}; + +#ifdef USE_SSL +const struct Curl_handler Curl_handler_wss = { + "WSS", /* scheme */ + ws_setup_conn, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + Curl_http_connect, /* connect_it */ + NULL, /* connecting */ + ZERO_NULL, /* doing */ + NULL, /* proto_getsock */ + Curl_http_getsock_do, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ws_disconnect, /* disconnect */ + Curl_http_write_resp, /* write_resp */ + Curl_http_write_resp_hd, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTPS, /* defport */ + CURLPROTO_WSS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL +}; +#endif + + +#else + +CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, + size_t *nread, + const struct curl_ws_frame **metap) +{ + (void)curl; + (void)buffer; + (void)buflen; + (void)nread; + (void)metap; + return CURLE_NOT_BUILT_IN; +} + +CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, + size_t buflen, size_t *sent, + curl_off_t fragsize, + unsigned int flags) +{ + (void)curl; + (void)buffer; + (void)buflen; + (void)sent; + (void)fragsize; + (void)flags; + return CURLE_NOT_BUILT_IN; +} + +CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) +{ + (void)data; + return NULL; +} +#endif /* USE_WEBSOCKETS */ diff --git a/third_party/curl/lib/ws.h b/third_party/curl/lib/ws.h new file mode 100644 index 0000000000..398900cc3f --- /dev/null +++ b/third_party/curl/lib/ws.h @@ -0,0 +1,90 @@ +#ifndef HEADER_CURL_WS_H +#define HEADER_CURL_WS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) + +#ifdef USE_HYPER +#define REQTYPE void +#else +#define REQTYPE struct dynbuf +#endif + +/* a client-side WS frame decoder, parsing frame headers and + * payload, keeping track of current position and stats */ +enum ws_dec_state { + WS_DEC_INIT, + WS_DEC_HEAD, + WS_DEC_PAYLOAD +}; + +struct ws_decoder { + int frame_age; /* zero */ + int frame_flags; /* See the CURLWS_* defines */ + curl_off_t payload_offset; /* the offset parsing is at */ + curl_off_t payload_len; + unsigned char head[10]; + int head_len, head_total; + enum ws_dec_state state; +}; + +/* a client-side WS frame encoder, generating frame headers and + * converting payloads, tracking remaining data in current frame */ +struct ws_encoder { + curl_off_t payload_len; /* payload length of current frame */ + curl_off_t payload_remain; /* remaining payload of current */ + unsigned int xori; /* xor index */ + unsigned char mask[4]; /* 32-bit mask for this connection */ + unsigned char firstbyte; /* first byte of frame we encode */ + bool contfragment; /* set TRUE if the previous fragment sent was not final */ +}; + +/* A websocket connection with en- and decoder that treat frames + * and keep track of boundaries. */ +struct websocket { + struct Curl_easy *data; /* used for write callback handling */ + struct ws_decoder dec; /* decode of we frames */ + struct ws_encoder enc; /* decode of we frames */ + struct bufq recvbuf; /* raw data from the server */ + struct bufq sendbuf; /* raw data to be sent to the server */ + struct curl_ws_frame frame; /* the current WS FRAME received */ +}; + +CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req); +CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len); + +extern const struct Curl_handler Curl_handler_ws; +#ifdef USE_SSL +extern const struct Curl_handler Curl_handler_wss; +#endif + + +#else +#define Curl_ws_request(x,y) CURLE_OK +#define Curl_ws_free(x) Curl_nop_stmt +#endif + +#endif /* HEADER_CURL_WS_H */ diff --git a/third_party/curl/libcurl.def b/third_party/curl/libcurl.def new file mode 100644 index 0000000000..9bf9fcc958 --- /dev/null +++ b/third_party/curl/libcurl.def @@ -0,0 +1,95 @@ +EXPORTS +curl_easy_cleanup +curl_easy_duphandle +curl_easy_escape +curl_easy_getinfo +curl_easy_header +curl_easy_init +curl_easy_nextheader +curl_easy_option_by_id +curl_easy_option_by_name +curl_easy_option_next +curl_easy_pause +curl_easy_perform +curl_easy_recv +curl_easy_reset +curl_easy_send +curl_easy_setopt +curl_easy_strerror +curl_easy_unescape +curl_easy_upkeep +curl_escape +curl_formadd +curl_formfree +curl_formget +curl_free +curl_getdate +curl_getenv +curl_global_cleanup +curl_global_init +curl_global_init_mem +curl_global_sslset +curl_global_trace +curl_maprintf +curl_mfprintf +curl_mime_addpart +curl_mime_data +curl_mime_data_cb +curl_mime_encoder +curl_mime_filedata +curl_mime_filename +curl_mime_free +curl_mime_headers +curl_mime_init +curl_mime_name +curl_mime_subparts +curl_mime_type +curl_mprintf +curl_msnprintf +curl_msprintf +curl_multi_add_handle +curl_multi_assign +curl_multi_cleanup +curl_multi_fdset +curl_multi_get_handles +curl_multi_info_read +curl_multi_init +curl_multi_perform +curl_multi_poll +curl_multi_remove_handle +curl_multi_setopt +curl_multi_socket +curl_multi_socket_action +curl_multi_socket_all +curl_multi_strerror +curl_multi_timeout +curl_multi_wait +curl_multi_waitfds +curl_multi_wakeup +curl_mvaprintf +curl_mvfprintf +curl_mvprintf +curl_mvsnprintf +curl_mvsprintf +curl_pushheader_byname +curl_pushheader_bynum +curl_share_cleanup +curl_share_init +curl_share_setopt +curl_share_strerror +curl_slist_append +curl_slist_free_all +curl_strequal +curl_strnequal +curl_unescape +curl_url +curl_url_cleanup +curl_url_dup +curl_url_get +curl_url_set +curl_url_strerror +curl_version +curl_version_info +curl_ws_meta +curl_ws_recv +curl_ws_send diff --git a/third_party/curl/libcurl.pc.in b/third_party/curl/libcurl.pc.in new file mode 100644 index 0000000000..0eb1804857 --- /dev/null +++ b/third_party/curl/libcurl.pc.in @@ -0,0 +1,40 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +supported_protocols="@SUPPORT_PROTOCOLS@" +supported_features="@SUPPORT_FEATURES@" + +Name: libcurl +URL: https://curl.se/ +Description: Library to transfer files with ftp, http, etc. +Version: @CURLVERSION@ +Requires: @LIBCURL_PC_REQUIRES@ +Requires.private: @LIBCURL_PC_REQUIRES_PRIVATE@ +Libs: -L${libdir} -lcurl @LIBCURL_NO_SHARED@ +Libs.private: @LIBCURL_LIBS@ +Cflags: -I${includedir} @CPPFLAG_CURL_STATICLIB@ diff --git a/third_party/curl/src/.checksrc b/third_party/curl/src/.checksrc new file mode 100644 index 0000000000..946367c499 --- /dev/null +++ b/third_party/curl/src/.checksrc @@ -0,0 +1 @@ +enable STDERR diff --git a/third_party/curl/src/CMakeLists.txt b/third_party/curl/src/CMakeLists.txt new file mode 100644 index 0000000000..43b2a4572e --- /dev/null +++ b/third_party/curl/src/CMakeLists.txt @@ -0,0 +1,144 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +set(EXE_NAME curl) +add_definitions("-DBUILDING_CURL") + +if(ENABLE_CURL_MANUAL AND HAVE_MANUAL_TOOLS) + add_definitions("-DUSE_MANUAL") + add_custom_command( + OUTPUT "tool_hugehelp.c" + COMMAND ${CMAKE_COMMAND} -E echo "#include \"tool_setup.h\"" > "tool_hugehelp.c" + COMMAND ${CMAKE_COMMAND} -E echo "#ifndef HAVE_LIBZ" >> "tool_hugehelp.c" + COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mkhelp.pl" + < "${CURL_BINARY_DIR}/docs/cmdline-opts/curl.txt" >> "tool_hugehelp.c" + + COMMAND ${CMAKE_COMMAND} -E echo "#else" >> "tool_hugehelp.c" + COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mkhelp.pl" -c + < "${CURL_BINARY_DIR}/docs/cmdline-opts/curl.txt" >> "tool_hugehelp.c" + COMMAND ${CMAKE_COMMAND} -E echo "#endif /* HAVE_LIBZ */" >> "tool_hugehelp.c" + DEPENDS + "generate-curl.1" + "${CURL_BINARY_DIR}/docs/cmdline-opts/curl.1" + "${CMAKE_CURRENT_SOURCE_DIR}/mkhelp.pl" + "${CMAKE_CURRENT_SOURCE_DIR}/tool_hugehelp.h" + VERBATIM) +else() + add_custom_command( + OUTPUT "tool_hugehelp.c" + COMMAND ${CMAKE_COMMAND} -E echo "#include \"tool_hugehelp.h\"" > "tool_hugehelp.c" + DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/tool_hugehelp.h" + VERBATIM) +endif() + +# Get 'CURL_CFILES', 'CURLX_CFILES', 'CURL_HFILES', 'CURLTOOL_LIBCURL_CFILES' variables +transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") +include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") + +if(CURL_CA_EMBED_SET) + if(PERL_FOUND) + add_definitions("-DCURL_CA_EMBED") + add_custom_command( + OUTPUT "tool_ca_embed.c" + COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mk-file-embed.pl" --var curl_ca_embed + < "${CURL_CA_EMBED}" > "tool_ca_embed.c" + DEPENDS + "${CURL_CA_EMBED}" + "${CMAKE_CURRENT_SOURCE_DIR}/mk-file-embed.pl" + VERBATIM) + list(APPEND CURL_CFILES "tool_ca_embed.c") + else() + message(WARNING "Perl not found. Will not embed the CA bundle.") + endif() +endif() + +if(WIN32) + list(APPEND CURL_CFILES "curl.rc") +endif() + +if(BUILD_STATIC_CURL) + set(CURLX_CFILES ${CURLTOOL_LIBCURL_CFILES}) +endif() + +if(ENABLE_CURLDEBUG) + # We must compile this source separately to avoid memdebug.h redefinitions + # applying to them. + set_source_files_properties("../lib/curl_multibyte.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) +endif() + +message(status "############ ${CURL_CFILES}") +message(status "%%%%%%%%%%%% ${CURLX_CFILES}") +message(status "@@@@@@@@@@@@ ${CURL_HFILES}") + +add_executable( + ${EXE_NAME} + ${CURL_CFILES} ${CURLX_CFILES} ${CURL_HFILES} +) + +add_executable( + ${PROJECT_NAME}::${EXE_NAME} + ALIAS ${EXE_NAME} +) + +add_library( + curltool # special libcurltool library just for unittests + STATIC + EXCLUDE_FROM_ALL + ${CURL_CFILES} ${CURLTOOL_LIBCURL_CFILES} ${CURL_HFILES} +) +target_compile_definitions(curltool PUBLIC "UNITTESTS" "CURL_STATICLIB") +target_link_libraries(curltool PRIVATE ${CURL_LIBS}) + +if(CURL_HAS_LTO) + set_target_properties(${EXE_NAME} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE + INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) +endif() + +if(ENABLE_UNICODE AND MINGW) + target_link_libraries(${EXE_NAME} "-municode") +endif() + +source_group("curlX source files" FILES ${CURLX_CFILES}) +source_group("curl source files" FILES ${CURL_CFILES}) +source_group("curl header files" FILES ${CURL_HFILES}) + +include_directories( + "${CURL_BINARY_DIR}/lib" # for "curl_config.h" + "${CURL_SOURCE_DIR}/lib" # for "curl_setup.h" + # This is needed as tool_hugehelp.c is generated in the binary dir + "${CURL_SOURCE_DIR}/src" # for "tool_hugehelp.h" +) + +# Build curl executable +message(status "=========== ${EXE_NAME} === ${LIB_SELECTED_FOR_EXE}}") +target_link_libraries(${EXE_NAME} ${LIB_SELECTED_FOR_EXE}) + +################################################################################ + +install(TARGETS ${EXE_NAME} EXPORT ${TARGETS_EXPORT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) +export(TARGETS ${EXE_NAME} + FILE "${PROJECT_BINARY_DIR}/curl-target.cmake" + NAMESPACE ${PROJECT_NAME}:: +) diff --git a/third_party/curl/src/Makefile.am b/third_party/curl/src/Makefile.am new file mode 100644 index 0000000000..4ce83c9239 --- /dev/null +++ b/third_party/curl/src/Makefile.am @@ -0,0 +1,163 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +AUTOMAKE_OPTIONS = foreign nostdinc + +# remove targets if the command fails +.DELETE_ON_ERROR: + +# Specify our include paths here, and do it relative to $(top_srcdir) and +# $(top_builddir), to ensure that these paths which belong to the library +# being currently built and tested are searched before the library which +# might possibly already be installed in the system. +# +# $(top_srcdir)/include is for libcurl's external include files +# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file +# $(top_builddir)/src is for curl's generated src/curl_config.h file +# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files +# $(top_srcdir)/src is for curl's src/tool_setup.h and "curl-private" files + +AM_CPPFLAGS = -I$(top_srcdir)/include \ + -I$(top_builddir)/lib \ + -I$(top_builddir)/src \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src + +bin_PROGRAMS = curl + +if USE_CPPFLAG_CURL_STATICLIB +AM_CPPFLAGS += -DCURL_STATICLIB +endif +AM_CPPFLAGS += -DBUILDING_CURL + +include Makefile.inc + +# CURL_FILES comes from Makefile.inc +curl_SOURCES = $(CURL_FILES) +if HAVE_WINDRES +curl_SOURCES += $(CURL_RCFILES) +$(CURL_RCFILES): tool_version.h +endif + +curl_LDFLAGS = $(AM_LDFLAGS) $(CURL_LDFLAGS_BIN) + +# This might hold -Werror +CFLAGS += @CURL_CFLAG_EXTRAS@ + +# Prevent LIBS from being used for all link targets +LIBS = $(BLANK_AT_MAKETIME) + +if USE_EXPLICIT_LIB_DEPS +curl_LDADD = $(top_builddir)/lib/libcurl.la @LIBCURL_LIBS@ +else +curl_LDADD = $(top_builddir)/lib/libcurl.la @SSL_LIBS@ @ZLIB_LIBS@ @CURL_NETWORK_AND_TIME_LIBS@ +endif + +# if unit tests are enabled, build a static library to link them with +if BUILD_UNITTESTS +noinst_LTLIBRARIES = libcurltool.la +libcurltool_la_CPPFLAGS = $(AM_CPPFLAGS) \ + -DCURL_STATICLIB -DUNITTESTS +libcurltool_la_CFLAGS = +libcurltool_la_LDFLAGS = -static $(LINKFLAGS) +libcurltool_la_SOURCES = $(CURL_FILES) +endif + +CLEANFILES = tool_hugehelp.c +# Use the C locale to ensure that only ASCII characters appear in the +# embedded text. +NROFF=env LC_ALL=C @NROFF@ @MANOPT@ 2>/dev/null # figured out by the configure script + +EXTRA_DIST = mkhelp.pl \ + Makefile.mk curl.rc Makefile.inc CMakeLists.txt .checksrc + +# Use absolute directory to disable VPATH +ASCIIPAGE=$(top_builddir)/docs/cmdline-opts/curl.txt +MKHELP=$(top_srcdir)/src/mkhelp.pl +HUGE=tool_hugehelp.c + +HUGECMD = $(HUGEIT_$(V)) +HUGEIT_0 = @echo " HUGE " $@; +HUGEIT_1 = +HUGEIT_ = $(HUGEIT_0) + +CHECKSRC = $(CS_$(V)) +CS_0 = @echo " RUN " $@; +CS_1 = +CS_ = $(CS_0) + +if USE_MANUAL +# Here are the stuff to create a built-in manual + +$(ASCIIPAGE): + cd $(top_builddir)/docs && $(MAKE) + +if HAVE_LIBZ +# This generates the tool_hugehelp.c file in both uncompressed and +# compressed formats. +$(HUGE): $(ASCIIPAGE) $(MKHELP) + $(HUGECMD) (echo '#include "tool_setup.h"' > $(HUGE); \ + echo '#ifndef HAVE_LIBZ' >> $(HUGE); \ + $(PERL) $(MKHELP) < $(ASCIIPAGE) >> $(HUGE); \ + echo '#else' >> $(HUGE); \ + $(PERL) $(MKHELP) -c < $(ASCIIPAGE) >> $(HUGE); \ + echo '#endif /* HAVE_LIBZ */' >> $(HUGE) ) +else # HAVE_LIBZ +# This generates the tool_hugehelp.c file uncompressed only +$(HUGE): $(ASCIIPAGE) $(MKHELP) + $(HUGECMD)(echo '#include "tool_setup.h"' > $(HUGE); \ + $(PERL) $(MKHELP) < $(ASCIIPAGE) >> $(HUGE) ) +endif + +else # USE_MANUAL +# built-in manual has been disabled, make a blank file +$(HUGE): + echo '#include "tool_hugehelp.h"' >> $(HUGE) +endif + +# ignore tool_hugehelp.c since it is generated source code and it plays +# by slightly different rules! +checksrc: + $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \ + -W$(srcdir)/tool_hugehelp.c $(srcdir)/*.[ch]) + +if DEBUGBUILD +# for debug builds, we scan the sources on all regular make invokes +all-local: checksrc +endif + +# disable the tests that are mostly causing false positives +TIDYFLAGS=-checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-valist.Uninitialized,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference + +TIDY:=clang-tidy + +tidy: + $(TIDY) $(CURL_CFILES) $(TIDYFLAGS) -- $(curl_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H + +listhelp: + (cd $(top_srcdir)/docs/cmdline-opts && make listhelp) + +if HAVE_WINDRES +.rc.o: + $(RC) -I$(top_srcdir)/include -DCURL_EMBED_MANIFEST $(RCFLAGS) -i $< -o $@ +endif diff --git a/third_party/curl/src/Makefile.in b/third_party/curl/src/Makefile.in new file mode 100644 index 0000000000..921927077b --- /dev/null +++ b/third_party/curl/src/Makefile.in @@ -0,0 +1,2001 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# ./src/Makefile.inc +# Using the backslash as line continuation character might be problematic with +# some make flavours. If we ever want to change this in a portable manner then +# we should consider this idea : +# CSRC1 = file1.c file2.c file3.c +# CSRC2 = file4.c file5.c file6.c +# CSOURCES = $(CSRC1) $(CSRC2) + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = curl$(EXEEXT) +@USE_CPPFLAG_CURL_STATICLIB_TRUE@am__append_1 = -DCURL_STATICLIB +@HAVE_WINDRES_TRUE@am__append_2 = $(CURL_RCFILES) +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/curl-amissl.m4 \ + $(top_srcdir)/m4/curl-bearssl.m4 \ + $(top_srcdir)/m4/curl-compilers.m4 \ + $(top_srcdir)/m4/curl-confopts.m4 \ + $(top_srcdir)/m4/curl-functions.m4 \ + $(top_srcdir)/m4/curl-gnutls.m4 \ + $(top_srcdir)/m4/curl-mbedtls.m4 \ + $(top_srcdir)/m4/curl-openssl.m4 \ + $(top_srcdir)/m4/curl-override.m4 \ + $(top_srcdir)/m4/curl-reentrant.m4 \ + $(top_srcdir)/m4/curl-rustls.m4 \ + $(top_srcdir)/m4/curl-schannel.m4 \ + $(top_srcdir)/m4/curl-sectransp.m4 \ + $(top_srcdir)/m4/curl-sysconfig.m4 \ + $(top_srcdir)/m4/curl-wolfssl.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/xc-am-iface.m4 \ + $(top_srcdir)/m4/xc-cc-check.m4 \ + $(top_srcdir)/m4/xc-lt-iface.m4 \ + $(top_srcdir)/m4/xc-val-flgs.m4 \ + $(top_srcdir)/m4/zz40-xc-ovr.m4 \ + $(top_srcdir)/m4/zz50-xc-ovr.m4 \ + $(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/lib/curl_config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +libcurltool_la_LIBADD = +am__libcurltool_la_SOURCES_DIST = slist_wc.c terminal.c tool_binmode.c \ + tool_bname.c tool_cb_dbg.c tool_cb_hdr.c tool_cb_prg.c \ + tool_cb_rea.c tool_cb_see.c tool_cb_soc.c tool_cb_wrt.c \ + tool_cfgable.c tool_dirhie.c tool_doswin.c tool_easysrc.c \ + tool_filetime.c tool_findfile.c tool_formparse.c \ + tool_getparam.c tool_getpass.c tool_help.c tool_helpers.c \ + tool_hugehelp.c tool_ipfs.c tool_libinfo.c tool_listhelp.c \ + tool_main.c tool_msgs.c tool_operate.c tool_operhlp.c \ + tool_paramhlp.c tool_parsecfg.c tool_progress.c tool_setopt.c \ + tool_sleep.c tool_stderr.c tool_strdup.c tool_urlglob.c \ + tool_util.c tool_vms.c tool_writeout.c tool_writeout_json.c \ + tool_xattr.c var.c ../lib/base64.c ../lib/curl_multibyte.c \ + ../lib/dynbuf.c ../lib/nonblock.c ../lib/strtoofft.c \ + ../lib/timediff.c ../lib/version_win32.c ../lib/warnless.c \ + slist_wc.h terminal.h tool_binmode.h tool_bname.h \ + tool_cb_dbg.h tool_cb_hdr.h tool_cb_prg.h tool_cb_rea.h \ + tool_cb_see.h tool_cb_soc.h tool_cb_wrt.h tool_cfgable.h \ + tool_dirhie.h tool_doswin.h tool_easysrc.h tool_filetime.h \ + tool_findfile.h tool_formparse.h tool_getparam.h \ + tool_getpass.h tool_help.h tool_helpers.h tool_hugehelp.h \ + tool_ipfs.h tool_libinfo.h tool_main.h tool_msgs.h \ + tool_operate.h tool_operhlp.h tool_paramhlp.h tool_parsecfg.h \ + tool_progress.h tool_sdecls.h tool_setopt.h tool_setup.h \ + tool_sleep.h tool_stderr.h tool_strdup.h tool_urlglob.h \ + tool_util.h tool_version.h tool_vms.h tool_writeout.h \ + tool_writeout_json.h tool_xattr.h var.h +am__objects_1 = libcurltool_la-slist_wc.lo libcurltool_la-terminal.lo \ + libcurltool_la-tool_binmode.lo libcurltool_la-tool_bname.lo \ + libcurltool_la-tool_cb_dbg.lo libcurltool_la-tool_cb_hdr.lo \ + libcurltool_la-tool_cb_prg.lo libcurltool_la-tool_cb_rea.lo \ + libcurltool_la-tool_cb_see.lo libcurltool_la-tool_cb_soc.lo \ + libcurltool_la-tool_cb_wrt.lo libcurltool_la-tool_cfgable.lo \ + libcurltool_la-tool_dirhie.lo libcurltool_la-tool_doswin.lo \ + libcurltool_la-tool_easysrc.lo libcurltool_la-tool_filetime.lo \ + libcurltool_la-tool_findfile.lo \ + libcurltool_la-tool_formparse.lo \ + libcurltool_la-tool_getparam.lo libcurltool_la-tool_getpass.lo \ + libcurltool_la-tool_help.lo libcurltool_la-tool_helpers.lo \ + libcurltool_la-tool_hugehelp.lo libcurltool_la-tool_ipfs.lo \ + libcurltool_la-tool_libinfo.lo libcurltool_la-tool_listhelp.lo \ + libcurltool_la-tool_main.lo libcurltool_la-tool_msgs.lo \ + libcurltool_la-tool_operate.lo libcurltool_la-tool_operhlp.lo \ + libcurltool_la-tool_paramhlp.lo \ + libcurltool_la-tool_parsecfg.lo \ + libcurltool_la-tool_progress.lo libcurltool_la-tool_setopt.lo \ + libcurltool_la-tool_sleep.lo libcurltool_la-tool_stderr.lo \ + libcurltool_la-tool_strdup.lo libcurltool_la-tool_urlglob.lo \ + libcurltool_la-tool_util.lo libcurltool_la-tool_vms.lo \ + libcurltool_la-tool_writeout.lo \ + libcurltool_la-tool_writeout_json.lo \ + libcurltool_la-tool_xattr.lo libcurltool_la-var.lo +am__dirstamp = $(am__leading_dot)dirstamp +am__objects_2 = ../lib/libcurltool_la-base64.lo \ + ../lib/libcurltool_la-curl_multibyte.lo \ + ../lib/libcurltool_la-dynbuf.lo \ + ../lib/libcurltool_la-nonblock.lo \ + ../lib/libcurltool_la-strtoofft.lo \ + ../lib/libcurltool_la-timediff.lo \ + ../lib/libcurltool_la-version_win32.lo \ + ../lib/libcurltool_la-warnless.lo +am__objects_3 = +am__objects_4 = $(am__objects_1) $(am__objects_2) $(am__objects_3) +@BUILD_UNITTESTS_TRUE@am_libcurltool_la_OBJECTS = $(am__objects_4) +libcurltool_la_OBJECTS = $(am_libcurltool_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libcurltool_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(libcurltool_la_CFLAGS) $(CFLAGS) $(libcurltool_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_UNITTESTS_TRUE@am_libcurltool_la_rpath = +am__curl_SOURCES_DIST = slist_wc.c terminal.c tool_binmode.c \ + tool_bname.c tool_cb_dbg.c tool_cb_hdr.c tool_cb_prg.c \ + tool_cb_rea.c tool_cb_see.c tool_cb_soc.c tool_cb_wrt.c \ + tool_cfgable.c tool_dirhie.c tool_doswin.c tool_easysrc.c \ + tool_filetime.c tool_findfile.c tool_formparse.c \ + tool_getparam.c tool_getpass.c tool_help.c tool_helpers.c \ + tool_hugehelp.c tool_ipfs.c tool_libinfo.c tool_listhelp.c \ + tool_main.c tool_msgs.c tool_operate.c tool_operhlp.c \ + tool_paramhlp.c tool_parsecfg.c tool_progress.c tool_setopt.c \ + tool_sleep.c tool_stderr.c tool_strdup.c tool_urlglob.c \ + tool_util.c tool_vms.c tool_writeout.c tool_writeout_json.c \ + tool_xattr.c var.c ../lib/base64.c ../lib/curl_multibyte.c \ + ../lib/dynbuf.c ../lib/nonblock.c ../lib/strtoofft.c \ + ../lib/timediff.c ../lib/version_win32.c ../lib/warnless.c \ + slist_wc.h terminal.h tool_binmode.h tool_bname.h \ + tool_cb_dbg.h tool_cb_hdr.h tool_cb_prg.h tool_cb_rea.h \ + tool_cb_see.h tool_cb_soc.h tool_cb_wrt.h tool_cfgable.h \ + tool_dirhie.h tool_doswin.h tool_easysrc.h tool_filetime.h \ + tool_findfile.h tool_formparse.h tool_getparam.h \ + tool_getpass.h tool_help.h tool_helpers.h tool_hugehelp.h \ + tool_ipfs.h tool_libinfo.h tool_main.h tool_msgs.h \ + tool_operate.h tool_operhlp.h tool_paramhlp.h tool_parsecfg.h \ + tool_progress.h tool_sdecls.h tool_setopt.h tool_setup.h \ + tool_sleep.h tool_stderr.h tool_strdup.h tool_urlglob.h \ + tool_util.h tool_version.h tool_vms.h tool_writeout.h \ + tool_writeout_json.h tool_xattr.h var.h curl.rc +am__objects_5 = slist_wc.$(OBJEXT) terminal.$(OBJEXT) \ + tool_binmode.$(OBJEXT) tool_bname.$(OBJEXT) \ + tool_cb_dbg.$(OBJEXT) tool_cb_hdr.$(OBJEXT) \ + tool_cb_prg.$(OBJEXT) tool_cb_rea.$(OBJEXT) \ + tool_cb_see.$(OBJEXT) tool_cb_soc.$(OBJEXT) \ + tool_cb_wrt.$(OBJEXT) tool_cfgable.$(OBJEXT) \ + tool_dirhie.$(OBJEXT) tool_doswin.$(OBJEXT) \ + tool_easysrc.$(OBJEXT) tool_filetime.$(OBJEXT) \ + tool_findfile.$(OBJEXT) tool_formparse.$(OBJEXT) \ + tool_getparam.$(OBJEXT) tool_getpass.$(OBJEXT) \ + tool_help.$(OBJEXT) tool_helpers.$(OBJEXT) \ + tool_hugehelp.$(OBJEXT) tool_ipfs.$(OBJEXT) \ + tool_libinfo.$(OBJEXT) tool_listhelp.$(OBJEXT) \ + tool_main.$(OBJEXT) tool_msgs.$(OBJEXT) tool_operate.$(OBJEXT) \ + tool_operhlp.$(OBJEXT) tool_paramhlp.$(OBJEXT) \ + tool_parsecfg.$(OBJEXT) tool_progress.$(OBJEXT) \ + tool_setopt.$(OBJEXT) tool_sleep.$(OBJEXT) \ + tool_stderr.$(OBJEXT) tool_strdup.$(OBJEXT) \ + tool_urlglob.$(OBJEXT) tool_util.$(OBJEXT) tool_vms.$(OBJEXT) \ + tool_writeout.$(OBJEXT) tool_writeout_json.$(OBJEXT) \ + tool_xattr.$(OBJEXT) var.$(OBJEXT) +am__objects_6 = ../lib/base64.$(OBJEXT) \ + ../lib/curl_multibyte.$(OBJEXT) ../lib/dynbuf.$(OBJEXT) \ + ../lib/nonblock.$(OBJEXT) ../lib/strtoofft.$(OBJEXT) \ + ../lib/timediff.$(OBJEXT) ../lib/version_win32.$(OBJEXT) \ + ../lib/warnless.$(OBJEXT) +am__objects_7 = $(am__objects_5) $(am__objects_6) $(am__objects_3) +am__objects_8 = curl.$(OBJEXT) +@HAVE_WINDRES_TRUE@am__objects_9 = $(am__objects_8) +am_curl_OBJECTS = $(am__objects_7) $(am__objects_9) +curl_OBJECTS = $(am_curl_OBJECTS) +@USE_EXPLICIT_LIB_DEPS_FALSE@curl_DEPENDENCIES = \ +@USE_EXPLICIT_LIB_DEPS_FALSE@ $(top_builddir)/lib/libcurl.la +@USE_EXPLICIT_LIB_DEPS_TRUE@curl_DEPENDENCIES = \ +@USE_EXPLICIT_LIB_DEPS_TRUE@ $(top_builddir)/lib/libcurl.la +curl_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(curl_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ../lib/$(DEPDIR)/base64.Po \ + ../lib/$(DEPDIR)/curl_multibyte.Po ../lib/$(DEPDIR)/dynbuf.Po \ + ../lib/$(DEPDIR)/libcurltool_la-base64.Plo \ + ../lib/$(DEPDIR)/libcurltool_la-curl_multibyte.Plo \ + ../lib/$(DEPDIR)/libcurltool_la-dynbuf.Plo \ + ../lib/$(DEPDIR)/libcurltool_la-nonblock.Plo \ + ../lib/$(DEPDIR)/libcurltool_la-strtoofft.Plo \ + ../lib/$(DEPDIR)/libcurltool_la-timediff.Plo \ + ../lib/$(DEPDIR)/libcurltool_la-version_win32.Plo \ + ../lib/$(DEPDIR)/libcurltool_la-warnless.Plo \ + ../lib/$(DEPDIR)/nonblock.Po ../lib/$(DEPDIR)/strtoofft.Po \ + ../lib/$(DEPDIR)/timediff.Po ../lib/$(DEPDIR)/version_win32.Po \ + ../lib/$(DEPDIR)/warnless.Po \ + ./$(DEPDIR)/libcurltool_la-slist_wc.Plo \ + ./$(DEPDIR)/libcurltool_la-terminal.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_binmode.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_bname.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_cb_dbg.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_cb_hdr.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_cb_prg.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_cb_rea.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_cb_see.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_cb_soc.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_cb_wrt.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_cfgable.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_dirhie.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_doswin.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_easysrc.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_filetime.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_findfile.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_formparse.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_getparam.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_getpass.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_help.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_helpers.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_hugehelp.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_ipfs.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_libinfo.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_listhelp.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_main.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_msgs.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_operate.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_operhlp.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_paramhlp.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_parsecfg.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_progress.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_setopt.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_sleep.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_stderr.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_strdup.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_urlglob.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_util.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_vms.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_writeout.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_writeout_json.Plo \ + ./$(DEPDIR)/libcurltool_la-tool_xattr.Plo \ + ./$(DEPDIR)/libcurltool_la-var.Plo ./$(DEPDIR)/slist_wc.Po \ + ./$(DEPDIR)/terminal.Po ./$(DEPDIR)/tool_binmode.Po \ + ./$(DEPDIR)/tool_bname.Po ./$(DEPDIR)/tool_cb_dbg.Po \ + ./$(DEPDIR)/tool_cb_hdr.Po ./$(DEPDIR)/tool_cb_prg.Po \ + ./$(DEPDIR)/tool_cb_rea.Po ./$(DEPDIR)/tool_cb_see.Po \ + ./$(DEPDIR)/tool_cb_soc.Po ./$(DEPDIR)/tool_cb_wrt.Po \ + ./$(DEPDIR)/tool_cfgable.Po ./$(DEPDIR)/tool_dirhie.Po \ + ./$(DEPDIR)/tool_doswin.Po ./$(DEPDIR)/tool_easysrc.Po \ + ./$(DEPDIR)/tool_filetime.Po ./$(DEPDIR)/tool_findfile.Po \ + ./$(DEPDIR)/tool_formparse.Po ./$(DEPDIR)/tool_getparam.Po \ + ./$(DEPDIR)/tool_getpass.Po ./$(DEPDIR)/tool_help.Po \ + ./$(DEPDIR)/tool_helpers.Po ./$(DEPDIR)/tool_hugehelp.Po \ + ./$(DEPDIR)/tool_ipfs.Po ./$(DEPDIR)/tool_libinfo.Po \ + ./$(DEPDIR)/tool_listhelp.Po ./$(DEPDIR)/tool_main.Po \ + ./$(DEPDIR)/tool_msgs.Po ./$(DEPDIR)/tool_operate.Po \ + ./$(DEPDIR)/tool_operhlp.Po ./$(DEPDIR)/tool_paramhlp.Po \ + ./$(DEPDIR)/tool_parsecfg.Po ./$(DEPDIR)/tool_progress.Po \ + ./$(DEPDIR)/tool_setopt.Po ./$(DEPDIR)/tool_sleep.Po \ + ./$(DEPDIR)/tool_stderr.Po ./$(DEPDIR)/tool_strdup.Po \ + ./$(DEPDIR)/tool_urlglob.Po ./$(DEPDIR)/tool_util.Po \ + ./$(DEPDIR)/tool_vms.Po ./$(DEPDIR)/tool_writeout.Po \ + ./$(DEPDIR)/tool_writeout_json.Po ./$(DEPDIR)/tool_xattr.Po \ + ./$(DEPDIR)/var.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libcurltool_la_SOURCES) $(curl_SOURCES) +DIST_SOURCES = $(am__libcurltool_la_SOURCES_DIST) \ + $(am__curl_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.inc \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APACHECTL = @APACHECTL@ +APXS = @APXS@ +AR = @AR@ +AR_FLAGS = @AR_FLAGS@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@ +CADDY = @CADDY@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ + +# This might hold -Werror +CFLAGS = @CFLAGS@ @CURL_CFLAG_EXTRAS@ +CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@ +CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURLVERSION = @CURLVERSION@ +CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ +CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@ +CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ +CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ +CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ +CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ +CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ +CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@ +CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ +CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@ +CURL_DISABLE_MQTT = @CURL_DISABLE_MQTT@ +CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@ +CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@ +CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@ +CURL_DISABLE_SMB = @CURL_DISABLE_SMB@ +CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@ +CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ +CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@ +CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@ +CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@ +CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@ +CURL_WITH_MULTI_SSL = @CURL_WITH_MULTI_SSL@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_SSL_BACKEND = @DEFAULT_SSL_BACKEND@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_SHARED = @ENABLE_SHARED@ +ENABLE_STATIC = @ENABLE_STATIC@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@ +GCOV = @GCOV@ +GREP = @GREP@ +HAVE_BROTLI = @HAVE_BROTLI@ +HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@ +HAVE_LDAP_SSL = @HAVE_LDAP_SSL@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_OPENSSL_QUIC = @HAVE_OPENSSL_QUIC@ +HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@ +HAVE_PROTO_BSDSOCKET_H = @HAVE_PROTO_BSDSOCKET_H@ +HAVE_ZSTD = @HAVE_ZSTD@ +HTTPD = @HTTPD@ +HTTPD_NGHTTPX = @HTTPD_NGHTTPX@ +IDN_ENABLED = @IDN_ENABLED@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPV6_ENABLED = @IPV6_ENABLED@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCURL_LIBS = @LIBCURL_LIBS@ +LIBCURL_NO_SHARED = @LIBCURL_NO_SHARED@ +LIBCURL_PC_REQUIRES = @LIBCURL_PC_REQUIRES@ +LIBCURL_PC_REQUIRES_PRIVATE = @LIBCURL_PC_REQUIRES_PRIVATE@ +LIBOBJS = @LIBOBJS@ + +# Prevent LIBS from being used for all link targets +LIBS = $(BLANK_AT_MAKETIME) +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKGADD_NAME = @PKGADD_NAME@ +PKGADD_PKG = @PKGADD_PKG@ +PKGADD_VENDOR = @PKGADD_VENDOR@ +PKGCONFIG = @PKGCONFIG@ +RANDOM_FILE = @RANDOM_FILE@ +RANLIB = @RANLIB@ +RC = @RC@ +REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSL_BACKENDS = @SSL_BACKENDS@ +SSL_ENABLED = @SSL_ENABLED@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SUPPORT_FEATURES = @SUPPORT_FEATURES@ +SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@ +TEST_NGHTTPX = @TEST_NGHTTPX@ +USE_ARES = @USE_ARES@ +USE_BEARSSL = @USE_BEARSSL@ +USE_GNUTLS = @USE_GNUTLS@ +USE_HYPER = @USE_HYPER@ +USE_LIBPSL = @USE_LIBPSL@ +USE_LIBRTMP = @USE_LIBRTMP@ +USE_LIBSSH = @USE_LIBSSH@ +USE_LIBSSH2 = @USE_LIBSSH2@ +USE_MBEDTLS = @USE_MBEDTLS@ +USE_MSH3 = @USE_MSH3@ +USE_NGHTTP2 = @USE_NGHTTP2@ +USE_NGHTTP3 = @USE_NGHTTP3@ +USE_NGTCP2 = @USE_NGTCP2@ +USE_NGTCP2_CRYPTO_BORINGSSL = @USE_NGTCP2_CRYPTO_BORINGSSL@ +USE_NGTCP2_CRYPTO_GNUTLS = @USE_NGTCP2_CRYPTO_GNUTLS@ +USE_NGTCP2_CRYPTO_QUICTLS = @USE_NGTCP2_CRYPTO_QUICTLS@ +USE_NGTCP2_CRYPTO_WOLFSSL = @USE_NGTCP2_CRYPTO_WOLFSSL@ +USE_NGTCP2_H3 = @USE_NGTCP2_H3@ +USE_OPENLDAP = @USE_OPENLDAP@ +USE_OPENSSL_H3 = @USE_OPENSSL_H3@ +USE_OPENSSL_QUIC = @USE_OPENSSL_QUIC@ +USE_QUICHE = @USE_QUICHE@ +USE_RUSTLS = @USE_RUSTLS@ +USE_SCHANNEL = @USE_SCHANNEL@ +USE_SECTRANSP = @USE_SECTRANSP@ +USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@ +USE_WIN32_CRYPTO = @USE_WIN32_CRYPTO@ +USE_WIN32_LARGE_FILES = @USE_WIN32_LARGE_FILES@ +USE_WIN32_SMALL_FILES = @USE_WIN32_SMALL_FILES@ +USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@ +USE_WOLFSSH = @USE_WOLFSSH@ +USE_WOLFSSL = @USE_WOLFSSL@ +VERSION = @VERSION@ +VERSIONNUM = @VERSIONNUM@ +VSFTPD = @VSFTPD@ +ZLIB_LIBS = @ZLIB_LIBS@ +ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libext = @libext@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +AUTOMAKE_OPTIONS = foreign nostdinc + +# Specify our include paths here, and do it relative to $(top_srcdir) and +# $(top_builddir), to ensure that these paths which belong to the library +# being currently built and tested are searched before the library which +# might possibly already be installed in the system. +# +# $(top_srcdir)/include is for libcurl's external include files +# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file +# $(top_builddir)/src is for curl's generated src/curl_config.h file +# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files +# $(top_srcdir)/src is for curl's src/tool_setup.h and "curl-private" files +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/lib \ + -I$(top_builddir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/src \ + $(am__append_1) -DBUILDING_CURL + +# libcurl sources to include in curltool lib we use for test binaries +CURLTOOL_LIBCURL_CFILES = \ + ../lib/base64.c \ + ../lib/dynbuf.c + + +# libcurl has sources that provide functions named curlx_* that are not part of +# the official API, but we reuse the code here to avoid duplication. +CURLX_CFILES = \ + ../lib/base64.c \ + ../lib/curl_multibyte.c \ + ../lib/dynbuf.c \ + ../lib/nonblock.c \ + ../lib/strtoofft.c \ + ../lib/timediff.c \ + ../lib/version_win32.c \ + ../lib/warnless.c + +CURLX_HFILES = \ + ../lib/curl_ctype.h \ + ../lib/curl_multibyte.h \ + ../lib/curl_setup.h \ + ../lib/dynbuf.h \ + ../lib/nonblock.h \ + ../lib/strtoofft.h \ + ../lib/timediff.h \ + ../lib/version_win32.h \ + ../lib/warnless.h + +CURL_CFILES = \ + slist_wc.c \ + terminal.c \ + tool_binmode.c \ + tool_bname.c \ + tool_cb_dbg.c \ + tool_cb_hdr.c \ + tool_cb_prg.c \ + tool_cb_rea.c \ + tool_cb_see.c \ + tool_cb_soc.c \ + tool_cb_wrt.c \ + tool_cfgable.c \ + tool_dirhie.c \ + tool_doswin.c \ + tool_easysrc.c \ + tool_filetime.c \ + tool_findfile.c \ + tool_formparse.c \ + tool_getparam.c \ + tool_getpass.c \ + tool_help.c \ + tool_helpers.c \ + tool_hugehelp.c \ + tool_ipfs.c \ + tool_libinfo.c \ + tool_listhelp.c \ + tool_main.c \ + tool_msgs.c \ + tool_operate.c \ + tool_operhlp.c \ + tool_paramhlp.c \ + tool_parsecfg.c \ + tool_progress.c \ + tool_setopt.c \ + tool_sleep.c \ + tool_stderr.c \ + tool_strdup.c \ + tool_urlglob.c \ + tool_util.c \ + tool_vms.c \ + tool_writeout.c \ + tool_writeout_json.c \ + tool_xattr.c \ + var.c + +CURL_HFILES = \ + slist_wc.h \ + terminal.h \ + tool_binmode.h \ + tool_bname.h \ + tool_cb_dbg.h \ + tool_cb_hdr.h \ + tool_cb_prg.h \ + tool_cb_rea.h \ + tool_cb_see.h \ + tool_cb_soc.h \ + tool_cb_wrt.h \ + tool_cfgable.h \ + tool_dirhie.h \ + tool_doswin.h \ + tool_easysrc.h \ + tool_filetime.h \ + tool_findfile.h \ + tool_formparse.h \ + tool_getparam.h \ + tool_getpass.h \ + tool_help.h \ + tool_helpers.h \ + tool_hugehelp.h \ + tool_ipfs.h \ + tool_libinfo.h \ + tool_main.h \ + tool_msgs.h \ + tool_operate.h \ + tool_operhlp.h \ + tool_paramhlp.h \ + tool_parsecfg.h \ + tool_progress.h \ + tool_sdecls.h \ + tool_setopt.h \ + tool_setup.h \ + tool_sleep.h \ + tool_stderr.h \ + tool_strdup.h \ + tool_urlglob.h \ + tool_util.h \ + tool_version.h \ + tool_vms.h \ + tool_writeout.h \ + tool_writeout_json.h \ + tool_xattr.h \ + var.h + +CURL_RCFILES = curl.rc + +# curl_SOURCES is special and gets assigned in src/Makefile.am +CURL_FILES = $(CURL_CFILES) $(CURLX_CFILES) $(CURL_HFILES) + +# CURL_FILES comes from Makefile.inc +curl_SOURCES = $(CURL_FILES) $(am__append_2) +curl_LDFLAGS = $(AM_LDFLAGS) $(CURL_LDFLAGS_BIN) +@USE_EXPLICIT_LIB_DEPS_FALSE@curl_LDADD = $(top_builddir)/lib/libcurl.la @SSL_LIBS@ @ZLIB_LIBS@ @CURL_NETWORK_AND_TIME_LIBS@ +@USE_EXPLICIT_LIB_DEPS_TRUE@curl_LDADD = $(top_builddir)/lib/libcurl.la @LIBCURL_LIBS@ + +# if unit tests are enabled, build a static library to link them with +@BUILD_UNITTESTS_TRUE@noinst_LTLIBRARIES = libcurltool.la +@BUILD_UNITTESTS_TRUE@libcurltool_la_CPPFLAGS = $(AM_CPPFLAGS) \ +@BUILD_UNITTESTS_TRUE@ -DCURL_STATICLIB -DUNITTESTS + +@BUILD_UNITTESTS_TRUE@libcurltool_la_CFLAGS = +@BUILD_UNITTESTS_TRUE@libcurltool_la_LDFLAGS = -static $(LINKFLAGS) +@BUILD_UNITTESTS_TRUE@libcurltool_la_SOURCES = $(CURL_FILES) +CLEANFILES = tool_hugehelp.c +# Use the C locale to ensure that only ASCII characters appear in the +# embedded text. +NROFF = env LC_ALL=C @NROFF@ @MANOPT@ 2>/dev/null # figured out by the configure script +EXTRA_DIST = mkhelp.pl \ + Makefile.mk curl.rc Makefile.inc CMakeLists.txt .checksrc + + +# Use absolute directory to disable VPATH +ASCIIPAGE = $(top_builddir)/docs/cmdline-opts/curl.txt +MKHELP = $(top_srcdir)/src/mkhelp.pl +HUGE = tool_hugehelp.c +HUGECMD = $(HUGEIT_$(V)) +HUGEIT_0 = @echo " HUGE " $@; +HUGEIT_1 = +HUGEIT_ = $(HUGEIT_0) +CHECKSRC = $(CS_$(V)) +CS_0 = @echo " RUN " $@; +CS_1 = +CS_ = $(CS_0) + +# disable the tests that are mostly causing false positives +TIDYFLAGS = -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-valist.Uninitialized,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference +TIDY := clang-tidy +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj .rc +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.inc $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(srcdir)/Makefile.inc $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +../lib/$(am__dirstamp): + @$(MKDIR_P) ../lib + @: > ../lib/$(am__dirstamp) +../lib/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ../lib/$(DEPDIR) + @: > ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/libcurltool_la-base64.lo: ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/libcurltool_la-curl_multibyte.lo: ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/libcurltool_la-dynbuf.lo: ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/libcurltool_la-nonblock.lo: ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/libcurltool_la-strtoofft.lo: ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/libcurltool_la-timediff.lo: ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/libcurltool_la-version_win32.lo: ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/libcurltool_la-warnless.lo: ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) + +libcurltool.la: $(libcurltool_la_OBJECTS) $(libcurltool_la_DEPENDENCIES) $(EXTRA_libcurltool_la_DEPENDENCIES) + $(AM_V_CCLD)$(libcurltool_la_LINK) $(am_libcurltool_la_rpath) $(libcurltool_la_OBJECTS) $(libcurltool_la_LIBADD) $(LIBS) +../lib/base64.$(OBJEXT): ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/curl_multibyte.$(OBJEXT): ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/dynbuf.$(OBJEXT): ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/nonblock.$(OBJEXT): ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/strtoofft.$(OBJEXT): ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/timediff.$(OBJEXT): ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/version_win32.$(OBJEXT): ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) +../lib/warnless.$(OBJEXT): ../lib/$(am__dirstamp) \ + ../lib/$(DEPDIR)/$(am__dirstamp) + +curl$(EXEEXT): $(curl_OBJECTS) $(curl_DEPENDENCIES) $(EXTRA_curl_DEPENDENCIES) + @rm -f curl$(EXEEXT) + $(AM_V_CCLD)$(curl_LINK) $(curl_OBJECTS) $(curl_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f ../lib/*.$(OBJEXT) + -rm -f ../lib/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/base64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/curl_multibyte.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/dynbuf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/libcurltool_la-base64.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/libcurltool_la-curl_multibyte.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/libcurltool_la-dynbuf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/libcurltool_la-nonblock.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/libcurltool_la-strtoofft.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/libcurltool_la-timediff.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/libcurltool_la-version_win32.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/libcurltool_la-warnless.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/nonblock.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/strtoofft.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/timediff.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/version_win32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@../lib/$(DEPDIR)/warnless.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-slist_wc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-terminal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_binmode.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_bname.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_cb_dbg.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_cb_hdr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_cb_prg.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_cb_rea.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_cb_see.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_cb_soc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_cb_wrt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_cfgable.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_dirhie.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_doswin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_easysrc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_filetime.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_findfile.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_formparse.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_getparam.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_getpass.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_help.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_helpers.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_hugehelp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_ipfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_libinfo.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_listhelp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_main.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_msgs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_operate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_operhlp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_paramhlp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_parsecfg.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_progress.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_setopt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_sleep.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_stderr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_strdup.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_urlglob.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_util.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_vms.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_writeout.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_writeout_json.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-tool_xattr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurltool_la-var.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slist_wc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/terminal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_binmode.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_bname.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_cb_dbg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_cb_hdr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_cb_prg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_cb_rea.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_cb_see.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_cb_soc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_cb_wrt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_cfgable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_dirhie.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_doswin.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_easysrc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_filetime.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_findfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_formparse.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_getparam.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_getpass.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_help.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_helpers.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_hugehelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_ipfs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_libinfo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_listhelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_msgs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_operate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_operhlp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_paramhlp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_parsecfg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_progress.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_setopt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_sleep.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_stderr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_strdup.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_urlglob.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_util.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_vms.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_writeout.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_writeout_json.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool_xattr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/var.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libcurltool_la-slist_wc.lo: slist_wc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-slist_wc.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-slist_wc.Tpo -c -o libcurltool_la-slist_wc.lo `test -f 'slist_wc.c' || echo '$(srcdir)/'`slist_wc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-slist_wc.Tpo $(DEPDIR)/libcurltool_la-slist_wc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='slist_wc.c' object='libcurltool_la-slist_wc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-slist_wc.lo `test -f 'slist_wc.c' || echo '$(srcdir)/'`slist_wc.c + +libcurltool_la-terminal.lo: terminal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-terminal.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-terminal.Tpo -c -o libcurltool_la-terminal.lo `test -f 'terminal.c' || echo '$(srcdir)/'`terminal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-terminal.Tpo $(DEPDIR)/libcurltool_la-terminal.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='terminal.c' object='libcurltool_la-terminal.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-terminal.lo `test -f 'terminal.c' || echo '$(srcdir)/'`terminal.c + +libcurltool_la-tool_binmode.lo: tool_binmode.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_binmode.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_binmode.Tpo -c -o libcurltool_la-tool_binmode.lo `test -f 'tool_binmode.c' || echo '$(srcdir)/'`tool_binmode.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_binmode.Tpo $(DEPDIR)/libcurltool_la-tool_binmode.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_binmode.c' object='libcurltool_la-tool_binmode.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_binmode.lo `test -f 'tool_binmode.c' || echo '$(srcdir)/'`tool_binmode.c + +libcurltool_la-tool_bname.lo: tool_bname.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_bname.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_bname.Tpo -c -o libcurltool_la-tool_bname.lo `test -f 'tool_bname.c' || echo '$(srcdir)/'`tool_bname.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_bname.Tpo $(DEPDIR)/libcurltool_la-tool_bname.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_bname.c' object='libcurltool_la-tool_bname.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_bname.lo `test -f 'tool_bname.c' || echo '$(srcdir)/'`tool_bname.c + +libcurltool_la-tool_cb_dbg.lo: tool_cb_dbg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_cb_dbg.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_cb_dbg.Tpo -c -o libcurltool_la-tool_cb_dbg.lo `test -f 'tool_cb_dbg.c' || echo '$(srcdir)/'`tool_cb_dbg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_cb_dbg.Tpo $(DEPDIR)/libcurltool_la-tool_cb_dbg.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_cb_dbg.c' object='libcurltool_la-tool_cb_dbg.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_cb_dbg.lo `test -f 'tool_cb_dbg.c' || echo '$(srcdir)/'`tool_cb_dbg.c + +libcurltool_la-tool_cb_hdr.lo: tool_cb_hdr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_cb_hdr.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_cb_hdr.Tpo -c -o libcurltool_la-tool_cb_hdr.lo `test -f 'tool_cb_hdr.c' || echo '$(srcdir)/'`tool_cb_hdr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_cb_hdr.Tpo $(DEPDIR)/libcurltool_la-tool_cb_hdr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_cb_hdr.c' object='libcurltool_la-tool_cb_hdr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_cb_hdr.lo `test -f 'tool_cb_hdr.c' || echo '$(srcdir)/'`tool_cb_hdr.c + +libcurltool_la-tool_cb_prg.lo: tool_cb_prg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_cb_prg.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_cb_prg.Tpo -c -o libcurltool_la-tool_cb_prg.lo `test -f 'tool_cb_prg.c' || echo '$(srcdir)/'`tool_cb_prg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_cb_prg.Tpo $(DEPDIR)/libcurltool_la-tool_cb_prg.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_cb_prg.c' object='libcurltool_la-tool_cb_prg.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_cb_prg.lo `test -f 'tool_cb_prg.c' || echo '$(srcdir)/'`tool_cb_prg.c + +libcurltool_la-tool_cb_rea.lo: tool_cb_rea.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_cb_rea.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_cb_rea.Tpo -c -o libcurltool_la-tool_cb_rea.lo `test -f 'tool_cb_rea.c' || echo '$(srcdir)/'`tool_cb_rea.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_cb_rea.Tpo $(DEPDIR)/libcurltool_la-tool_cb_rea.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_cb_rea.c' object='libcurltool_la-tool_cb_rea.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_cb_rea.lo `test -f 'tool_cb_rea.c' || echo '$(srcdir)/'`tool_cb_rea.c + +libcurltool_la-tool_cb_see.lo: tool_cb_see.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_cb_see.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_cb_see.Tpo -c -o libcurltool_la-tool_cb_see.lo `test -f 'tool_cb_see.c' || echo '$(srcdir)/'`tool_cb_see.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_cb_see.Tpo $(DEPDIR)/libcurltool_la-tool_cb_see.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_cb_see.c' object='libcurltool_la-tool_cb_see.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_cb_see.lo `test -f 'tool_cb_see.c' || echo '$(srcdir)/'`tool_cb_see.c + +libcurltool_la-tool_cb_soc.lo: tool_cb_soc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_cb_soc.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_cb_soc.Tpo -c -o libcurltool_la-tool_cb_soc.lo `test -f 'tool_cb_soc.c' || echo '$(srcdir)/'`tool_cb_soc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_cb_soc.Tpo $(DEPDIR)/libcurltool_la-tool_cb_soc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_cb_soc.c' object='libcurltool_la-tool_cb_soc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_cb_soc.lo `test -f 'tool_cb_soc.c' || echo '$(srcdir)/'`tool_cb_soc.c + +libcurltool_la-tool_cb_wrt.lo: tool_cb_wrt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_cb_wrt.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_cb_wrt.Tpo -c -o libcurltool_la-tool_cb_wrt.lo `test -f 'tool_cb_wrt.c' || echo '$(srcdir)/'`tool_cb_wrt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_cb_wrt.Tpo $(DEPDIR)/libcurltool_la-tool_cb_wrt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_cb_wrt.c' object='libcurltool_la-tool_cb_wrt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_cb_wrt.lo `test -f 'tool_cb_wrt.c' || echo '$(srcdir)/'`tool_cb_wrt.c + +libcurltool_la-tool_cfgable.lo: tool_cfgable.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_cfgable.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_cfgable.Tpo -c -o libcurltool_la-tool_cfgable.lo `test -f 'tool_cfgable.c' || echo '$(srcdir)/'`tool_cfgable.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_cfgable.Tpo $(DEPDIR)/libcurltool_la-tool_cfgable.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_cfgable.c' object='libcurltool_la-tool_cfgable.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_cfgable.lo `test -f 'tool_cfgable.c' || echo '$(srcdir)/'`tool_cfgable.c + +libcurltool_la-tool_dirhie.lo: tool_dirhie.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_dirhie.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_dirhie.Tpo -c -o libcurltool_la-tool_dirhie.lo `test -f 'tool_dirhie.c' || echo '$(srcdir)/'`tool_dirhie.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_dirhie.Tpo $(DEPDIR)/libcurltool_la-tool_dirhie.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_dirhie.c' object='libcurltool_la-tool_dirhie.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_dirhie.lo `test -f 'tool_dirhie.c' || echo '$(srcdir)/'`tool_dirhie.c + +libcurltool_la-tool_doswin.lo: tool_doswin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_doswin.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_doswin.Tpo -c -o libcurltool_la-tool_doswin.lo `test -f 'tool_doswin.c' || echo '$(srcdir)/'`tool_doswin.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_doswin.Tpo $(DEPDIR)/libcurltool_la-tool_doswin.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_doswin.c' object='libcurltool_la-tool_doswin.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_doswin.lo `test -f 'tool_doswin.c' || echo '$(srcdir)/'`tool_doswin.c + +libcurltool_la-tool_easysrc.lo: tool_easysrc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_easysrc.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_easysrc.Tpo -c -o libcurltool_la-tool_easysrc.lo `test -f 'tool_easysrc.c' || echo '$(srcdir)/'`tool_easysrc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_easysrc.Tpo $(DEPDIR)/libcurltool_la-tool_easysrc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_easysrc.c' object='libcurltool_la-tool_easysrc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_easysrc.lo `test -f 'tool_easysrc.c' || echo '$(srcdir)/'`tool_easysrc.c + +libcurltool_la-tool_filetime.lo: tool_filetime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_filetime.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_filetime.Tpo -c -o libcurltool_la-tool_filetime.lo `test -f 'tool_filetime.c' || echo '$(srcdir)/'`tool_filetime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_filetime.Tpo $(DEPDIR)/libcurltool_la-tool_filetime.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_filetime.c' object='libcurltool_la-tool_filetime.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_filetime.lo `test -f 'tool_filetime.c' || echo '$(srcdir)/'`tool_filetime.c + +libcurltool_la-tool_findfile.lo: tool_findfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_findfile.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_findfile.Tpo -c -o libcurltool_la-tool_findfile.lo `test -f 'tool_findfile.c' || echo '$(srcdir)/'`tool_findfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_findfile.Tpo $(DEPDIR)/libcurltool_la-tool_findfile.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_findfile.c' object='libcurltool_la-tool_findfile.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_findfile.lo `test -f 'tool_findfile.c' || echo '$(srcdir)/'`tool_findfile.c + +libcurltool_la-tool_formparse.lo: tool_formparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_formparse.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_formparse.Tpo -c -o libcurltool_la-tool_formparse.lo `test -f 'tool_formparse.c' || echo '$(srcdir)/'`tool_formparse.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_formparse.Tpo $(DEPDIR)/libcurltool_la-tool_formparse.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_formparse.c' object='libcurltool_la-tool_formparse.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_formparse.lo `test -f 'tool_formparse.c' || echo '$(srcdir)/'`tool_formparse.c + +libcurltool_la-tool_getparam.lo: tool_getparam.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_getparam.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_getparam.Tpo -c -o libcurltool_la-tool_getparam.lo `test -f 'tool_getparam.c' || echo '$(srcdir)/'`tool_getparam.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_getparam.Tpo $(DEPDIR)/libcurltool_la-tool_getparam.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_getparam.c' object='libcurltool_la-tool_getparam.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_getparam.lo `test -f 'tool_getparam.c' || echo '$(srcdir)/'`tool_getparam.c + +libcurltool_la-tool_getpass.lo: tool_getpass.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_getpass.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_getpass.Tpo -c -o libcurltool_la-tool_getpass.lo `test -f 'tool_getpass.c' || echo '$(srcdir)/'`tool_getpass.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_getpass.Tpo $(DEPDIR)/libcurltool_la-tool_getpass.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_getpass.c' object='libcurltool_la-tool_getpass.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_getpass.lo `test -f 'tool_getpass.c' || echo '$(srcdir)/'`tool_getpass.c + +libcurltool_la-tool_help.lo: tool_help.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_help.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_help.Tpo -c -o libcurltool_la-tool_help.lo `test -f 'tool_help.c' || echo '$(srcdir)/'`tool_help.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_help.Tpo $(DEPDIR)/libcurltool_la-tool_help.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_help.c' object='libcurltool_la-tool_help.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_help.lo `test -f 'tool_help.c' || echo '$(srcdir)/'`tool_help.c + +libcurltool_la-tool_helpers.lo: tool_helpers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_helpers.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_helpers.Tpo -c -o libcurltool_la-tool_helpers.lo `test -f 'tool_helpers.c' || echo '$(srcdir)/'`tool_helpers.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_helpers.Tpo $(DEPDIR)/libcurltool_la-tool_helpers.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_helpers.c' object='libcurltool_la-tool_helpers.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_helpers.lo `test -f 'tool_helpers.c' || echo '$(srcdir)/'`tool_helpers.c + +libcurltool_la-tool_hugehelp.lo: tool_hugehelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_hugehelp.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_hugehelp.Tpo -c -o libcurltool_la-tool_hugehelp.lo `test -f 'tool_hugehelp.c' || echo '$(srcdir)/'`tool_hugehelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_hugehelp.Tpo $(DEPDIR)/libcurltool_la-tool_hugehelp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_hugehelp.c' object='libcurltool_la-tool_hugehelp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_hugehelp.lo `test -f 'tool_hugehelp.c' || echo '$(srcdir)/'`tool_hugehelp.c + +libcurltool_la-tool_ipfs.lo: tool_ipfs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_ipfs.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_ipfs.Tpo -c -o libcurltool_la-tool_ipfs.lo `test -f 'tool_ipfs.c' || echo '$(srcdir)/'`tool_ipfs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_ipfs.Tpo $(DEPDIR)/libcurltool_la-tool_ipfs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_ipfs.c' object='libcurltool_la-tool_ipfs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_ipfs.lo `test -f 'tool_ipfs.c' || echo '$(srcdir)/'`tool_ipfs.c + +libcurltool_la-tool_libinfo.lo: tool_libinfo.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_libinfo.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_libinfo.Tpo -c -o libcurltool_la-tool_libinfo.lo `test -f 'tool_libinfo.c' || echo '$(srcdir)/'`tool_libinfo.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_libinfo.Tpo $(DEPDIR)/libcurltool_la-tool_libinfo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_libinfo.c' object='libcurltool_la-tool_libinfo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_libinfo.lo `test -f 'tool_libinfo.c' || echo '$(srcdir)/'`tool_libinfo.c + +libcurltool_la-tool_listhelp.lo: tool_listhelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_listhelp.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_listhelp.Tpo -c -o libcurltool_la-tool_listhelp.lo `test -f 'tool_listhelp.c' || echo '$(srcdir)/'`tool_listhelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_listhelp.Tpo $(DEPDIR)/libcurltool_la-tool_listhelp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_listhelp.c' object='libcurltool_la-tool_listhelp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_listhelp.lo `test -f 'tool_listhelp.c' || echo '$(srcdir)/'`tool_listhelp.c + +libcurltool_la-tool_main.lo: tool_main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_main.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_main.Tpo -c -o libcurltool_la-tool_main.lo `test -f 'tool_main.c' || echo '$(srcdir)/'`tool_main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_main.Tpo $(DEPDIR)/libcurltool_la-tool_main.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_main.c' object='libcurltool_la-tool_main.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_main.lo `test -f 'tool_main.c' || echo '$(srcdir)/'`tool_main.c + +libcurltool_la-tool_msgs.lo: tool_msgs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_msgs.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_msgs.Tpo -c -o libcurltool_la-tool_msgs.lo `test -f 'tool_msgs.c' || echo '$(srcdir)/'`tool_msgs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_msgs.Tpo $(DEPDIR)/libcurltool_la-tool_msgs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_msgs.c' object='libcurltool_la-tool_msgs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_msgs.lo `test -f 'tool_msgs.c' || echo '$(srcdir)/'`tool_msgs.c + +libcurltool_la-tool_operate.lo: tool_operate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_operate.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_operate.Tpo -c -o libcurltool_la-tool_operate.lo `test -f 'tool_operate.c' || echo '$(srcdir)/'`tool_operate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_operate.Tpo $(DEPDIR)/libcurltool_la-tool_operate.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_operate.c' object='libcurltool_la-tool_operate.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_operate.lo `test -f 'tool_operate.c' || echo '$(srcdir)/'`tool_operate.c + +libcurltool_la-tool_operhlp.lo: tool_operhlp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_operhlp.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_operhlp.Tpo -c -o libcurltool_la-tool_operhlp.lo `test -f 'tool_operhlp.c' || echo '$(srcdir)/'`tool_operhlp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_operhlp.Tpo $(DEPDIR)/libcurltool_la-tool_operhlp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_operhlp.c' object='libcurltool_la-tool_operhlp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_operhlp.lo `test -f 'tool_operhlp.c' || echo '$(srcdir)/'`tool_operhlp.c + +libcurltool_la-tool_paramhlp.lo: tool_paramhlp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_paramhlp.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_paramhlp.Tpo -c -o libcurltool_la-tool_paramhlp.lo `test -f 'tool_paramhlp.c' || echo '$(srcdir)/'`tool_paramhlp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_paramhlp.Tpo $(DEPDIR)/libcurltool_la-tool_paramhlp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_paramhlp.c' object='libcurltool_la-tool_paramhlp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_paramhlp.lo `test -f 'tool_paramhlp.c' || echo '$(srcdir)/'`tool_paramhlp.c + +libcurltool_la-tool_parsecfg.lo: tool_parsecfg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_parsecfg.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_parsecfg.Tpo -c -o libcurltool_la-tool_parsecfg.lo `test -f 'tool_parsecfg.c' || echo '$(srcdir)/'`tool_parsecfg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_parsecfg.Tpo $(DEPDIR)/libcurltool_la-tool_parsecfg.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_parsecfg.c' object='libcurltool_la-tool_parsecfg.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_parsecfg.lo `test -f 'tool_parsecfg.c' || echo '$(srcdir)/'`tool_parsecfg.c + +libcurltool_la-tool_progress.lo: tool_progress.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_progress.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_progress.Tpo -c -o libcurltool_la-tool_progress.lo `test -f 'tool_progress.c' || echo '$(srcdir)/'`tool_progress.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_progress.Tpo $(DEPDIR)/libcurltool_la-tool_progress.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_progress.c' object='libcurltool_la-tool_progress.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_progress.lo `test -f 'tool_progress.c' || echo '$(srcdir)/'`tool_progress.c + +libcurltool_la-tool_setopt.lo: tool_setopt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_setopt.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_setopt.Tpo -c -o libcurltool_la-tool_setopt.lo `test -f 'tool_setopt.c' || echo '$(srcdir)/'`tool_setopt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_setopt.Tpo $(DEPDIR)/libcurltool_la-tool_setopt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_setopt.c' object='libcurltool_la-tool_setopt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_setopt.lo `test -f 'tool_setopt.c' || echo '$(srcdir)/'`tool_setopt.c + +libcurltool_la-tool_sleep.lo: tool_sleep.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_sleep.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_sleep.Tpo -c -o libcurltool_la-tool_sleep.lo `test -f 'tool_sleep.c' || echo '$(srcdir)/'`tool_sleep.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_sleep.Tpo $(DEPDIR)/libcurltool_la-tool_sleep.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_sleep.c' object='libcurltool_la-tool_sleep.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_sleep.lo `test -f 'tool_sleep.c' || echo '$(srcdir)/'`tool_sleep.c + +libcurltool_la-tool_stderr.lo: tool_stderr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_stderr.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_stderr.Tpo -c -o libcurltool_la-tool_stderr.lo `test -f 'tool_stderr.c' || echo '$(srcdir)/'`tool_stderr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_stderr.Tpo $(DEPDIR)/libcurltool_la-tool_stderr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_stderr.c' object='libcurltool_la-tool_stderr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_stderr.lo `test -f 'tool_stderr.c' || echo '$(srcdir)/'`tool_stderr.c + +libcurltool_la-tool_strdup.lo: tool_strdup.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_strdup.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_strdup.Tpo -c -o libcurltool_la-tool_strdup.lo `test -f 'tool_strdup.c' || echo '$(srcdir)/'`tool_strdup.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_strdup.Tpo $(DEPDIR)/libcurltool_la-tool_strdup.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_strdup.c' object='libcurltool_la-tool_strdup.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_strdup.lo `test -f 'tool_strdup.c' || echo '$(srcdir)/'`tool_strdup.c + +libcurltool_la-tool_urlglob.lo: tool_urlglob.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_urlglob.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_urlglob.Tpo -c -o libcurltool_la-tool_urlglob.lo `test -f 'tool_urlglob.c' || echo '$(srcdir)/'`tool_urlglob.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_urlglob.Tpo $(DEPDIR)/libcurltool_la-tool_urlglob.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_urlglob.c' object='libcurltool_la-tool_urlglob.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_urlglob.lo `test -f 'tool_urlglob.c' || echo '$(srcdir)/'`tool_urlglob.c + +libcurltool_la-tool_util.lo: tool_util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_util.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_util.Tpo -c -o libcurltool_la-tool_util.lo `test -f 'tool_util.c' || echo '$(srcdir)/'`tool_util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_util.Tpo $(DEPDIR)/libcurltool_la-tool_util.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_util.c' object='libcurltool_la-tool_util.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_util.lo `test -f 'tool_util.c' || echo '$(srcdir)/'`tool_util.c + +libcurltool_la-tool_vms.lo: tool_vms.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_vms.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_vms.Tpo -c -o libcurltool_la-tool_vms.lo `test -f 'tool_vms.c' || echo '$(srcdir)/'`tool_vms.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_vms.Tpo $(DEPDIR)/libcurltool_la-tool_vms.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_vms.c' object='libcurltool_la-tool_vms.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_vms.lo `test -f 'tool_vms.c' || echo '$(srcdir)/'`tool_vms.c + +libcurltool_la-tool_writeout.lo: tool_writeout.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_writeout.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_writeout.Tpo -c -o libcurltool_la-tool_writeout.lo `test -f 'tool_writeout.c' || echo '$(srcdir)/'`tool_writeout.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_writeout.Tpo $(DEPDIR)/libcurltool_la-tool_writeout.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_writeout.c' object='libcurltool_la-tool_writeout.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_writeout.lo `test -f 'tool_writeout.c' || echo '$(srcdir)/'`tool_writeout.c + +libcurltool_la-tool_writeout_json.lo: tool_writeout_json.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_writeout_json.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_writeout_json.Tpo -c -o libcurltool_la-tool_writeout_json.lo `test -f 'tool_writeout_json.c' || echo '$(srcdir)/'`tool_writeout_json.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_writeout_json.Tpo $(DEPDIR)/libcurltool_la-tool_writeout_json.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_writeout_json.c' object='libcurltool_la-tool_writeout_json.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_writeout_json.lo `test -f 'tool_writeout_json.c' || echo '$(srcdir)/'`tool_writeout_json.c + +libcurltool_la-tool_xattr.lo: tool_xattr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-tool_xattr.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-tool_xattr.Tpo -c -o libcurltool_la-tool_xattr.lo `test -f 'tool_xattr.c' || echo '$(srcdir)/'`tool_xattr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-tool_xattr.Tpo $(DEPDIR)/libcurltool_la-tool_xattr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tool_xattr.c' object='libcurltool_la-tool_xattr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-tool_xattr.lo `test -f 'tool_xattr.c' || echo '$(srcdir)/'`tool_xattr.c + +libcurltool_la-var.lo: var.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT libcurltool_la-var.lo -MD -MP -MF $(DEPDIR)/libcurltool_la-var.Tpo -c -o libcurltool_la-var.lo `test -f 'var.c' || echo '$(srcdir)/'`var.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurltool_la-var.Tpo $(DEPDIR)/libcurltool_la-var.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='var.c' object='libcurltool_la-var.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o libcurltool_la-var.lo `test -f 'var.c' || echo '$(srcdir)/'`var.c + +../lib/libcurltool_la-base64.lo: ../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT ../lib/libcurltool_la-base64.lo -MD -MP -MF ../lib/$(DEPDIR)/libcurltool_la-base64.Tpo -c -o ../lib/libcurltool_la-base64.lo `test -f '../lib/base64.c' || echo '$(srcdir)/'`../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../lib/$(DEPDIR)/libcurltool_la-base64.Tpo ../lib/$(DEPDIR)/libcurltool_la-base64.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../lib/base64.c' object='../lib/libcurltool_la-base64.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o ../lib/libcurltool_la-base64.lo `test -f '../lib/base64.c' || echo '$(srcdir)/'`../lib/base64.c + +../lib/libcurltool_la-curl_multibyte.lo: ../lib/curl_multibyte.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT ../lib/libcurltool_la-curl_multibyte.lo -MD -MP -MF ../lib/$(DEPDIR)/libcurltool_la-curl_multibyte.Tpo -c -o ../lib/libcurltool_la-curl_multibyte.lo `test -f '../lib/curl_multibyte.c' || echo '$(srcdir)/'`../lib/curl_multibyte.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../lib/$(DEPDIR)/libcurltool_la-curl_multibyte.Tpo ../lib/$(DEPDIR)/libcurltool_la-curl_multibyte.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../lib/curl_multibyte.c' object='../lib/libcurltool_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o ../lib/libcurltool_la-curl_multibyte.lo `test -f '../lib/curl_multibyte.c' || echo '$(srcdir)/'`../lib/curl_multibyte.c + +../lib/libcurltool_la-dynbuf.lo: ../lib/dynbuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT ../lib/libcurltool_la-dynbuf.lo -MD -MP -MF ../lib/$(DEPDIR)/libcurltool_la-dynbuf.Tpo -c -o ../lib/libcurltool_la-dynbuf.lo `test -f '../lib/dynbuf.c' || echo '$(srcdir)/'`../lib/dynbuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../lib/$(DEPDIR)/libcurltool_la-dynbuf.Tpo ../lib/$(DEPDIR)/libcurltool_la-dynbuf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../lib/dynbuf.c' object='../lib/libcurltool_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o ../lib/libcurltool_la-dynbuf.lo `test -f '../lib/dynbuf.c' || echo '$(srcdir)/'`../lib/dynbuf.c + +../lib/libcurltool_la-nonblock.lo: ../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT ../lib/libcurltool_la-nonblock.lo -MD -MP -MF ../lib/$(DEPDIR)/libcurltool_la-nonblock.Tpo -c -o ../lib/libcurltool_la-nonblock.lo `test -f '../lib/nonblock.c' || echo '$(srcdir)/'`../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../lib/$(DEPDIR)/libcurltool_la-nonblock.Tpo ../lib/$(DEPDIR)/libcurltool_la-nonblock.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../lib/nonblock.c' object='../lib/libcurltool_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o ../lib/libcurltool_la-nonblock.lo `test -f '../lib/nonblock.c' || echo '$(srcdir)/'`../lib/nonblock.c + +../lib/libcurltool_la-strtoofft.lo: ../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT ../lib/libcurltool_la-strtoofft.lo -MD -MP -MF ../lib/$(DEPDIR)/libcurltool_la-strtoofft.Tpo -c -o ../lib/libcurltool_la-strtoofft.lo `test -f '../lib/strtoofft.c' || echo '$(srcdir)/'`../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../lib/$(DEPDIR)/libcurltool_la-strtoofft.Tpo ../lib/$(DEPDIR)/libcurltool_la-strtoofft.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../lib/strtoofft.c' object='../lib/libcurltool_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o ../lib/libcurltool_la-strtoofft.lo `test -f '../lib/strtoofft.c' || echo '$(srcdir)/'`../lib/strtoofft.c + +../lib/libcurltool_la-timediff.lo: ../lib/timediff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT ../lib/libcurltool_la-timediff.lo -MD -MP -MF ../lib/$(DEPDIR)/libcurltool_la-timediff.Tpo -c -o ../lib/libcurltool_la-timediff.lo `test -f '../lib/timediff.c' || echo '$(srcdir)/'`../lib/timediff.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../lib/$(DEPDIR)/libcurltool_la-timediff.Tpo ../lib/$(DEPDIR)/libcurltool_la-timediff.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../lib/timediff.c' object='../lib/libcurltool_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o ../lib/libcurltool_la-timediff.lo `test -f '../lib/timediff.c' || echo '$(srcdir)/'`../lib/timediff.c + +../lib/libcurltool_la-version_win32.lo: ../lib/version_win32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT ../lib/libcurltool_la-version_win32.lo -MD -MP -MF ../lib/$(DEPDIR)/libcurltool_la-version_win32.Tpo -c -o ../lib/libcurltool_la-version_win32.lo `test -f '../lib/version_win32.c' || echo '$(srcdir)/'`../lib/version_win32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../lib/$(DEPDIR)/libcurltool_la-version_win32.Tpo ../lib/$(DEPDIR)/libcurltool_la-version_win32.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../lib/version_win32.c' object='../lib/libcurltool_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o ../lib/libcurltool_la-version_win32.lo `test -f '../lib/version_win32.c' || echo '$(srcdir)/'`../lib/version_win32.c + +../lib/libcurltool_la-warnless.lo: ../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -MT ../lib/libcurltool_la-warnless.lo -MD -MP -MF ../lib/$(DEPDIR)/libcurltool_la-warnless.Tpo -c -o ../lib/libcurltool_la-warnless.lo `test -f '../lib/warnless.c' || echo '$(srcdir)/'`../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../lib/$(DEPDIR)/libcurltool_la-warnless.Tpo ../lib/$(DEPDIR)/libcurltool_la-warnless.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../lib/warnless.c' object='../lib/libcurltool_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurltool_la_CPPFLAGS) $(CPPFLAGS) $(libcurltool_la_CFLAGS) $(CFLAGS) -c -o ../lib/libcurltool_la-warnless.lo `test -f '../lib/warnless.c' || echo '$(srcdir)/'`../lib/warnless.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf ../lib/.libs ../lib/_libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +@DEBUGBUILD_FALSE@all-local: +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) all-local +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f ../lib/$(DEPDIR)/$(am__dirstamp) + -rm -f ../lib/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ../lib/$(DEPDIR)/base64.Po + -rm -f ../lib/$(DEPDIR)/curl_multibyte.Po + -rm -f ../lib/$(DEPDIR)/dynbuf.Po + -rm -f ../lib/$(DEPDIR)/libcurltool_la-base64.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-curl_multibyte.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-dynbuf.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-nonblock.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-strtoofft.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-timediff.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-version_win32.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-warnless.Plo + -rm -f ../lib/$(DEPDIR)/nonblock.Po + -rm -f ../lib/$(DEPDIR)/strtoofft.Po + -rm -f ../lib/$(DEPDIR)/timediff.Po + -rm -f ../lib/$(DEPDIR)/version_win32.Po + -rm -f ../lib/$(DEPDIR)/warnless.Po + -rm -f ./$(DEPDIR)/libcurltool_la-slist_wc.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-terminal.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_binmode.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_bname.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_dbg.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_hdr.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_prg.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_rea.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_see.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_soc.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_wrt.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cfgable.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_dirhie.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_doswin.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_easysrc.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_filetime.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_findfile.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_formparse.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_getparam.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_getpass.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_help.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_helpers.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_hugehelp.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_ipfs.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_libinfo.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_listhelp.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_main.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_msgs.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_operate.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_operhlp.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_paramhlp.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_parsecfg.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_progress.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_setopt.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_sleep.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_stderr.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_strdup.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_urlglob.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_util.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_vms.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_writeout.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_writeout_json.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_xattr.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-var.Plo + -rm -f ./$(DEPDIR)/slist_wc.Po + -rm -f ./$(DEPDIR)/terminal.Po + -rm -f ./$(DEPDIR)/tool_binmode.Po + -rm -f ./$(DEPDIR)/tool_bname.Po + -rm -f ./$(DEPDIR)/tool_cb_dbg.Po + -rm -f ./$(DEPDIR)/tool_cb_hdr.Po + -rm -f ./$(DEPDIR)/tool_cb_prg.Po + -rm -f ./$(DEPDIR)/tool_cb_rea.Po + -rm -f ./$(DEPDIR)/tool_cb_see.Po + -rm -f ./$(DEPDIR)/tool_cb_soc.Po + -rm -f ./$(DEPDIR)/tool_cb_wrt.Po + -rm -f ./$(DEPDIR)/tool_cfgable.Po + -rm -f ./$(DEPDIR)/tool_dirhie.Po + -rm -f ./$(DEPDIR)/tool_doswin.Po + -rm -f ./$(DEPDIR)/tool_easysrc.Po + -rm -f ./$(DEPDIR)/tool_filetime.Po + -rm -f ./$(DEPDIR)/tool_findfile.Po + -rm -f ./$(DEPDIR)/tool_formparse.Po + -rm -f ./$(DEPDIR)/tool_getparam.Po + -rm -f ./$(DEPDIR)/tool_getpass.Po + -rm -f ./$(DEPDIR)/tool_help.Po + -rm -f ./$(DEPDIR)/tool_helpers.Po + -rm -f ./$(DEPDIR)/tool_hugehelp.Po + -rm -f ./$(DEPDIR)/tool_ipfs.Po + -rm -f ./$(DEPDIR)/tool_libinfo.Po + -rm -f ./$(DEPDIR)/tool_listhelp.Po + -rm -f ./$(DEPDIR)/tool_main.Po + -rm -f ./$(DEPDIR)/tool_msgs.Po + -rm -f ./$(DEPDIR)/tool_operate.Po + -rm -f ./$(DEPDIR)/tool_operhlp.Po + -rm -f ./$(DEPDIR)/tool_paramhlp.Po + -rm -f ./$(DEPDIR)/tool_parsecfg.Po + -rm -f ./$(DEPDIR)/tool_progress.Po + -rm -f ./$(DEPDIR)/tool_setopt.Po + -rm -f ./$(DEPDIR)/tool_sleep.Po + -rm -f ./$(DEPDIR)/tool_stderr.Po + -rm -f ./$(DEPDIR)/tool_strdup.Po + -rm -f ./$(DEPDIR)/tool_urlglob.Po + -rm -f ./$(DEPDIR)/tool_util.Po + -rm -f ./$(DEPDIR)/tool_vms.Po + -rm -f ./$(DEPDIR)/tool_writeout.Po + -rm -f ./$(DEPDIR)/tool_writeout_json.Po + -rm -f ./$(DEPDIR)/tool_xattr.Po + -rm -f ./$(DEPDIR)/var.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ../lib/$(DEPDIR)/base64.Po + -rm -f ../lib/$(DEPDIR)/curl_multibyte.Po + -rm -f ../lib/$(DEPDIR)/dynbuf.Po + -rm -f ../lib/$(DEPDIR)/libcurltool_la-base64.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-curl_multibyte.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-dynbuf.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-nonblock.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-strtoofft.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-timediff.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-version_win32.Plo + -rm -f ../lib/$(DEPDIR)/libcurltool_la-warnless.Plo + -rm -f ../lib/$(DEPDIR)/nonblock.Po + -rm -f ../lib/$(DEPDIR)/strtoofft.Po + -rm -f ../lib/$(DEPDIR)/timediff.Po + -rm -f ../lib/$(DEPDIR)/version_win32.Po + -rm -f ../lib/$(DEPDIR)/warnless.Po + -rm -f ./$(DEPDIR)/libcurltool_la-slist_wc.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-terminal.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_binmode.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_bname.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_dbg.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_hdr.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_prg.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_rea.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_see.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_soc.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cb_wrt.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_cfgable.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_dirhie.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_doswin.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_easysrc.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_filetime.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_findfile.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_formparse.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_getparam.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_getpass.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_help.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_helpers.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_hugehelp.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_ipfs.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_libinfo.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_listhelp.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_main.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_msgs.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_operate.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_operhlp.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_paramhlp.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_parsecfg.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_progress.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_setopt.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_sleep.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_stderr.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_strdup.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_urlglob.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_util.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_vms.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_writeout.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_writeout_json.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-tool_xattr.Plo + -rm -f ./$(DEPDIR)/libcurltool_la-var.Plo + -rm -f ./$(DEPDIR)/slist_wc.Po + -rm -f ./$(DEPDIR)/terminal.Po + -rm -f ./$(DEPDIR)/tool_binmode.Po + -rm -f ./$(DEPDIR)/tool_bname.Po + -rm -f ./$(DEPDIR)/tool_cb_dbg.Po + -rm -f ./$(DEPDIR)/tool_cb_hdr.Po + -rm -f ./$(DEPDIR)/tool_cb_prg.Po + -rm -f ./$(DEPDIR)/tool_cb_rea.Po + -rm -f ./$(DEPDIR)/tool_cb_see.Po + -rm -f ./$(DEPDIR)/tool_cb_soc.Po + -rm -f ./$(DEPDIR)/tool_cb_wrt.Po + -rm -f ./$(DEPDIR)/tool_cfgable.Po + -rm -f ./$(DEPDIR)/tool_dirhie.Po + -rm -f ./$(DEPDIR)/tool_doswin.Po + -rm -f ./$(DEPDIR)/tool_easysrc.Po + -rm -f ./$(DEPDIR)/tool_filetime.Po + -rm -f ./$(DEPDIR)/tool_findfile.Po + -rm -f ./$(DEPDIR)/tool_formparse.Po + -rm -f ./$(DEPDIR)/tool_getparam.Po + -rm -f ./$(DEPDIR)/tool_getpass.Po + -rm -f ./$(DEPDIR)/tool_help.Po + -rm -f ./$(DEPDIR)/tool_helpers.Po + -rm -f ./$(DEPDIR)/tool_hugehelp.Po + -rm -f ./$(DEPDIR)/tool_ipfs.Po + -rm -f ./$(DEPDIR)/tool_libinfo.Po + -rm -f ./$(DEPDIR)/tool_listhelp.Po + -rm -f ./$(DEPDIR)/tool_main.Po + -rm -f ./$(DEPDIR)/tool_msgs.Po + -rm -f ./$(DEPDIR)/tool_operate.Po + -rm -f ./$(DEPDIR)/tool_operhlp.Po + -rm -f ./$(DEPDIR)/tool_paramhlp.Po + -rm -f ./$(DEPDIR)/tool_parsecfg.Po + -rm -f ./$(DEPDIR)/tool_progress.Po + -rm -f ./$(DEPDIR)/tool_setopt.Po + -rm -f ./$(DEPDIR)/tool_sleep.Po + -rm -f ./$(DEPDIR)/tool_stderr.Po + -rm -f ./$(DEPDIR)/tool_strdup.Po + -rm -f ./$(DEPDIR)/tool_urlglob.Po + -rm -f ./$(DEPDIR)/tool_util.Po + -rm -f ./$(DEPDIR)/tool_vms.Po + -rm -f ./$(DEPDIR)/tool_writeout.Po + -rm -f ./$(DEPDIR)/tool_writeout_json.Po + -rm -f ./$(DEPDIR)/tool_xattr.Po + -rm -f ./$(DEPDIR)/var.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \ + check-am clean clean-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS + +.PRECIOUS: Makefile + + +# remove targets if the command fails +.DELETE_ON_ERROR: +@HAVE_WINDRES_TRUE@$(CURL_RCFILES): tool_version.h + +# Here are the stuff to create a built-in manual + +@USE_MANUAL_TRUE@$(ASCIIPAGE): +@USE_MANUAL_TRUE@ cd $(top_builddir)/docs && $(MAKE) + +# This generates the tool_hugehelp.c file in both uncompressed and +# compressed formats. +@HAVE_LIBZ_TRUE@@USE_MANUAL_TRUE@$(HUGE): $(ASCIIPAGE) $(MKHELP) +@HAVE_LIBZ_TRUE@@USE_MANUAL_TRUE@ $(HUGECMD) (echo '#include "tool_setup.h"' > $(HUGE); \ +@HAVE_LIBZ_TRUE@@USE_MANUAL_TRUE@ echo '#ifndef HAVE_LIBZ' >> $(HUGE); \ +@HAVE_LIBZ_TRUE@@USE_MANUAL_TRUE@ $(PERL) $(MKHELP) < $(ASCIIPAGE) >> $(HUGE); \ +@HAVE_LIBZ_TRUE@@USE_MANUAL_TRUE@ echo '#else' >> $(HUGE); \ +@HAVE_LIBZ_TRUE@@USE_MANUAL_TRUE@ $(PERL) $(MKHELP) -c < $(ASCIIPAGE) >> $(HUGE); \ +@HAVE_LIBZ_TRUE@@USE_MANUAL_TRUE@ echo '#endif /* HAVE_LIBZ */' >> $(HUGE) ) +# This generates the tool_hugehelp.c file uncompressed only +@HAVE_LIBZ_FALSE@@USE_MANUAL_TRUE@$(HUGE): $(ASCIIPAGE) $(MKHELP) +@HAVE_LIBZ_FALSE@@USE_MANUAL_TRUE@ $(HUGECMD)(echo '#include "tool_setup.h"' > $(HUGE); \ +@HAVE_LIBZ_FALSE@@USE_MANUAL_TRUE@ $(PERL) $(MKHELP) < $(ASCIIPAGE) >> $(HUGE) ) + +# built-in manual has been disabled, make a blank file +@USE_MANUAL_FALSE@$(HUGE): +@USE_MANUAL_FALSE@ echo '#include "tool_hugehelp.h"' >> $(HUGE) + +# ignore tool_hugehelp.c since it is generated source code and it plays +# by slightly different rules! +checksrc: + $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \ + -W$(srcdir)/tool_hugehelp.c $(srcdir)/*.[ch]) + +# for debug builds, we scan the sources on all regular make invokes +@DEBUGBUILD_TRUE@all-local: checksrc + +tidy: + $(TIDY) $(CURL_CFILES) $(TIDYFLAGS) -- $(curl_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H + +listhelp: + (cd $(top_srcdir)/docs/cmdline-opts && make listhelp) + +@HAVE_WINDRES_TRUE@.rc.o: +@HAVE_WINDRES_TRUE@ $(RC) -I$(top_srcdir)/include -DCURL_EMBED_MANIFEST $(RCFLAGS) -i $< -o $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/third_party/curl/src/Makefile.inc b/third_party/curl/src/Makefile.inc new file mode 100644 index 0000000000..93bef122c9 --- /dev/null +++ b/third_party/curl/src/Makefile.inc @@ -0,0 +1,157 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# ./src/Makefile.inc +# Using the backslash as line continuation character might be problematic with +# some make flavours. If we ever want to change this in a portable manner then +# we should consider this idea : +# CSRC1 = file1.c file2.c file3.c +# CSRC2 = file4.c file5.c file6.c +# CSOURCES = $(CSRC1) $(CSRC2) + +# libcurl sources to include in curltool lib we use for test binaries +CURLTOOL_LIBCURL_CFILES = \ + ../lib/base64.c \ + ../lib/dynbuf.c + +# libcurl has sources that provide functions named curlx_* that are not part of +# the official API, but we reuse the code here to avoid duplication. +CURLX_CFILES = \ + ../lib/base64.c \ + ../lib/curl_multibyte.c \ + ../lib/dynbuf.c \ + ../lib/nonblock.c \ + ../lib/strtoofft.c \ + ../lib/timediff.c \ + ../lib/version_win32.c \ + ../lib/warnless.c + +CURLX_HFILES = \ + ../lib/curl_ctype.h \ + ../lib/curl_multibyte.h \ + ../lib/curl_setup.h \ + ../lib/dynbuf.h \ + ../lib/nonblock.h \ + ../lib/strtoofft.h \ + ../lib/timediff.h \ + ../lib/version_win32.h \ + ../lib/warnless.h + +CURL_CFILES = \ + slist_wc.c \ + terminal.c \ + tool_binmode.c \ + tool_bname.c \ + tool_cb_dbg.c \ + tool_cb_hdr.c \ + tool_cb_prg.c \ + tool_cb_rea.c \ + tool_cb_see.c \ + tool_cb_soc.c \ + tool_cb_wrt.c \ + tool_cfgable.c \ + tool_dirhie.c \ + tool_doswin.c \ + tool_easysrc.c \ + tool_filetime.c \ + tool_findfile.c \ + tool_formparse.c \ + tool_getparam.c \ + tool_getpass.c \ + tool_help.c \ + tool_helpers.c \ + tool_hugehelp.c \ + tool_ipfs.c \ + tool_libinfo.c \ + tool_listhelp.c \ + tool_main.c \ + tool_msgs.c \ + tool_operate.c \ + tool_operhlp.c \ + tool_paramhlp.c \ + tool_parsecfg.c \ + tool_progress.c \ + tool_setopt.c \ + tool_sleep.c \ + tool_stderr.c \ + tool_strdup.c \ + tool_urlglob.c \ + tool_util.c \ + tool_vms.c \ + tool_writeout.c \ + tool_writeout_json.c \ + tool_xattr.c \ + var.c + +CURL_HFILES = \ + slist_wc.h \ + terminal.h \ + tool_binmode.h \ + tool_bname.h \ + tool_cb_dbg.h \ + tool_cb_hdr.h \ + tool_cb_prg.h \ + tool_cb_rea.h \ + tool_cb_see.h \ + tool_cb_soc.h \ + tool_cb_wrt.h \ + tool_cfgable.h \ + tool_dirhie.h \ + tool_doswin.h \ + tool_easysrc.h \ + tool_filetime.h \ + tool_findfile.h \ + tool_formparse.h \ + tool_getparam.h \ + tool_getpass.h \ + tool_help.h \ + tool_helpers.h \ + tool_hugehelp.h \ + tool_ipfs.h \ + tool_libinfo.h \ + tool_main.h \ + tool_msgs.h \ + tool_operate.h \ + tool_operhlp.h \ + tool_paramhlp.h \ + tool_parsecfg.h \ + tool_progress.h \ + tool_sdecls.h \ + tool_setopt.h \ + tool_setup.h \ + tool_sleep.h \ + tool_stderr.h \ + tool_strdup.h \ + tool_urlglob.h \ + tool_util.h \ + tool_version.h \ + tool_vms.h \ + tool_writeout.h \ + tool_writeout_json.h \ + tool_xattr.h \ + var.h + +CURL_RCFILES = curl.rc + +# curl_SOURCES is special and gets assigned in src/Makefile.am +CURL_FILES = $(CURL_CFILES) $(CURLX_CFILES) $(CURL_HFILES) diff --git a/third_party/curl/src/Makefile.mk b/third_party/curl/src/Makefile.mk new file mode 100644 index 0000000000..83dd65d16f --- /dev/null +++ b/third_party/curl/src/Makefile.mk @@ -0,0 +1,90 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +#*************************************************************************** + +# See usage in lib/Makefile.mk + +PROOT := .. + +### Common + +include $(PROOT)/lib/Makefile.mk + +### Local + +CPPFLAGS += -I$(PROOT)/lib +LDFLAGS += -L$(PROOT)/lib +LIBS := -lcurl $(LIBS) + +### Sources and targets + +# Provides CURL_CFILES, CURLX_CFILES +include Makefile.inc + +TARGETS := curl$(BIN_EXT) + +CURL_CFILES += $(notdir $(CURLX_CFILES)) + +curl_OBJECTS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(strip $(CURL_CFILES))) +ifdef MAP +CURL_MAP := curl.map +LDFLAGS += -Wl,-Map,$(CURL_MAP) +TOVCLEAN := $(CURL_MAP) +endif +vpath %.c $(PROOT)/lib + +TOCLEAN := $(curl_OBJECTS) + +### Rules + +ifneq ($(wildcard tool_hugehelp.c.cvs),) +PERL ?= perl +NROFF ?= groff + +TOCLEAN += tool_hugehelp.c + +ifneq ($(shell $(call WHICH, $(NROFF))),) +$(PROOT)/docs/curl.1: $(wildcard $(PROOT)/docs/cmdline-opts/*.d) + cd $(PROOT)/docs/cmdline-opts && \ + $(PERL) gen.pl mainpage $(notdir $^) > ../curl.1 + +# Necessary for the generated tools_hugehelp.c +CPPFLAGS += -DUSE_MANUAL + +ifdef ZLIB +_MKHELPOPT += -c +endif +tool_hugehelp.c: $(PROOT)/docs/curl.1 mkhelp.pl + $(NROFF) -man -Tascii $(MANOPT) $< | \ + $(PERL) mkhelp.pl $(_MKHELPOPT) $< > $@ +else +tool_hugehelp.c: + @echo Creating $@ + @$(call COPY, $@.cvs, $@) +endif +endif + +$(TARGETS): $(curl_OBJECTS) $(PROOT)/lib/libcurl.a + $(CC) $(LDFLAGS) -o $@ $(curl_OBJECTS) $(LIBS) + +all: $(OBJ_DIR) $(TARGETS) diff --git a/third_party/curl/src/curl.rc b/third_party/curl/src/curl.rc new file mode 100644 index 0000000000..475dfbe896 --- /dev/null +++ b/third_party/curl/src/curl.rc @@ -0,0 +1,113 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include +#include "tool_version.h" + +LANGUAGE 0, 0 + +#define RC_VERSION CURL_VERSION_MAJOR, CURL_VERSION_MINOR, CURL_VERSION_PATCH, 0 + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RC_VERSION + PRODUCTVERSION RC_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#if defined(DEBUGBUILD) || defined(UNITTESTS) || defined(CURLDEBUG) || defined(_DEBUG) + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0L + +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "curl, https://curl.se/\0" + VALUE "FileDescription", "The curl executable\0" + VALUE "FileVersion", CURL_VERSION "\0" + VALUE "InternalName", "curl\0" + VALUE "OriginalFilename", "curl.exe\0" + VALUE "ProductName", "The curl executable\0" + VALUE "ProductVersion", CURL_VERSION "\0" + VALUE "LegalCopyright", "Copyright (C) " CURL_COPYRIGHT "\0" + VALUE "License", "https://curl.se/docs/copyright.html\0" + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +/* Manifest */ + +#if defined(CURL_EMBED_MANIFEST) + +/* String escaping rules: + https://msdn.microsoft.com/library/aa381050 + Application Manifest doc, including the list of 'supportedOS Id's: + https://msdn.microsoft.com/library/aa374191 */ + +#ifndef CREATEPROCESS_MANIFEST_RESOURCE_ID +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +#endif +#ifndef RT_MANIFEST +#define RT_MANIFEST 24 +#endif + +#define _STR(macro) _STR_(macro) +#define _STR_(macro) #macro + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST +BEGIN + "" + "" + "" + "" + "" + "" /* Vista / Server 2008 */ + "" /* 7 / Server 2008 R2 */ + "" /* 8 / Server 2012 */ + "" /* 8.1 / Server 2012 R2 */ + "" /* 10 / Server 2016 */ + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" +END + +#endif diff --git a/third_party/curl/src/mkhelp.pl b/third_party/curl/src/mkhelp.pl new file mode 100755 index 0000000000..96c2b4192b --- /dev/null +++ b/third_party/curl/src/mkhelp.pl @@ -0,0 +1,201 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +# Yeah, I know, probably 1000 other persons already wrote a script like +# this, but I'll tell ya: + +# THEY DON'T FIT ME :-) + +# Get readme file as parameter: + +if($ARGV[0] eq "-c") { + $c=1; + shift @ARGV; +} + +push @out, " _ _ ____ _\n"; +push @out, " ___| | | | _ \\| |\n"; +push @out, " / __| | | | |_) | |\n"; +push @out, " | (__| |_| | _ <| |___\n"; +push @out, " \\___|\\___/|_| \\_\\_____|\n"; + +my $olen=0; +while () { + my $line = $_; + push @out, $line; +} + +print <import(); + 1; + }; + print STDERR "Warning: compression requested but Gzip is not available\n" if (!$c) +} + +if($c) +{ + my $content = join("", @out); + my $gzippedContent; + IO::Compress::Gzip::gzip( + \$content, \$gzippedContent, Level => 9, TextFlag => 1, Time=>0) or die "gzip failed:"; + $gzip = length($content); + $gzipped = length($gzippedContent); + + print < +#include "memdebug.h" /* keep this as LAST include */ +static const unsigned char hugehelpgz[] = { + /* This mumbo-jumbo is the huge help text compressed with gzip. + Thanks to this operation, the size of this data shrank from $gzip + to $gzipped bytes. You can disable the use of compressed help + texts by NOT passing -c to the mkhelp.pl tool. */ +HEAD +; + + my $c=0; + print " "; + for(split(//, $gzippedContent)) { + my $num=ord($_); + printf(" 0x%02x,", 0+$num); + if(!(++$c % 12)) { + print "\n "; + } + } + print "\n};\n"; + + print <, 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 "tool_setup.h" + +#ifndef CURL_DISABLE_LIBCURL_OPTION + +#include "slist_wc.h" + +/* The last #include files should be: */ +#include "memdebug.h" + +/* + * slist_wc_append() appends a string to the linked list. This function can be + * used as an initialization function as well as an append function. + */ +struct slist_wc *slist_wc_append(struct slist_wc *list, + const char *data) +{ + struct curl_slist *new_item = curl_slist_append(NULL, data); + + if(!new_item) + return NULL; + + if(!list) { + list = malloc(sizeof(struct slist_wc)); + + if(!list) { + curl_slist_free_all(new_item); + return NULL; + } + + list->first = new_item; + list->last = new_item; + return list; + } + + list->last->next = new_item; + list->last = list->last->next; + return list; +} + +/* be nice and clean up resources */ +void slist_wc_free_all(struct slist_wc *list) +{ + if(!list) + return; + + curl_slist_free_all(list->first); + free(list); +} + +#endif /* CURL_DISABLE_LIBCURL_OPTION */ diff --git a/third_party/curl/src/slist_wc.h b/third_party/curl/src/slist_wc.h new file mode 100644 index 0000000000..dd7b8c10a7 --- /dev/null +++ b/third_party/curl/src/slist_wc.h @@ -0,0 +1,57 @@ +#ifndef HEADER_CURL_SLIST_WC_H +#define HEADER_CURL_SLIST_WC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "tool_setup.h" +#ifndef CURL_DISABLE_LIBCURL_OPTION + +/* linked-list structure with last node cache for easysrc */ +struct slist_wc { + struct curl_slist *first; + struct curl_slist *last; +}; + +/* + * NAME curl_slist_wc_append() + * + * DESCRIPTION + * + * Appends a string to a linked list. If no list exists, it will be created + * first. Returns the new list, after appending. + */ +struct slist_wc *slist_wc_append(struct slist_wc *, const char *); + +/* + * NAME curl_slist_free_all() + * + * DESCRIPTION + * + * free a previously built curl_slist_wc. + */ +void slist_wc_free_all(struct slist_wc *); + +#endif /* CURL_DISABLE_LIBCURL_OPTION */ + +#endif /* HEADER_CURL_SLIST_WC_H */ diff --git a/third_party/curl/src/terminal.c b/third_party/curl/src/terminal.c new file mode 100644 index 0000000000..7ba991d3e5 --- /dev/null +++ b/third_party/curl/src/terminal.c @@ -0,0 +1,91 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#include "terminal.h" + +#include "memdebug.h" /* keep this as LAST include */ + +#ifdef HAVE_TERMIOS_H +# include +#elif defined(HAVE_TERMIO_H) +# include +#endif + +/* + * get_terminal_columns() returns the number of columns in the current + * terminal. It will return 79 on failure. Also, the number can be very big. + */ + +unsigned int get_terminal_columns(void) +{ + unsigned int width = 0; + char *colp = curl_getenv("COLUMNS"); + if(colp) { + char *endptr; + long num = strtol(colp, &endptr, 10); + if((endptr != colp) && (endptr == colp + strlen(colp)) && (num > 20) && + (num < 10000)) + width = (unsigned int)num; + curl_free(colp); + } + + if(!width) { + int cols = 0; + +#ifdef TIOCGSIZE + struct ttysize ts; + if(!ioctl(STDIN_FILENO, TIOCGSIZE, &ts)) + cols = ts.ts_cols; +#elif defined(TIOCGWINSZ) + struct winsize ts; + if(!ioctl(STDIN_FILENO, TIOCGWINSZ, &ts)) + cols = (int)ts.ws_col; +#elif defined(_WIN32) && !defined(CURL_WINDOWS_APP) + { + HANDLE stderr_hnd = GetStdHandle(STD_ERROR_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO console_info; + + if((stderr_hnd != INVALID_HANDLE_VALUE) && + GetConsoleScreenBufferInfo(stderr_hnd, &console_info)) { + /* + * Do not use +1 to get the true screen-width since writing a + * character at the right edge will cause a line wrap. + */ + cols = (int) + (console_info.srWindow.Right - console_info.srWindow.Left); + } + } +#endif /* TIOCGSIZE */ + if(cols >= 0 && cols < 10000) + width = (unsigned int)cols; + } + if(!width) + width = 79; + return width; /* 79 for unknown, might also be very small or very big */ +} diff --git a/third_party/curl/src/terminal.h b/third_party/curl/src/terminal.h new file mode 100644 index 0000000000..dbd4abe281 --- /dev/null +++ b/third_party/curl/src/terminal.h @@ -0,0 +1,30 @@ +#ifndef HEADER_CURL_TERMINAL_H +#define HEADER_CURL_TERMINAL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +unsigned int get_terminal_columns(void); + +#endif /* HEADER_CURL_TERMINAL_H */ diff --git a/third_party/curl/src/tool_binmode.c b/third_party/curl/src/tool_binmode.c new file mode 100644 index 0000000000..e27ce9663c --- /dev/null +++ b/third_party/curl/src/tool_binmode.c @@ -0,0 +1,53 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifdef HAVE_SETMODE + +#ifdef HAVE_IO_H +# include +#endif + +#ifdef HAVE_FCNTL_H +# include +#endif + +#include "tool_binmode.h" + +#include "memdebug.h" /* keep this as LAST include */ + +void set_binmode(FILE *stream) +{ +#ifdef O_BINARY +# ifdef __HIGHC__ + _setmode(stream, O_BINARY); +# else + (void)setmode(fileno(stream), O_BINARY); +# endif +#else + (void)stream; +#endif +} + +#endif /* HAVE_SETMODE */ diff --git a/third_party/curl/src/tool_binmode.h b/third_party/curl/src/tool_binmode.h new file mode 100644 index 0000000000..bee837b000 --- /dev/null +++ b/third_party/curl/src/tool_binmode.h @@ -0,0 +1,38 @@ +#ifndef HEADER_CURL_TOOL_BINMODE_H +#define HEADER_CURL_TOOL_BINMODE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifdef HAVE_SETMODE + +void set_binmode(FILE *stream); + +#else + +#define set_binmode(x) Curl_nop_stmt + +#endif /* HAVE_SETMODE */ + +#endif /* HEADER_CURL_TOOL_BINMODE_H */ diff --git a/third_party/curl/src/tool_bname.c b/third_party/curl/src/tool_bname.c new file mode 100644 index 0000000000..4ba1a3b8ee --- /dev/null +++ b/third_party/curl/src/tool_bname.c @@ -0,0 +1,51 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#include "tool_bname.h" + +#include "memdebug.h" /* keep this as LAST include */ + +#ifndef HAVE_BASENAME + +char *tool_basename(char *path) +{ + char *s1; + char *s2; + + s1 = strrchr(path, '/'); + s2 = strrchr(path, '\\'); + + if(s1 && s2) { + path = (s1 > s2) ? s1 + 1 : s2 + 1; + } + else if(s1) + path = s1 + 1; + else if(s2) + path = s2 + 1; + + return path; +} + +#endif /* HAVE_BASENAME */ diff --git a/third_party/curl/src/tool_bname.h b/third_party/curl/src/tool_bname.h new file mode 100644 index 0000000000..d091c2231a --- /dev/null +++ b/third_party/curl/src/tool_bname.h @@ -0,0 +1,36 @@ +#ifndef HEADER_CURL_TOOL_BNAME_H +#define HEADER_CURL_TOOL_BNAME_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifndef HAVE_BASENAME + +char *tool_basename(char *path); + +#define basename(x) tool_basename((x)) + +#endif /* HAVE_BASENAME */ + +#endif /* HEADER_CURL_TOOL_BNAME_H */ diff --git a/third_party/curl/src/tool_cb_dbg.c b/third_party/curl/src/tool_cb_dbg.c new file mode 100644 index 0000000000..dd2a124ad7 --- /dev/null +++ b/third_party/curl/src/tool_cb_dbg.c @@ -0,0 +1,300 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_cfgable.h" +#include "tool_msgs.h" +#include "tool_cb_dbg.h" +#include "tool_util.h" + +#include "memdebug.h" /* keep this as LAST include */ + +static void dump(const char *timebuf, const char *idsbuf, const char *text, + FILE *stream, const unsigned char *ptr, size_t size, + trace tracetype, curl_infotype infotype); + +/* + * Return the formatted HH:MM:SS for the tv_sec given. + * NOT thread safe. + */ +static const char *hms_for_sec(time_t tv_sec) +{ + static time_t cached_tv_sec; + static char hms_buf[12]; + static time_t epoch_offset; + static int known_epoch; + + if(tv_sec != cached_tv_sec) { + struct tm *now; + time_t secs; + /* recalculate */ + if(!known_epoch) { + epoch_offset = time(NULL) - tv_sec; + known_epoch = 1; + } + secs = epoch_offset + tv_sec; + /* !checksrc! disable BANNEDFUNC 1 */ + now = localtime(&secs); /* not thread safe but we do not care */ + msnprintf(hms_buf, sizeof(hms_buf), "%02d:%02d:%02d", + now->tm_hour, now->tm_min, now->tm_sec); + cached_tv_sec = tv_sec; + } + return hms_buf; +} + +static void log_line_start(FILE *log, const char *timebuf, + const char *idsbuf, curl_infotype type) +{ + /* + * This is the trace look that is similar to what libcurl makes on its + * own. + */ + static const char * const s_infotype[] = { + "* ", "< ", "> ", "{ ", "} ", "{ ", "} " + }; + if((timebuf && *timebuf) || (idsbuf && *idsbuf)) + fprintf(log, "%s%s%s", timebuf, idsbuf, s_infotype[type]); + else + fputs(s_infotype[type], log); +} + +#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] " +#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \ + CURL_FORMAT_CURL_OFF_T "] " +/* +** callback for CURLOPT_DEBUGFUNCTION +*/ +int tool_debug_cb(CURL *handle, curl_infotype type, + char *data, size_t size, + void *userdata) +{ + struct OperationConfig *operation = userdata; + struct GlobalConfig *config = operation->global; + FILE *output = tool_stderr; + const char *text; + struct timeval tv; + char timebuf[20]; + /* largest signed 64-bit is: 9,223,372,036,854,775,807 + * max length in decimal: 1 + (6*3) = 19 + * formatted via TRC_IDS_FORMAT_IDS_2 this becomes 2 + 19 + 1 + 19 + 2 = 43 + * negative xfer-id are not printed, negative conn-ids use TRC_IDS_FORMAT_1 + */ + char idsbuf[60]; + curl_off_t xfer_id, conn_id; + + (void)handle; /* not used */ + + if(config->tracetime) { + tv = tvnow(); + msnprintf(timebuf, sizeof(timebuf), "%s.%06ld ", + hms_for_sec(tv.tv_sec), (long)tv.tv_usec); + } + else + timebuf[0] = 0; + + if(handle && config->traceids && + !curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) { + if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) && + conn_id >= 0) { + msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, + xfer_id, conn_id); + } + else { + msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id); + } + } + else + idsbuf[0] = 0; + + if(!config->trace_stream) { + /* open for append */ + if(!strcmp("-", config->trace_dump)) + config->trace_stream = stdout; + else if(!strcmp("%", config->trace_dump)) + /* Ok, this is somewhat hackish but we do it undocumented for now */ + config->trace_stream = tool_stderr; + else { + config->trace_stream = fopen(config->trace_dump, FOPEN_WRITETEXT); + config->trace_fopened = TRUE; + } + } + + if(config->trace_stream) + output = config->trace_stream; + + if(!output) { + warnf(config, "Failed to create/open output"); + return 0; + } + + if(config->tracetype == TRACE_PLAIN) { + static bool newl = FALSE; + static bool traced_data = FALSE; + + switch(type) { + case CURLINFO_HEADER_OUT: + if(size > 0) { + size_t st = 0; + size_t i; + for(i = 0; i < size - 1; i++) { + if(data[i] == '\n') { /* LF */ + if(!newl) { + log_line_start(output, timebuf, idsbuf, type); + } + (void)fwrite(data + st, i - st + 1, 1, output); + st = i + 1; + newl = FALSE; + } + } + if(!newl) + log_line_start(output, timebuf, idsbuf, type); + (void)fwrite(data + st, i - st + 1, 1, output); + } + newl = (size && (data[size - 1] != '\n')) ? TRUE : FALSE; + traced_data = FALSE; + break; + case CURLINFO_TEXT: + case CURLINFO_HEADER_IN: + if(!newl) + log_line_start(output, timebuf, idsbuf, type); + (void)fwrite(data, size, 1, output); + newl = (size && (data[size - 1] != '\n')) ? TRUE : FALSE; + traced_data = FALSE; + break; + case CURLINFO_DATA_OUT: + case CURLINFO_DATA_IN: + case CURLINFO_SSL_DATA_IN: + case CURLINFO_SSL_DATA_OUT: + if(!traced_data) { + /* if the data is output to a tty and we are sending this debug trace + to stderr or stdout, we do not display the alert about the data not + being shown as the data _is_ shown then just not via this + function */ + if(!config->isatty || + ((output != tool_stderr) && (output != stdout))) { + if(!newl) + log_line_start(output, timebuf, idsbuf, type); + fprintf(output, "[%zu bytes data]\n", size); + newl = FALSE; + traced_data = TRUE; + } + } + break; + default: /* nada */ + newl = FALSE; + traced_data = FALSE; + break; + } + + return 0; + } + + switch(type) { + case CURLINFO_TEXT: + fprintf(output, "%s%s== Info: %.*s", timebuf, idsbuf, (int)size, data); + FALLTHROUGH(); + default: /* in case a new one is introduced to shock us */ + return 0; + + case CURLINFO_HEADER_OUT: + text = "=> Send header"; + break; + case CURLINFO_DATA_OUT: + text = "=> Send data"; + break; + case CURLINFO_HEADER_IN: + text = "<= Recv header"; + break; + case CURLINFO_DATA_IN: + text = "<= Recv data"; + break; + case CURLINFO_SSL_DATA_IN: + text = "<= Recv SSL data"; + break; + case CURLINFO_SSL_DATA_OUT: + text = "=> Send SSL data"; + break; + } + + dump(timebuf, idsbuf, text, output, (unsigned char *) data, size, + config->tracetype, type); + return 0; +} + +static void dump(const char *timebuf, const char *idsbuf, const char *text, + FILE *stream, const unsigned char *ptr, size_t size, + trace tracetype, curl_infotype infotype) +{ + size_t i; + size_t c; + + unsigned int width = 0x10; + + if(tracetype == TRACE_ASCII) + /* without the hex output, we can fit more on screen */ + width = 0x40; + + fprintf(stream, "%s%s%s, %zu bytes (0x%zx)\n", timebuf, idsbuf, + text, size, size); + + for(i = 0; i < size; i += width) { + + fprintf(stream, "%04zx: ", i); + + if(tracetype == TRACE_BIN) { + /* hex not disabled, show it */ + for(c = 0; c < width; c++) + if(i + c < size) + fprintf(stream, "%02x ", ptr[i + c]); + else + fputs(" ", stream); + } + + for(c = 0; (c < width) && (i + c < size); c++) { + /* check for 0D0A; if found, skip past and start a new line of output */ + if((tracetype == TRACE_ASCII) && + (i + c + 1 < size) && (ptr[i + c] == 0x0D) && + (ptr[i + c + 1] == 0x0A)) { + i += (c + 2 - width); + break; + } + (void)infotype; + fprintf(stream, "%c", ((ptr[i + c] >= 0x20) && (ptr[i + c] < 0x7F)) ? + ptr[i + c] : UNPRINTABLE_CHAR); + /* check again for 0D0A, to avoid an extra \n if it is at width */ + if((tracetype == TRACE_ASCII) && + (i + c + 2 < size) && (ptr[i + c + 1] == 0x0D) && + (ptr[i + c + 2] == 0x0A)) { + i += (c + 3 - width); + break; + } + } + fputc('\n', stream); /* newline */ + } + fflush(stream); +} diff --git a/third_party/curl/src/tool_cb_dbg.h b/third_party/curl/src/tool_cb_dbg.h new file mode 100644 index 0000000000..d78afb36ea --- /dev/null +++ b/third_party/curl/src/tool_cb_dbg.h @@ -0,0 +1,36 @@ +#ifndef HEADER_CURL_TOOL_CB_DBG_H +#define HEADER_CURL_TOOL_CB_DBG_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +/* +** callback for CURLOPT_DEBUGFUNCTION +*/ + +int tool_debug_cb(CURL *handle, curl_infotype type, + char *data, size_t size, + void *userdata); + +#endif /* HEADER_CURL_TOOL_CB_DBG_H */ diff --git a/third_party/curl/src/tool_cb_hdr.c b/third_party/curl/src/tool_cb_hdr.c new file mode 100644 index 0000000000..96b6fabb7c --- /dev/null +++ b/third_party/curl/src/tool_cb_hdr.c @@ -0,0 +1,467 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#include "strcase.h" +#ifdef HAVE_UNISTD_H +#include +#endif + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_cfgable.h" +#include "tool_doswin.h" +#include "tool_msgs.h" +#include "tool_cb_hdr.h" +#include "tool_cb_wrt.h" +#include "tool_operate.h" +#include "tool_libinfo.h" + +#include "memdebug.h" /* keep this as LAST include */ + +static char *parse_filename(const char *ptr, size_t len); + +#ifdef _WIN32 +#define BOLD "\x1b[1m" +#define BOLDOFF "\x1b[22m" +#else +#define BOLD "\x1b[1m" +/* Switch off bold by setting "all attributes off" since the explicit + bold-off code (21) is not supported everywhere - like in the mac + Terminal. */ +#define BOLDOFF "\x1b[0m" +/* OSC 8 hyperlink escape sequence */ +#define LINK "\x1b]8;;" +#define LINKST "\x1b\\" +#define LINKOFF LINK LINKST +#endif + +#ifdef LINK +static void write_linked_location(CURL *curl, const char *location, + size_t loclen, FILE *stream); +#endif + +/* +** callback for CURLOPT_HEADERFUNCTION +*/ + +size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + struct per_transfer *per = userdata; + struct HdrCbData *hdrcbdata = &per->hdrcbdata; + struct OutStruct *outs = &per->outs; + struct OutStruct *heads = &per->heads; + struct OutStruct *etag_save = &per->etag_save; + const char *str = ptr; + const size_t cb = size * nmemb; + const char *end = (char *)ptr + cb; + const char *scheme = NULL; + + if(!per->config) + return CURL_WRITEFUNC_ERROR; + +#ifdef DEBUGBUILD + if(size * nmemb > (size_t)CURL_MAX_HTTP_HEADER) { + warnf(per->config->global, "Header data exceeds single call write limit"); + return CURL_WRITEFUNC_ERROR; + } +#endif + +#ifdef _WIN32 + /* Discard incomplete UTF-8 sequence buffered from body */ + if(outs->utf8seq[0]) + memset(outs->utf8seq, 0, sizeof(outs->utf8seq)); +#endif + + /* + * Write header data when curl option --dump-header (-D) is given. + */ + + if(per->config->headerfile && heads->stream) { + size_t rc = fwrite(ptr, size, nmemb, heads->stream); + if(rc != cb) + return rc; + /* flush the stream to send off what we got earlier */ + if(fflush(heads->stream)) { + errorf(per->config->global, "Failed writing headers to %s", + per->config->headerfile); + return CURL_WRITEFUNC_ERROR; + } + } + + curl_easy_getinfo(per->curl, CURLINFO_SCHEME, &scheme); + scheme = proto_token(scheme); + if((scheme == proto_http || scheme == proto_https)) { + long response = 0; + curl_easy_getinfo(per->curl, CURLINFO_RESPONSE_CODE, &response); + + if((response/100 != 2) && (response/100 != 3)) + /* only care about etag and content-disposition headers in 2xx and 3xx + responses */ + ; + /* + * Write etag to file when --etag-save option is given. + */ + else if(per->config->etag_save_file && etag_save->stream && + /* match only header that start with etag (case insensitive) */ + checkprefix("etag:", str)) { + const char *etag_h = &str[5]; + const char *eot = end - 1; + if(*eot == '\n') { + while(ISBLANK(*etag_h) && (etag_h < eot)) + etag_h++; + while(ISSPACE(*eot)) + eot--; + + if(eot >= etag_h) { + size_t etag_length = eot - etag_h + 1; + /* + * Truncate the etag save stream, it can have an existing etag value. + */ +#ifdef HAVE_FTRUNCATE + if(ftruncate(fileno(etag_save->stream), 0)) { + return CURL_WRITEFUNC_ERROR; + } +#else + if(fseek(etag_save->stream, 0, SEEK_SET)) { + return CURL_WRITEFUNC_ERROR; + } +#endif + + fwrite(etag_h, size, etag_length, etag_save->stream); + /* terminate with newline */ + fputc('\n', etag_save->stream); + (void)fflush(etag_save->stream); + } + } + } + + /* + * This callback sets the filename where output shall be written when + * curl options --remote-name (-O) and --remote-header-name (-J) have + * been simultaneously given and additionally server returns an HTTP + * Content-Disposition header specifying a filename property. + */ + + else if(hdrcbdata->honor_cd_filename && + (cb > 20) && checkprefix("Content-disposition:", str)) { + const char *p = str + 20; + + /* look for the 'filename=' parameter + (encoded filenames (*=) are not supported) */ + for(;;) { + char *filename; + size_t len; + + while((p < end) && *p && !ISALPHA(*p)) + p++; + if(p > end - 9) + break; + + if(memcmp(p, "filename=", 9)) { + /* no match, find next parameter */ + while((p < end) && *p && (*p != ';')) + p++; + if((p < end) && *p) + continue; + else + break; + } + p += 9; + + len = cb - (size_t)(p - str); + filename = parse_filename(p, len); + if(filename) { + if(outs->stream) { + /* indication of problem, get out! */ + free(filename); + return CURL_WRITEFUNC_ERROR; + } + + if(per->config->output_dir) { + outs->filename = aprintf("%s/%s", per->config->output_dir, + filename); + free(filename); + if(!outs->filename) + return CURL_WRITEFUNC_ERROR; + } + else + outs->filename = filename; + + outs->is_cd_filename = TRUE; + outs->s_isreg = TRUE; + outs->fopened = FALSE; + outs->alloc_filename = TRUE; + hdrcbdata->honor_cd_filename = FALSE; /* done now! */ + if(!tool_create_output_file(outs, per->config)) + return CURL_WRITEFUNC_ERROR; + } + break; + } + if(!outs->stream && !tool_create_output_file(outs, per->config)) + return CURL_WRITEFUNC_ERROR; + } + } + if(hdrcbdata->config->writeout) { + char *value = memchr(ptr, ':', cb); + if(value) { + if(per->was_last_header_empty) + per->num_headers = 0; + per->was_last_header_empty = FALSE; + per->num_headers++; + } + else if(ptr[0] == '\r' || ptr[0] == '\n') + per->was_last_header_empty = TRUE; + } + if(hdrcbdata->config->show_headers && + (scheme == proto_http || scheme == proto_https || + scheme == proto_rtsp || scheme == proto_file)) { + /* bold headers only for selected protocols */ + char *value = NULL; + + if(!outs->stream && !tool_create_output_file(outs, per->config)) + return CURL_WRITEFUNC_ERROR; + + if(hdrcbdata->global->isatty && +#ifdef _WIN32 + tool_term_has_bold && +#endif + hdrcbdata->global->styled_output) + value = memchr(ptr, ':', cb); + if(value) { + size_t namelen = value - ptr; + fprintf(outs->stream, BOLD "%.*s" BOLDOFF ":", (int)namelen, ptr); +#ifndef LINK + fwrite(&value[1], cb - namelen - 1, 1, outs->stream); +#else + if(curl_strnequal("Location", ptr, namelen)) { + write_linked_location(per->curl, &value[1], cb - namelen - 1, + outs->stream); + } + else + fwrite(&value[1], cb - namelen - 1, 1, outs->stream); +#endif + } + else + /* not "handled", just show it */ + fwrite(ptr, cb, 1, outs->stream); + } + return cb; +} + +/* + * Copies a filename part and returns an ALLOCATED data buffer. + */ +static char *parse_filename(const char *ptr, size_t len) +{ + char *copy; + char *p; + char *q; + char stop = '\0'; + + /* simple implementation of strndup() */ + copy = malloc(len + 1); + if(!copy) + return NULL; + memcpy(copy, ptr, len); + copy[len] = '\0'; + + p = copy; + if(*p == '\'' || *p == '"') { + /* store the starting quote */ + stop = *p; + p++; + } + else + stop = ';'; + + /* scan for the end letter and stop there */ + q = strchr(p, stop); + if(q) + *q = '\0'; + + /* if the filename contains a path, only use filename portion */ + q = strrchr(p, '/'); + if(q) { + p = q + 1; + if(!*p) { + Curl_safefree(copy); + return NULL; + } + } + + /* If the filename contains a backslash, only use filename portion. The idea + is that even systems that do not handle backslashes as path separators + probably want the path removed for convenience. */ + q = strrchr(p, '\\'); + if(q) { + p = q + 1; + if(!*p) { + Curl_safefree(copy); + return NULL; + } + } + + /* make sure the filename does not end in \r or \n */ + q = strchr(p, '\r'); + if(q) + *q = '\0'; + + q = strchr(p, '\n'); + if(q) + *q = '\0'; + + if(copy != p) + memmove(copy, p, strlen(p) + 1); + +#if defined(_WIN32) || defined(MSDOS) + { + char *sanitized; + SANITIZEcode sc = sanitize_file_name(&sanitized, copy, 0); + Curl_safefree(copy); + if(sc) + return NULL; + copy = sanitized; + } +#endif /* _WIN32 || MSDOS */ + + /* in case we built debug enabled, we allow an environment variable + * named CURL_TESTDIR to prefix the given filename to put it into a + * specific directory + */ +#ifdef DEBUGBUILD + { + char *tdir = curl_getenv("CURL_TESTDIR"); + if(tdir) { + char buffer[512]; /* suitably large */ + msnprintf(buffer, sizeof(buffer), "%s/%s", tdir, copy); + Curl_safefree(copy); + copy = strdup(buffer); /* clone the buffer, we do not use the libcurl + aprintf() or similar since we want to use the + same memory code as the "real" parse_filename + function */ + curl_free(tdir); + } + } +#endif + + return copy; +} + +#ifdef LINK +/* + * Treat the Location: header specially, by writing a special escape + * sequence that adds a hyperlink to the displayed text. This makes + * the absolute URL of the redirect clickable in supported terminals, + * which could not happen otherwise for relative URLs. The Location: + * header is supposed to always be absolute so this theoretically + * should not be needed but the real world returns plenty of relative + * URLs here. + */ +static +void write_linked_location(CURL *curl, const char *location, size_t loclen, + FILE *stream) { + /* This would so simple if CURLINFO_REDIRECT_URL were available here */ + CURLU *u = NULL; + char *copyloc = NULL, *locurl = NULL, *scheme = NULL, *finalurl = NULL; + const char *loc = location; + size_t llen = loclen; + int space_skipped = 0; + char *vver = getenv("VTE_VERSION"); + + if(vver) { + long vvn = strtol(vver, NULL, 10); + /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since some + of those versions have formatting bugs. (#10428) */ + if(0 < vvn && vvn <= 4801) + goto locout; + } + + /* Strip leading whitespace of the redirect URL */ + while(llen && (*loc == ' ' || *loc == '\t')) { + ++loc; + --llen; + ++space_skipped; + } + + /* Strip the trailing end-of-line characters, normally "\r\n" */ + while(llen && (loc[llen-1] == '\n' || loc[llen-1] == '\r')) + --llen; + + /* CURLU makes it easy to handle the relative URL case */ + u = curl_url(); + if(!u) + goto locout; + + /* Create a NUL-terminated and whitespace-stripped copy of Location: */ + copyloc = malloc(llen + 1); + if(!copyloc) + goto locout; + memcpy(copyloc, loc, llen); + copyloc[llen] = 0; + + /* The original URL to use as a base for a relative redirect URL */ + if(curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &locurl)) + goto locout; + if(curl_url_set(u, CURLUPART_URL, locurl, 0)) + goto locout; + + /* Redirected location. This can be either absolute or relative. */ + if(curl_url_set(u, CURLUPART_URL, copyloc, 0)) + goto locout; + + if(curl_url_get(u, CURLUPART_URL, &finalurl, CURLU_NO_DEFAULT_PORT)) + goto locout; + + if(curl_url_get(u, CURLUPART_SCHEME, &scheme, 0)) + goto locout; + + if(!strcmp("http", scheme) || + !strcmp("https", scheme) || + !strcmp("ftp", scheme) || + !strcmp("ftps", scheme)) { + fprintf(stream, "%.*s" LINK "%s" LINKST "%.*s" LINKOFF, + space_skipped, location, + finalurl, + (int)loclen - space_skipped, loc); + goto locdone; + } + + /* Not a "safe" URL: do not linkify it */ + +locout: + /* Write the normal output in case of error or unsafe */ + fwrite(location, loclen, 1, stream); + +locdone: + if(u) { + curl_free(finalurl); + curl_free(scheme); + curl_url_cleanup(u); + free(copyloc); + } +} +#endif diff --git a/third_party/curl/src/tool_cb_hdr.h b/third_party/curl/src/tool_cb_hdr.h new file mode 100644 index 0000000000..a855052d0d --- /dev/null +++ b/third_party/curl/src/tool_cb_hdr.h @@ -0,0 +1,58 @@ +#ifndef HEADER_CURL_TOOL_CB_HDR_H +#define HEADER_CURL_TOOL_CB_HDR_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +/* + * curl operates using a single HdrCbData struct variable, a + * pointer to this is passed as userdata pointer to tool_header_cb. + * + * 'outs' member is a pointer to the OutStruct variable used to keep + * track of information relative to curl's output writing. + * + * 'heads' member is a pointer to the OutStruct variable used to keep + * track of information relative to header response writing. + * + * 'honor_cd_filename' member is TRUE when tool_header_cb is allowed + * to honor Content-Disposition filename property and accordingly + * set 'outs' filename, otherwise FALSE; + */ + +struct HdrCbData { + struct GlobalConfig *global; + struct OperationConfig *config; + struct OutStruct *outs; + struct OutStruct *heads; + struct OutStruct *etag_save; + bool honor_cd_filename; +}; + +/* +** callback for CURLOPT_HEADERFUNCTION +*/ + +size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata); + +#endif /* HEADER_CURL_TOOL_CB_HDR_H */ diff --git a/third_party/curl/src/tool_cb_prg.c b/third_party/curl/src/tool_cb_prg.c new file mode 100644 index 0000000000..4c3d3cdb5a --- /dev/null +++ b/third_party/curl/src/tool_cb_prg.c @@ -0,0 +1,243 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_cfgable.h" +#include "tool_cb_prg.h" +#include "tool_util.h" +#include "tool_operate.h" +#include "terminal.h" + +#include "memdebug.h" /* keep this as LAST include */ + +#define MAX_BARLENGTH 256 + +/* 200 values generated by this perl code: + + my $pi = 3.1415; + foreach my $i (1 .. 200) { + printf "%d, ", sin($i/200 * 2 * $pi) * 500000 + 500000; + } +*/ +static const int sinus[] = { + 515704, 531394, 547052, 562664, 578214, 593687, 609068, 624341, 639491, + 654504, 669364, 684057, 698568, 712883, 726989, 740870, 754513, 767906, + 781034, 793885, 806445, 818704, 830647, 842265, 853545, 864476, 875047, + 885248, 895069, 904500, 913532, 922156, 930363, 938145, 945495, 952406, + 958870, 964881, 970434, 975522, 980141, 984286, 987954, 991139, 993840, + 996054, 997778, 999011, 999752, 999999, 999754, 999014, 997783, 996060, + 993848, 991148, 987964, 984298, 980154, 975536, 970449, 964898, 958888, + 952426, 945516, 938168, 930386, 922180, 913558, 904527, 895097, 885277, + 875077, 864507, 853577, 842299, 830682, 818739, 806482, 793922, 781072, + 767945, 754553, 740910, 727030, 712925, 698610, 684100, 669407, 654548, + 639536, 624386, 609113, 593733, 578260, 562710, 547098, 531440, 515751, + 500046, 484341, 468651, 452993, 437381, 421830, 406357, 390976, 375703, + 360552, 345539, 330679, 315985, 301474, 287158, 273052, 259170, 245525, + 232132, 219003, 206152, 193590, 181331, 169386, 157768, 146487, 135555, + 124983, 114781, 104959, 95526, 86493, 77868, 69660, 61876, 54525, 47613, + 41147, 35135, 29581, 24491, 19871, 15724, 12056, 8868, 6166, 3951, 2225, + 990, 248, 0, 244, 982, 2212, 3933, 6144, 8842, 12025, 15690, 19832, 24448, + 29534, 35084, 41092, 47554, 54462, 61809, 69589, 77794, 86415, 95445, + 104873, 114692, 124891, 135460, 146389, 157667, 169282, 181224, 193480, + 206039, 218888, 232015, 245406, 259048, 272928, 287032, 301346, 315856, + 330548, 345407, 360419, 375568, 390841, 406221, 421693, 437243, 452854, + 468513, 484202, 499907 +}; + +static void fly(struct ProgressData *bar, bool moved) +{ + char buf[MAX_BARLENGTH + 2]; + int pos; + int check = bar->width - 2; + + /* bar->width is range checked when assigned */ + DEBUGASSERT(bar->width <= MAX_BARLENGTH); + buf[0] = '\r'; + memset(&buf[1], ' ', bar->width); + buf[bar->width + 1] = '\0'; + + memcpy(&buf[bar->bar + 1], "-=O=-", 5); + + pos = sinus[bar->tick%200] / (1000000 / check) + 1; + buf[pos] = '#'; + pos = sinus[(bar->tick + 5)%200] / (1000000 / check) + 1; + buf[pos] = '#'; + pos = sinus[(bar->tick + 10)%200] / (1000000 / check) + 1; + buf[pos] = '#'; + pos = sinus[(bar->tick + 15)%200] / (1000000 / check) + 1; + buf[pos] = '#'; + + fputs(buf, bar->out); + bar->tick += 2; + if(bar->tick >= 200) + bar->tick -= 200; + + bar->bar += (moved?bar->barmove:0); + if(bar->bar >= (bar->width - 6)) { + bar->barmove = -1; + bar->bar = bar->width - 6; + } + else if(bar->bar < 0) { + bar->barmove = 1; + bar->bar = 0; + } +} + +/* +** callback for CURLOPT_XFERINFOFUNCTION +*/ + +#if (SIZEOF_CURL_OFF_T < 8) +#error "too small curl_off_t" +#else + /* assume SIZEOF_CURL_OFF_T == 8 */ +# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) +#endif + +int tool_progress_cb(void *clientp, + curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow) +{ + struct timeval now = tvnow(); + struct per_transfer *per = clientp; + struct OperationConfig *config = per->config; + struct ProgressData *bar = &per->progressbar; + curl_off_t total; + curl_off_t point; + + /* Calculate expected transfer size. initial_size can be less than zero when + indicating that we are expecting to get the filesize from the remote */ + if(bar->initial_size < 0) { + if(dltotal || ultotal) + total = dltotal + ultotal; + else + total = CURL_OFF_T_MAX; + } + else if((CURL_OFF_T_MAX - bar->initial_size) < (dltotal + ultotal)) + total = CURL_OFF_T_MAX; + else + total = dltotal + ultotal + bar->initial_size; + + /* Calculate the current progress. initial_size can be less than zero when + indicating that we are expecting to get the filesize from the remote */ + if(bar->initial_size < 0) { + if(dltotal || ultotal) + point = dlnow + ulnow; + else + point = CURL_OFF_T_MAX; + } + else if((CURL_OFF_T_MAX - bar->initial_size) < (dlnow + ulnow)) + point = CURL_OFF_T_MAX; + else + point = dlnow + ulnow + bar->initial_size; + + if(bar->calls) { + /* after first call... */ + if(total) { + /* we know the total data to get... */ + if(bar->prev == point) + /* progress did not change since last invoke */ + return 0; + else if((tvdiff(now, bar->prevtime) < 100L) && point < total) + /* limit progress-bar updating to 10 Hz except when we are at 100% */ + return 0; + } + else { + /* total is unknown */ + if(tvdiff(now, bar->prevtime) < 100L) + /* limit progress-bar updating to 10 Hz */ + return 0; + fly(bar, point != bar->prev); + } + } + + /* simply count invokes */ + bar->calls++; + + if((total > 0) && (point != bar->prev)) { + char line[MAX_BARLENGTH + 1]; + char format[40]; + double frac; + double percent; + int barwidth; + size_t num; + if(point > total) + /* we have got more than the expected total! */ + total = point; + + frac = (double)point / (double)total; + percent = frac * 100.0; + barwidth = bar->width - 7; + num = (size_t) (((double)barwidth) * frac); + if(num > MAX_BARLENGTH) + num = MAX_BARLENGTH; + memset(line, '#', num); + line[num] = '\0'; + msnprintf(format, sizeof(format), "\r%%-%ds %%5.1f%%%%", barwidth); +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif + fprintf(bar->out, format, line, percent); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + fflush(bar->out); + bar->prev = point; + bar->prevtime = now; + + if(config->readbusy) { + config->readbusy = FALSE; + curl_easy_pause(per->curl, CURLPAUSE_CONT); + } + + return 0; +} + +void progressbarinit(struct ProgressData *bar, + struct OperationConfig *config) +{ + unsigned int cols; + memset(bar, 0, sizeof(struct ProgressData)); + + /* pass the resume from value through to the progress function so it can + * display progress towards total file not just the part that is left. */ + if(config->use_resume) + bar->initial_size = config->resume_from; + + cols = get_terminal_columns(); + if(cols > MAX_BARLENGTH) + bar->width = MAX_BARLENGTH; + else if(cols > 20) + bar->width = (int)cols; + + bar->out = tool_stderr; + bar->tick = 150; + bar->barmove = 1; +} diff --git a/third_party/curl/src/tool_cb_prg.h b/third_party/curl/src/tool_cb_prg.h new file mode 100644 index 0000000000..dc10f2a5cc --- /dev/null +++ b/third_party/curl/src/tool_cb_prg.h @@ -0,0 +1,56 @@ +#ifndef HEADER_CURL_TOOL_CB_PRG_H +#define HEADER_CURL_TOOL_CB_PRG_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#define CURL_PROGRESS_STATS 0 /* default progress display */ +#define CURL_PROGRESS_BAR 1 + +struct ProgressData { + int calls; + curl_off_t prev; + struct timeval prevtime; + int width; + FILE *out; /* where to write everything to */ + curl_off_t initial_size; + unsigned int tick; + int bar; + int barmove; +}; + +struct OperationConfig; + +void progressbarinit(struct ProgressData *bar, + struct OperationConfig *config); + +/* +** callback for CURLOPT_PROGRESSFUNCTION +*/ + +int tool_progress_cb(void *clientp, + curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow); + +#endif /* HEADER_CURL_TOOL_CB_PRG_H */ diff --git a/third_party/curl/src/tool_cb_rea.c b/third_party/curl/src/tool_cb_rea.c new file mode 100644 index 0000000000..1e23895ca5 --- /dev/null +++ b/third_party/curl/src/tool_cb_rea.c @@ -0,0 +1,158 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_cfgable.h" +#include "tool_cb_rea.h" +#include "tool_operate.h" +#include "tool_util.h" +#include "tool_msgs.h" +#include "tool_sleep.h" + +#include "memdebug.h" /* keep this as LAST include */ + +/* +** callback for CURLOPT_READFUNCTION +*/ + +size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) +{ + ssize_t rc = 0; + struct per_transfer *per = userdata; + struct OperationConfig *config = per->config; + + if((per->uploadfilesize != -1) && + (per->uploadedsofar == per->uploadfilesize)) { + /* done */ + return 0; + } + + if(config->timeout_ms) { + struct timeval now = tvnow(); + long msdelta = tvdiff(now, per->start); + + if(msdelta > config->timeout_ms) + /* timeout */ + return 0; +#ifndef _WIN32 + /* this logic waits on read activity on a file descriptor that is not a + socket which makes it not work with select() on Windows */ + else { + fd_set bits; + struct timeval timeout; + long wait = config->timeout_ms - msdelta; + + /* wait this long at the most */ + timeout.tv_sec = wait/1000; + timeout.tv_usec = (int)((wait%1000)*1000); + + FD_ZERO(&bits); + FD_SET(per->infd, &bits); + if(!select(per->infd + 1, &bits, NULL, NULL, &timeout)) + return 0; /* timeout */ + } +#endif + } + + rc = read(per->infd, buffer, sz*nmemb); + if(rc < 0) { + if(errno == EAGAIN) { + errno = 0; + config->readbusy = TRUE; + return CURL_READFUNC_PAUSE; + } + /* since size_t is unsigned we cannot return negative values fine */ + rc = 0; + } + if((per->uploadfilesize != -1) && + (per->uploadedsofar + rc > per->uploadfilesize)) { + /* do not allow uploading more than originally set out to do */ + curl_off_t delta = per->uploadedsofar + rc - per->uploadfilesize; + warnf(per->config->global, "File size larger in the end than when " + "started. Dropping at least %" CURL_FORMAT_CURL_OFF_T " bytes", + delta); + rc = (ssize_t)(per->uploadfilesize - per->uploadedsofar); + } + config->readbusy = FALSE; + + /* when select() returned zero here, it timed out */ + return (size_t)rc; +} + +/* +** callback for CURLOPT_XFERINFOFUNCTION used to unpause busy reads +*/ + +int tool_readbusy_cb(void *clientp, + curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow) +{ + struct per_transfer *per = clientp; + struct OperationConfig *config = per->config; + + (void)dltotal; /* unused */ + (void)dlnow; /* unused */ + (void)ultotal; /* unused */ + (void)ulnow; /* unused */ + + if(config->readbusy) { + /* lame code to keep the rate down because the input might not deliver + anything, get paused again and come back here immediately */ + static long rate = 500; + static struct timeval prev; + static curl_off_t ulprev; + + if(ulprev == ulnow) { + /* it did not upload anything since last call */ + struct timeval now = tvnow(); + if(prev.tv_sec) + /* get a rolling average rate */ + /* rate = rate - rate/4 + tvdiff(now, prev)/4; */ + rate -= rate/4 - tvdiff(now, prev)/4; + prev = now; + } + else { + rate = 50; + ulprev = ulnow; + } + if(rate >= 50) { + /* keeps the looping down to 20 times per second in the crazy case */ + config->readbusy = FALSE; + curl_easy_pause(per->curl, CURLPAUSE_CONT); + } + else + /* sleep half a period */ + tool_go_sleep(25); + } + + return per->noprogress? 0 : CURL_PROGRESSFUNC_CONTINUE; +} diff --git a/third_party/curl/src/tool_cb_rea.h b/third_party/curl/src/tool_cb_rea.h new file mode 100644 index 0000000000..06899d3ef3 --- /dev/null +++ b/third_party/curl/src/tool_cb_rea.h @@ -0,0 +1,42 @@ +#ifndef HEADER_CURL_TOOL_CB_REA_H +#define HEADER_CURL_TOOL_CB_REA_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +/* +** callback for CURLOPT_READFUNCTION +*/ + +size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata); + +/* +** callback for CURLOPT_XFERINFOFUNCTION used to unpause busy reads +*/ + +int tool_readbusy_cb(void *clientp, + curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow); + +#endif /* HEADER_CURL_TOOL_CB_REA_H */ diff --git a/third_party/curl/src/tool_cb_see.c b/third_party/curl/src/tool_cb_see.c new file mode 100644 index 0000000000..7ce678aabf --- /dev/null +++ b/third_party/curl/src/tool_cb_see.c @@ -0,0 +1,92 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_cfgable.h" +#include "tool_operate.h" +#include "tool_cb_see.h" + +#include "memdebug.h" /* keep this as LAST include */ + +/* OUR_MAX_SEEK_L has 'long' data type, OUR_MAX_SEEK_O has 'curl_off_t, + both represent the same value. Maximum offset used here when we lseek + using a 'long' data type offset */ + +#define OUR_MAX_SEEK_L 2147483647L - 1L +#define OUR_MAX_SEEK_O CURL_OFF_T_C(0x7FFFFFFF) - CURL_OFF_T_C(0x1) + +/* +** callback for CURLOPT_SEEKFUNCTION +** +** Notice that this is not supposed to return the resulting offset. This +** shall only return CURL_SEEKFUNC_* return codes. +*/ + +int tool_seek_cb(void *userdata, curl_off_t offset, int whence) +{ + struct per_transfer *per = userdata; + +#if(SIZEOF_CURL_OFF_T > SIZEOF_OFF_T) && !defined(USE_WIN32_LARGE_FILES) + + /* The offset check following here is only interesting if curl_off_t is + larger than off_t and we are not using the WIN32 large file support + macros that provide the support to do 64bit seeks correctly */ + + if(offset > OUR_MAX_SEEK_O) { + /* Some precaution code to work around problems with different data sizes + to allow seeking >32bit even if off_t is 32bit. Should be very rare and + is really valid on weirdo-systems. */ + curl_off_t left = offset; + + if(whence != SEEK_SET) + /* this code path does not support other types */ + return CURL_SEEKFUNC_FAIL; + + if(LSEEK_ERROR == lseek(per->infd, 0, SEEK_SET)) + /* could not rewind to beginning */ + return CURL_SEEKFUNC_FAIL; + + while(left) { + long step = (left > OUR_MAX_SEEK_O) ? OUR_MAX_SEEK_L : (long)left; + if(LSEEK_ERROR == lseek(per->infd, step, SEEK_CUR)) + /* could not seek forwards the desired amount */ + return CURL_SEEKFUNC_FAIL; + left -= step; + } + return CURL_SEEKFUNC_OK; + } +#endif + + if(LSEEK_ERROR == lseek(per->infd, offset, whence)) + /* could not rewind, the reason is in errno but errno is just not portable + enough and we do not actually care that much why we failed. We will let + libcurl know that it may try other means if it wants to. */ + return CURL_SEEKFUNC_CANTSEEK; + + return CURL_SEEKFUNC_OK; +} diff --git a/third_party/curl/src/tool_cb_see.h b/third_party/curl/src/tool_cb_see.h new file mode 100644 index 0000000000..e7b30a765d --- /dev/null +++ b/third_party/curl/src/tool_cb_see.h @@ -0,0 +1,34 @@ +#ifndef HEADER_CURL_TOOL_CB_SEE_H +#define HEADER_CURL_TOOL_CB_SEE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +/* +** callback for CURLOPT_SEEKFUNCTION +*/ + +int tool_seek_cb(void *userdata, curl_off_t offset, int whence); + +#endif /* HEADER_CURL_TOOL_CB_SEE_H */ diff --git a/third_party/curl/src/tool_cb_soc.c b/third_party/curl/src/tool_cb_soc.c new file mode 100644 index 0000000000..22048ee6bb --- /dev/null +++ b/third_party/curl/src/tool_cb_soc.c @@ -0,0 +1,58 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include /* IPPROTO_TCP */ +#endif + +#include "tool_cb_soc.h" + +/* +** callback for CURLOPT_OPENSOCKETFUNCTION +** +** Notice that only Linux is supported for the moment. +*/ + +curl_socket_t tool_socket_open_mptcp_cb(void *clientp, + curlsocktype purpose, + struct curl_sockaddr *addr) +{ + int protocol = addr->protocol; + + (void)clientp; + (void)purpose; + + if(protocol == IPPROTO_TCP) +#if defined(__linux__) +# ifndef IPPROTO_MPTCP +# define IPPROTO_MPTCP 262 +# endif + protocol = IPPROTO_MPTCP; +#else + return CURL_SOCKET_BAD; +#endif + + return socket(addr->family, addr->socktype, protocol); +} diff --git a/third_party/curl/src/tool_cb_soc.h b/third_party/curl/src/tool_cb_soc.h new file mode 100644 index 0000000000..f02150aa82 --- /dev/null +++ b/third_party/curl/src/tool_cb_soc.h @@ -0,0 +1,36 @@ +#ifndef HEADER_CURL_TOOL_CB_SOC_H +#define HEADER_CURL_TOOL_CB_SOC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +/* +** callback for CURLOPT_OPENSOCKETFUNCTION +*/ + +curl_socket_t tool_socket_open_mptcp_cb(void *clientp, + curlsocktype purpose, + struct curl_sockaddr *addr); + +#endif /* HEADER_CURL_TOOL_CB_SOC_H */ diff --git a/third_party/curl/src/tool_cb_wrt.c b/third_party/curl/src/tool_cb_wrt.c new file mode 100644 index 0000000000..ccc0109171 --- /dev/null +++ b/third_party/curl/src/tool_cb_wrt.c @@ -0,0 +1,372 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifdef HAVE_FCNTL_H +/* for open() */ +#include +#endif + +#include + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_cfgable.h" +#include "tool_msgs.h" +#include "tool_cb_wrt.h" +#include "tool_operate.h" + +#include "memdebug.h" /* keep this as LAST include */ + +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#ifdef _WIN32 +#define OPENMODE S_IREAD | S_IWRITE +#else +#define OPENMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH +#endif + +/* create/open a local file for writing, return TRUE on success */ +bool tool_create_output_file(struct OutStruct *outs, + struct OperationConfig *config) +{ + struct GlobalConfig *global; + FILE *file = NULL; + char *fname = outs->filename; + DEBUGASSERT(outs); + DEBUGASSERT(config); + global = config->global; + if(!fname || !*fname) { + warnf(global, "Remote filename has no length"); + return FALSE; + } + + if(config->file_clobber_mode == CLOBBER_ALWAYS || + (config->file_clobber_mode == CLOBBER_DEFAULT && + !outs->is_cd_filename)) { + /* open file for writing */ + file = fopen(fname, "wb"); + } + else { + int fd; + do { + fd = open(fname, O_CREAT | O_WRONLY | O_EXCL | O_BINARY, OPENMODE); + /* Keep retrying in the hope that it is not interrupted sometime */ + } while(fd == -1 && errno == EINTR); + if(config->file_clobber_mode == CLOBBER_NEVER && fd == -1) { + int next_num = 1; + size_t len = strlen(fname); + size_t newlen = len + 13; /* nul + 1-11 digits + dot */ + char *newname; + /* Guard against wraparound in new filename */ + if(newlen < len) { + errorf(global, "overflow in filename generation"); + return FALSE; + } + newname = malloc(newlen); + if(!newname) { + errorf(global, "out of memory"); + return FALSE; + } + memcpy(newname, fname, len); + newname[len] = '.'; + while(fd == -1 && /* have not successfully opened a file */ + (errno == EEXIST || errno == EISDIR) && + /* because we keep having files that already exist */ + next_num < 100 /* and we have not reached the retry limit */ ) { + curlx_msnprintf(newname + len + 1, 12, "%d", next_num); + next_num++; + do { + fd = open(newname, O_CREAT | O_WRONLY | O_EXCL | O_BINARY, OPENMODE); + /* Keep retrying in the hope that it is not interrupted sometime */ + } while(fd == -1 && errno == EINTR); + } + outs->filename = newname; /* remember the new one */ + outs->alloc_filename = TRUE; + } + /* An else statement to not overwrite existing files and not retry with + new numbered names (which would cover + config->file_clobber_mode == CLOBBER_DEFAULT && outs->is_cd_filename) + is not needed because we would have failed earlier, in the while loop + and `fd` would now be -1 */ + if(fd != -1) { + file = fdopen(fd, "wb"); + if(!file) + close(fd); + } + } + + if(!file) { + warnf(global, "Failed to open the file %s: %s", fname, + strerror(errno)); + return FALSE; + } + outs->s_isreg = TRUE; + outs->fopened = TRUE; + outs->stream = file; + outs->bytes = 0; + outs->init = 0; + return TRUE; +} + +/* +** callback for CURLOPT_WRITEFUNCTION +*/ + +size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) +{ + size_t rc; + struct per_transfer *per = userdata; + struct OutStruct *outs = &per->outs; + struct OperationConfig *config = per->config; + size_t bytes = sz * nmemb; + bool is_tty = config->global->isatty; +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO console_info; + intptr_t fhnd; +#endif + +#ifdef DEBUGBUILD + { + char *tty = curl_getenv("CURL_ISATTY"); + if(tty) { + is_tty = TRUE; + curl_free(tty); + } + } + + if(config->show_headers) { + if(bytes > (size_t)CURL_MAX_HTTP_HEADER) { + warnf(config->global, "Header data size exceeds single call write " + "limit"); + return CURL_WRITEFUNC_ERROR; + } + } + else { + if(bytes > (size_t)CURL_MAX_WRITE_SIZE) { + warnf(config->global, "Data size exceeds single call write limit"); + return CURL_WRITEFUNC_ERROR; + } + } + + { + /* Some internal congruency checks on received OutStruct */ + bool check_fails = FALSE; + if(outs->filename) { + /* regular file */ + if(!*outs->filename) + check_fails = TRUE; + if(!outs->s_isreg) + check_fails = TRUE; + if(outs->fopened && !outs->stream) + check_fails = TRUE; + if(!outs->fopened && outs->stream) + check_fails = TRUE; + if(!outs->fopened && outs->bytes) + check_fails = TRUE; + } + else { + /* standard stream */ + if(!outs->stream || outs->s_isreg || outs->fopened) + check_fails = TRUE; + if(outs->alloc_filename || outs->is_cd_filename || outs->init) + check_fails = TRUE; + } + if(check_fails) { + warnf(config->global, "Invalid output struct data for write callback"); + return CURL_WRITEFUNC_ERROR; + } + } +#endif + + if(!outs->stream && !tool_create_output_file(outs, per->config)) + return CURL_WRITEFUNC_ERROR; + + if(is_tty && (outs->bytes < 2000) && !config->terminal_binary_ok) { + /* binary output to terminal? */ + if(memchr(buffer, 0, bytes)) { + warnf(config->global, "Binary output can mess up your terminal. " + "Use \"--output -\" to tell curl to output it to your terminal " + "anyway, or consider \"--output \" to save to a file."); + config->synthetic_error = TRUE; + return CURL_WRITEFUNC_ERROR; + } + } + +#ifdef _WIN32 + fhnd = _get_osfhandle(fileno(outs->stream)); + /* if windows console then UTF-8 must be converted to UTF-16 */ + if(isatty(fileno(outs->stream)) && + GetConsoleScreenBufferInfo((HANDLE)fhnd, &console_info)) { + wchar_t *wc_buf; + DWORD wc_len, chars_written; + unsigned char *rbuf = (unsigned char *)buffer; + DWORD rlen = (DWORD)bytes; + +#define IS_TRAILING_BYTE(x) (0x80 <= (x) && (x) < 0xC0) + + /* attempt to complete an incomplete UTF-8 sequence from previous call. + the sequence does not have to be well-formed. */ + if(outs->utf8seq[0] && rlen) { + bool complete = false; + /* two byte sequence (lead byte 110yyyyy) */ + if(0xC0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xE0) { + outs->utf8seq[1] = *rbuf++; + --rlen; + complete = true; + } + /* three byte sequence (lead byte 1110zzzz) */ + else if(0xE0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xF0) { + if(!outs->utf8seq[1]) { + outs->utf8seq[1] = *rbuf++; + --rlen; + } + if(rlen && !outs->utf8seq[2]) { + outs->utf8seq[2] = *rbuf++; + --rlen; + complete = true; + } + } + /* four byte sequence (lead byte 11110uuu) */ + else if(0xF0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xF8) { + if(!outs->utf8seq[1]) { + outs->utf8seq[1] = *rbuf++; + --rlen; + } + if(rlen && !outs->utf8seq[2]) { + outs->utf8seq[2] = *rbuf++; + --rlen; + } + if(rlen && !outs->utf8seq[3]) { + outs->utf8seq[3] = *rbuf++; + --rlen; + complete = true; + } + } + + if(complete) { + WCHAR prefix[3] = {0}; /* UTF-16 (1-2 WCHARs) + NUL */ + + if(MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)outs->utf8seq, -1, + prefix, sizeof(prefix)/sizeof(prefix[0]))) { + DEBUGASSERT(prefix[2] == L'\0'); + if(!WriteConsoleW( + (HANDLE) fhnd, + prefix, + prefix[1] ? 2 : 1, + &chars_written, + NULL)) { + return CURL_WRITEFUNC_ERROR; + } + } + /* else: UTF-8 input was not well formed and OS is pre-Vista which + drops invalid characters instead of writing U+FFFD to output. */ + + memset(outs->utf8seq, 0, sizeof(outs->utf8seq)); + } + } + + /* suppress an incomplete utf-8 sequence at end of rbuf */ + if(!outs->utf8seq[0] && rlen && (rbuf[rlen - 1] & 0x80)) { + /* check for lead byte from a two, three or four byte sequence */ + if(0xC0 <= rbuf[rlen - 1] && rbuf[rlen - 1] < 0xF8) { + outs->utf8seq[0] = rbuf[rlen - 1]; + rlen -= 1; + } + else if(rlen >= 2 && IS_TRAILING_BYTE(rbuf[rlen - 1])) { + /* check for lead byte from a three or four byte sequence */ + if(0xE0 <= rbuf[rlen - 2] && rbuf[rlen - 2] < 0xF8) { + outs->utf8seq[0] = rbuf[rlen - 2]; + outs->utf8seq[1] = rbuf[rlen - 1]; + rlen -= 2; + } + else if(rlen >= 3 && IS_TRAILING_BYTE(rbuf[rlen - 2])) { + /* check for lead byte from a four byte sequence */ + if(0xF0 <= rbuf[rlen - 3] && rbuf[rlen - 3] < 0xF8) { + outs->utf8seq[0] = rbuf[rlen - 3]; + outs->utf8seq[1] = rbuf[rlen - 2]; + outs->utf8seq[2] = rbuf[rlen - 1]; + rlen -= 3; + } + } + } + } + + if(rlen) { + /* calculate buffer size for wide characters */ + wc_len = (DWORD)MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)rbuf, (int)rlen, + NULL, 0); + if(!wc_len) + return CURL_WRITEFUNC_ERROR; + + wc_buf = (wchar_t*) malloc(wc_len * sizeof(wchar_t)); + if(!wc_buf) + return CURL_WRITEFUNC_ERROR; + + wc_len = (DWORD)MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)rbuf, (int)rlen, + wc_buf, (int)wc_len); + if(!wc_len) { + free(wc_buf); + return CURL_WRITEFUNC_ERROR; + } + + if(!WriteConsoleW( + (HANDLE) fhnd, + wc_buf, + wc_len, + &chars_written, + NULL)) { + free(wc_buf); + return CURL_WRITEFUNC_ERROR; + } + free(wc_buf); + } + + rc = bytes; + } + else +#endif + rc = fwrite(buffer, sz, nmemb, outs->stream); + + if(bytes == rc) + /* we added this amount of data to the output */ + outs->bytes += bytes; + + if(config->readbusy) { + config->readbusy = FALSE; + curl_easy_pause(per->curl, CURLPAUSE_CONT); + } + + if(config->nobuffer) { + /* output buffering disabled */ + int res = fflush(outs->stream); + if(res) + return CURL_WRITEFUNC_ERROR; + } + + return rc; +} diff --git a/third_party/curl/src/tool_cb_wrt.h b/third_party/curl/src/tool_cb_wrt.h new file mode 100644 index 0000000000..55502f440d --- /dev/null +++ b/third_party/curl/src/tool_cb_wrt.h @@ -0,0 +1,38 @@ +#ifndef HEADER_CURL_TOOL_CB_WRT_H +#define HEADER_CURL_TOOL_CB_WRT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +/* +** callback for CURLOPT_WRITEFUNCTION +*/ + +size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata); + +/* create a local file for writing, return TRUE on success */ +bool tool_create_output_file(struct OutStruct *outs, + struct OperationConfig *config); + +#endif /* HEADER_CURL_TOOL_CB_WRT_H */ diff --git a/third_party/curl/src/tool_cfgable.c b/third_party/curl/src/tool_cfgable.c new file mode 100644 index 0000000000..5564e250d3 --- /dev/null +++ b/third_party/curl/src/tool_cfgable.c @@ -0,0 +1,204 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#include "tool_cfgable.h" +#include "tool_formparse.h" +#include "tool_paramhlp.h" +#include "tool_main.h" + +#include "memdebug.h" /* keep this as LAST include */ + +void config_init(struct OperationConfig *config) +{ + memset(config, 0, sizeof(struct OperationConfig)); + + config->use_httpget = FALSE; + config->create_dirs = FALSE; + config->maxredirs = DEFAULT_MAXREDIRS; + config->proto_present = FALSE; + config->proto_redir_present = FALSE; + config->proto_default = NULL; + config->tcp_nodelay = TRUE; /* enabled by default */ + config->happy_eyeballs_timeout_ms = CURL_HET_DEFAULT; + config->http09_allowed = FALSE; + config->ftp_skip_ip = TRUE; + config->file_clobber_mode = CLOBBER_DEFAULT; + curlx_dyn_init(&config->postdata, MAX_FILE2MEMORY); +} + +static void free_config_fields(struct OperationConfig *config) +{ + struct getout *urlnode; + + Curl_safefree(config->useragent); + Curl_safefree(config->altsvc); + Curl_safefree(config->hsts); + Curl_safefree(config->haproxy_clientip); + curl_slist_free_all(config->cookies); + Curl_safefree(config->cookiejar); + curl_slist_free_all(config->cookiefiles); + + Curl_dyn_free(&config->postdata); + Curl_safefree(config->query); + Curl_safefree(config->referer); + + Curl_safefree(config->headerfile); + Curl_safefree(config->ftpport); + Curl_safefree(config->iface); + + Curl_safefree(config->range); + + Curl_safefree(config->userpwd); + Curl_safefree(config->tls_username); + Curl_safefree(config->tls_password); + Curl_safefree(config->tls_authtype); + Curl_safefree(config->proxy_tls_username); + Curl_safefree(config->proxy_tls_password); + Curl_safefree(config->proxy_tls_authtype); + Curl_safefree(config->proxyuserpwd); + Curl_safefree(config->proxy); + + Curl_safefree(config->dns_ipv6_addr); + Curl_safefree(config->dns_ipv4_addr); + Curl_safefree(config->dns_interface); + Curl_safefree(config->dns_servers); + + Curl_safefree(config->noproxy); + + Curl_safefree(config->mail_from); + curl_slist_free_all(config->mail_rcpt); + Curl_safefree(config->mail_auth); + + Curl_safefree(config->netrc_file); + Curl_safefree(config->output_dir); + Curl_safefree(config->proto_str); + Curl_safefree(config->proto_redir_str); + + urlnode = config->url_list; + while(urlnode) { + struct getout *next = urlnode->next; + Curl_safefree(urlnode->url); + Curl_safefree(urlnode->outfile); + Curl_safefree(urlnode->infile); + Curl_safefree(urlnode); + urlnode = next; + } + config->url_list = NULL; + config->url_last = NULL; + config->url_get = NULL; + config->url_out = NULL; + + Curl_safefree(config->ipfs_gateway); + Curl_safefree(config->doh_url); + Curl_safefree(config->cipher_list); + Curl_safefree(config->proxy_cipher_list); + Curl_safefree(config->cipher13_list); + Curl_safefree(config->proxy_cipher13_list); + Curl_safefree(config->cert); + Curl_safefree(config->proxy_cert); + Curl_safefree(config->cert_type); + Curl_safefree(config->proxy_cert_type); + Curl_safefree(config->cacert); + Curl_safefree(config->login_options); + Curl_safefree(config->proxy_cacert); + Curl_safefree(config->capath); + Curl_safefree(config->proxy_capath); + Curl_safefree(config->crlfile); + Curl_safefree(config->pinnedpubkey); + Curl_safefree(config->proxy_pinnedpubkey); + Curl_safefree(config->proxy_crlfile); + Curl_safefree(config->key); + Curl_safefree(config->proxy_key); + Curl_safefree(config->key_type); + Curl_safefree(config->proxy_key_type); + Curl_safefree(config->key_passwd); + Curl_safefree(config->proxy_key_passwd); + Curl_safefree(config->pubkey); + Curl_safefree(config->hostpubmd5); + Curl_safefree(config->hostpubsha256); + Curl_safefree(config->engine); + Curl_safefree(config->etag_save_file); + Curl_safefree(config->etag_compare_file); + Curl_safefree(config->ssl_ec_curves); + Curl_safefree(config->request_target); + Curl_safefree(config->customrequest); + Curl_safefree(config->krblevel); + Curl_safefree(config->oauth_bearer); + Curl_safefree(config->sasl_authzid); + Curl_safefree(config->unix_socket_path); + Curl_safefree(config->writeout); + Curl_safefree(config->proto_default); + + curl_slist_free_all(config->quote); + curl_slist_free_all(config->postquote); + curl_slist_free_all(config->prequote); + + curl_slist_free_all(config->headers); + curl_slist_free_all(config->proxyheaders); + + curl_mime_free(config->mimepost); + config->mimepost = NULL; + tool_mime_free(config->mimeroot); + config->mimeroot = NULL; + config->mimecurrent = NULL; + + curl_slist_free_all(config->telnet_options); + curl_slist_free_all(config->resolve); + curl_slist_free_all(config->connect_to); + + Curl_safefree(config->preproxy); + Curl_safefree(config->proxy_service_name); + Curl_safefree(config->service_name); + + Curl_safefree(config->ftp_account); + Curl_safefree(config->ftp_alternative_to_user); + + Curl_safefree(config->aws_sigv4); + Curl_safefree(config->proto_str); + Curl_safefree(config->proto_redir_str); +#ifdef USE_ECH + Curl_safefree(config->ech); + config->ech = NULL; + Curl_safefree(config->ech_config); + config->ech_config = NULL; + Curl_safefree(config->ech_public); + config->ech_public = NULL; +#endif +} + +void config_free(struct OperationConfig *config) +{ + struct OperationConfig *last = config; + + /* Free each of the structures in reverse order */ + while(last) { + struct OperationConfig *prev = last->prev; + + free_config_fields(last); + free(last); + + last = prev; + } +} diff --git a/third_party/curl/src/tool_cfgable.h b/third_party/curl/src/tool_cfgable.h new file mode 100644 index 0000000000..a887881da0 --- /dev/null +++ b/third_party/curl/src/tool_cfgable.h @@ -0,0 +1,346 @@ +#ifndef HEADER_CURL_TOOL_CFGABLE_H +#define HEADER_CURL_TOOL_CFGABLE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" +#include "tool_sdecls.h" +#include "tool_urlglob.h" +#include "var.h" + +struct GlobalConfig; + +struct State { + struct getout *urlnode; + struct URLGlob *inglob; + struct URLGlob *urls; + char *outfiles; + char *httpgetfields; + char *uploadfile; + curl_off_t infilenum; /* number of files to upload */ + curl_off_t up; /* upload file counter within a single upload glob */ + curl_off_t urlnum; /* how many iterations this single URL has with ranges + etc */ + curl_off_t li; +}; + +struct OperationConfig { + bool remote_time; + char *useragent; + struct curl_slist *cookies; /* cookies to serialize into a single line */ + char *cookiejar; /* write to this file */ + struct curl_slist *cookiefiles; /* file(s) to load cookies from */ + char *altsvc; /* alt-svc cache filename */ + char *hsts; /* HSTS cache filename */ + bool cookiesession; /* new session? */ + bool encoding; /* Accept-Encoding please */ + bool tr_encoding; /* Transfer-Encoding please */ + unsigned long authtype; /* auth bitmask */ + bool use_resume; + bool resume_from_current; + bool disable_epsv; + bool disable_eprt; + bool ftp_pret; + char *proto_str; + bool proto_present; + char *proto_redir_str; + bool proto_redir_present; + char *proto_default; + curl_off_t resume_from; + char *postfields; + struct curlx_dynbuf postdata; + char *referer; + char *query; + long timeout_ms; + long connecttimeout_ms; + long maxredirs; + curl_off_t max_filesize; + char *output_dir; + char *headerfile; + char *ftpport; + char *iface; + long localport; + long localportrange; + unsigned short porttouse; + char *range; + long low_speed_limit; + long low_speed_time; + long ip_tos; /* IP Type of Service */ + long vlan_priority; /* VLAN priority */ + char *dns_servers; /* dot notation: 1.1.1.1;2.2.2.2 */ + char *dns_interface; /* interface name */ + char *dns_ipv4_addr; /* dot notation */ + char *dns_ipv6_addr; /* dot notation */ + char *userpwd; + char *login_options; + char *tls_username; + char *tls_password; + char *tls_authtype; + char *proxy_tls_username; + char *proxy_tls_password; + char *proxy_tls_authtype; + char *proxyuserpwd; + char *proxy; + int proxyver; /* set to CURLPROXY_HTTP* define */ + char *noproxy; + char *mail_from; + struct curl_slist *mail_rcpt; + char *mail_auth; + bool mail_rcpt_allowfails; /* --mail-rcpt-allowfails */ + char *sasl_authzid; /* Authorization identity (identity to use) */ + bool sasl_ir; /* Enable/disable SASL initial response */ + bool proxytunnel; + bool ftp_append; /* APPE on ftp */ + bool use_ascii; /* select ascii or text transfer */ + bool autoreferer; /* automatically set referer */ + bool failonerror; /* fail on (HTTP) errors */ + bool failwithbody; /* fail on (HTTP) errors but still store body */ + bool show_headers; /* show headers to data output */ + bool no_body; /* do not get the body */ + bool dirlistonly; /* only get the FTP dir list */ + bool followlocation; /* follow http redirects */ + bool unrestricted_auth; /* Continue to send authentication (user+password) + when following redirects, even when hostname + changed */ + bool netrc_opt; + bool netrc; + char *netrc_file; + struct getout *url_list; /* point to the first node */ + struct getout *url_last; /* point to the last/current node */ + struct getout *url_get; /* point to the node to fill in URL */ + struct getout *url_out; /* point to the node to fill in outfile */ + struct getout *url_ul; /* point to the node to fill in upload */ + char *ipfs_gateway; + char *doh_url; + char *cipher_list; + char *proxy_cipher_list; + char *cipher13_list; + char *proxy_cipher13_list; + char *cert; + char *proxy_cert; + char *cert_type; + char *proxy_cert_type; + char *cacert; + char *proxy_cacert; + char *capath; + char *proxy_capath; + char *crlfile; + char *proxy_crlfile; + char *pinnedpubkey; + char *proxy_pinnedpubkey; + char *key; + char *proxy_key; + char *key_type; + char *proxy_key_type; + char *key_passwd; + char *proxy_key_passwd; + char *pubkey; + char *hostpubmd5; + char *hostpubsha256; + char *engine; + char *etag_save_file; + char *etag_compare_file; + bool crlf; + char *customrequest; + char *ssl_ec_curves; + char *krblevel; + char *request_target; + long httpversion; + bool http09_allowed; + bool nobuffer; + bool readbusy; /* set when reading input returns EAGAIN */ + bool globoff; + bool use_httpget; + bool insecure_ok; /* set TRUE to allow insecure SSL connects */ + bool doh_insecure_ok; /* set TRUE to allow insecure SSL connects + for DoH */ + bool proxy_insecure_ok; /* set TRUE to allow insecure SSL connects + for proxy */ + bool terminal_binary_ok; + bool verifystatus; + bool doh_verifystatus; + bool create_dirs; + bool ftp_create_dirs; + bool ftp_skip_ip; + bool proxynegotiate; + bool proxyntlm; + bool proxydigest; + bool proxybasic; + bool proxyanyauth; + bool jsoned; /* added json content-type */ + char *writeout; /* %-styled format string to output */ + struct curl_slist *quote; + struct curl_slist *postquote; + struct curl_slist *prequote; + long ssl_version; + long ssl_version_max; + long proxy_ssl_version; + long ip_version; + long create_file_mode; /* CURLOPT_NEW_FILE_PERMS */ + curl_TimeCond timecond; + curl_off_t condtime; + struct curl_slist *headers; + struct curl_slist *proxyheaders; + struct tool_mime *mimeroot; + struct tool_mime *mimecurrent; + curl_mime *mimepost; + struct curl_slist *telnet_options; + struct curl_slist *resolve; + struct curl_slist *connect_to; + HttpReq httpreq; + + /* for bandwidth limiting features: */ + curl_off_t sendpersecond; /* send to peer */ + curl_off_t recvpersecond; /* receive from peer */ + + bool ftp_ssl; + bool ftp_ssl_reqd; + bool ftp_ssl_control; + bool ftp_ssl_ccc; + int ftp_ssl_ccc_mode; + char *preproxy; + bool socks5_gssapi_nec; /* The NEC reference server does not protect the + encryption type exchange */ + unsigned long socks5_auth;/* auth bitmask for socks5 proxies */ + char *proxy_service_name; /* set authentication service name for HTTP and + SOCKS5 proxies */ + char *service_name; /* set authentication service name for DIGEST-MD5, + Kerberos 5 and SPNEGO */ + + bool tcp_nodelay; + bool tcp_fastopen; + long req_retry; /* number of retries */ + bool retry_all_errors; /* retry on any error */ + bool retry_connrefused; /* set connection refused as a transient error */ + long retry_delay; /* delay between retries (in seconds) */ + long retry_maxtime; /* maximum time to keep retrying */ + + char *ftp_account; /* for ACCT */ + char *ftp_alternative_to_user; /* send command if USER/PASS fails */ + int ftp_filemethod; + long mime_options; /* Mime option flags. */ + long tftp_blksize; /* TFTP BLKSIZE option */ + bool tftp_no_options; /* do not send TFTP options requests */ + bool ignorecl; /* --ignore-content-length */ + bool disable_sessionid; + + bool raw; + bool post301; + bool post302; + bool post303; + bool nokeepalive; /* for keepalive needs */ + long alivetime; /* keepalive-time */ + long alivecnt; /* keepalive-cnt */ + bool content_disposition; /* use Content-disposition filename */ + + int default_node_flags; /* default flags to search for each 'node', which + is basically each given URL to transfer */ + + bool xattr; /* store metadata in extended attributes */ + long gssapi_delegation; + bool ssl_allow_beast; /* allow this SSL vulnerability */ + bool proxy_ssl_allow_beast; /* allow this SSL vulnerability for proxy */ + bool ssl_no_revoke; /* disable SSL certificate revocation checks */ + bool ssl_revoke_best_effort; /* ignore SSL revocation offline/missing + revocation list errors */ + + bool native_ca_store; /* use the native OS CA store */ + bool proxy_native_ca_store; /* use the native OS CA store for proxy */ + bool ssl_auto_client_cert; /* automatically locate and use a client + certificate for authentication (Schannel) */ + bool proxy_ssl_auto_client_cert; /* proxy version of ssl_auto_client_cert */ + char *oauth_bearer; /* OAuth 2.0 bearer token */ + bool noalpn; /* enable/disable TLS ALPN extension */ + char *unix_socket_path; /* path to Unix domain socket */ + bool abstract_unix_socket; /* path to an abstract Unix domain socket */ + bool falsestart; + bool path_as_is; + long expect100timeout_ms; + bool suppress_connect_headers; /* suppress proxy CONNECT response headers + from user callbacks */ + bool synthetic_error; /* if TRUE, this is tool-internal error */ + bool ssh_compression; /* enable/disable SSH compression */ + long happy_eyeballs_timeout_ms; /* happy eyeballs timeout in milliseconds. + 0 is valid. default: CURL_HET_DEFAULT. */ + bool haproxy_protocol; /* whether to send HAProxy protocol v1 */ + char *haproxy_clientip; /* client IP for HAProxy protocol */ + bool disallow_username_in_url; /* disallow usernames in URLs */ + char *aws_sigv4; + enum { + CLOBBER_DEFAULT, /* Provides compatibility with previous versions of curl, + by using the default behavior for -o, -O, and -J. + If those options would have overwritten files, like + -o and -O would, then overwrite them. In the case of + -J, this will not overwrite any files. */ + CLOBBER_NEVER, /* If the file exists, always fail */ + CLOBBER_ALWAYS /* If the file exists, always overwrite it */ + } file_clobber_mode; + bool mptcp; /* enable MPTCP support */ + struct GlobalConfig *global; + struct OperationConfig *prev; + struct OperationConfig *next; /* Always last in the struct */ + struct State state; /* for create_transfer() */ + bool rm_partial; /* on error, remove partially written output + files */ +#ifdef USE_ECH + char *ech; /* Config set by --ech keywords */ + char *ech_config; /* Config set by "--ech esl:" option */ + char *ech_public; /* Config set by "--ech pn:" option */ +#endif + +}; + +struct GlobalConfig { + bool showerror; /* show errors when silent */ + bool silent; /* do not show messages, --silent given */ + bool noprogress; /* do not show progress bar */ + bool isatty; /* Updated internally if output is a tty */ + char *trace_dump; /* file to dump the network trace to */ + FILE *trace_stream; + bool trace_fopened; + trace tracetype; + bool tracetime; /* include timestamp? */ + bool traceids; /* include xfer-/conn-id? */ + int progressmode; /* CURL_PROGRESS_BAR / CURL_PROGRESS_STATS */ + char *libcurl; /* Output libcurl code to this filename */ + bool fail_early; /* exit on first transfer error */ + bool styled_output; /* enable fancy output style detection */ + long ms_per_transfer; /* start next transfer after (at least) this + many milliseconds */ +#ifdef DEBUGBUILD + bool test_event_based; +#endif + bool parallel; + unsigned short parallel_max; /* MAX_PARALLEL is the maximum */ + bool parallel_connect; + char *help_category; /* The help category, if set */ + struct var *variables; + struct OperationConfig *first; + struct OperationConfig *current; + struct OperationConfig *last; /* Always last in the struct */ +}; + +void config_init(struct OperationConfig *config); +void config_free(struct OperationConfig *config); + +#endif /* HEADER_CURL_TOOL_CFGABLE_H */ diff --git a/third_party/curl/src/tool_dirhie.c b/third_party/curl/src/tool_dirhie.c new file mode 100644 index 0000000000..0efdc398c7 --- /dev/null +++ b/third_party/curl/src/tool_dirhie.c @@ -0,0 +1,167 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#include + +#ifdef _WIN32 +# include +#endif + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_dirhie.h" +#include "tool_msgs.h" + +#include "memdebug.h" /* keep this as LAST include */ + +#if defined(_WIN32) || (defined(MSDOS) && !defined(__DJGPP__)) +# define mkdir(x,y) (mkdir)((x)) +# ifndef F_OK +# define F_OK 0 +# endif +#endif + +static void show_dir_errno(struct GlobalConfig *global, const char *name) +{ + switch(errno) { +#ifdef EACCES + case EACCES: + errorf(global, "You do not have permission to create %s", name); + break; +#endif +#ifdef ENAMETOOLONG + case ENAMETOOLONG: + errorf(global, "The directory name %s is too long", name); + break; +#endif +#ifdef EROFS + case EROFS: + errorf(global, "%s resides on a read-only file system", name); + break; +#endif +#ifdef ENOSPC + case ENOSPC: + errorf(global, "No space left on the file system that will " + "contain the directory %s", name); + break; +#endif +#ifdef EDQUOT + case EDQUOT: + errorf(global, "Cannot create directory %s because you " + "exceeded your quota", name); + break; +#endif + default: + errorf(global, "Error creating directory %s", name); + break; + } +} + +/* + * Create the needed directory hierarchy recursively in order to save + * multi-GETs in file output, ie: + * curl "http://example.org/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt" + * should create all the dir* automagically + */ + +#if defined(_WIN32) || defined(__DJGPP__) +/* systems that may use either or when specifying a path */ +#define PATH_DELIMITERS "\\/" +#else +#define PATH_DELIMITERS DIR_CHAR +#endif + + +CURLcode create_dir_hierarchy(const char *outfile, struct GlobalConfig *global) +{ + char *tempdir; + char *tempdir2; + char *outdup; + char *dirbuildup; + CURLcode result = CURLE_OK; + size_t outlen; + + outlen = strlen(outfile); + outdup = strdup(outfile); + if(!outdup) + return CURLE_OUT_OF_MEMORY; + + dirbuildup = malloc(outlen + 1); + if(!dirbuildup) { + Curl_safefree(outdup); + return CURLE_OUT_OF_MEMORY; + } + dirbuildup[0] = '\0'; + + /* Allow strtok() here since this is not used threaded */ + /* !checksrc! disable BANNEDFUNC 2 */ + tempdir = strtok(outdup, PATH_DELIMITERS); + + while(tempdir) { + bool skip = false; + tempdir2 = strtok(NULL, PATH_DELIMITERS); + /* since strtok returns a token for the last word even + if not ending with DIR_CHAR, we need to prune it */ + if(tempdir2) { + size_t dlen = strlen(dirbuildup); + if(dlen) + msnprintf(&dirbuildup[dlen], outlen - dlen, "%s%s", DIR_CHAR, tempdir); + else { + if(outdup == tempdir) { +#if defined(_WIN32) || defined(MSDOS) + /* Skip creating a drive's current directory. + It may seem as though that would harmlessly fail but it could be + a corner case if X: did not exist, since we would be creating it + erroneously. + eg if outfile is X:\foo\bar\filename then do not mkdir X: + This logic takes into account unsupported drives !:, 1:, etc. */ + char *p = strchr(tempdir, ':'); + if(p && !p[1]) + skip = true; +#endif + /* the output string does not start with a separator */ + strcpy(dirbuildup, tempdir); + } + else + msnprintf(dirbuildup, outlen, "%s%s", DIR_CHAR, tempdir); + } + /* Create directory. Ignore access denied error to allow traversal. */ + if(!skip && (-1 == mkdir(dirbuildup, (mode_t)0000750)) && + (errno != EACCES) && (errno != EEXIST)) { + show_dir_errno(global, dirbuildup); + result = CURLE_WRITE_ERROR; + break; /* get out of loop */ + } + } + tempdir = tempdir2; + } + + Curl_safefree(dirbuildup); + Curl_safefree(outdup); + + return result; +} diff --git a/third_party/curl/src/tool_dirhie.h b/third_party/curl/src/tool_dirhie.h new file mode 100644 index 0000000000..0ee407fe54 --- /dev/null +++ b/third_party/curl/src/tool_dirhie.h @@ -0,0 +1,32 @@ +#ifndef HEADER_CURL_TOOL_DIRHIE_H +#define HEADER_CURL_TOOL_DIRHIE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" +#include "tool_cfgable.h" + +CURLcode create_dir_hierarchy(const char *outfile, + struct GlobalConfig *global); + +#endif /* HEADER_CURL_TOOL_DIRHIE_H */ diff --git a/third_party/curl/src/tool_doswin.c b/third_party/curl/src/tool_doswin.c new file mode 100644 index 0000000000..38ef0a01eb --- /dev/null +++ b/third_party/curl/src/tool_doswin.c @@ -0,0 +1,807 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#if defined(_WIN32) || defined(MSDOS) + +#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) +# include +#endif + +#ifdef _WIN32 +# include +# include +# include "tool_cfgable.h" +# include "tool_libinfo.h" +#endif + +#include "tool_bname.h" +#include "tool_doswin.h" + +#include "curlx.h" +#include "memdebug.h" /* keep this as LAST include */ + +#ifdef _WIN32 +# undef PATH_MAX +# define PATH_MAX MAX_PATH +#endif + +#ifndef S_ISCHR +# ifdef S_IFCHR +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +# else +# define S_ISCHR(m) (0) /* cannot tell if file is a device */ +# endif +#endif + +#ifdef _WIN32 +# define _use_lfn(f) (1) /* long filenames always available */ +#elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */ +# define _use_lfn(f) (0) /* long filenames never available */ +#elif defined(__DJGPP__) +# include /* _use_lfn(f) prototype */ +#endif + +#ifndef UNITTESTS +static SANITIZEcode truncate_dryrun(const char *path, + const size_t truncate_pos); +#ifdef MSDOS +static SANITIZEcode msdosify(char **const sanitized, const char *file_name, + int flags); +#endif +static SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized, + const char *file_name, + int flags); +#endif /* !UNITTESTS (static declarations used if no unit tests) */ + + +/* +Sanitize a file or path name. + +All banned characters are replaced by underscores, for example: +f?*foo => f__foo +f:foo::$DATA => f_foo__$DATA +f:\foo:bar => f__foo_bar +f:\foo:bar => f:\foo:bar (flag SANITIZE_ALLOW_PATH) + +This function was implemented according to the guidelines in 'Naming Files, +Paths, and Namespaces' section 'Naming Conventions'. +https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx + +Flags +----- +SANITIZE_ALLOW_COLONS: Allow colons. +Without this flag colons are sanitized. + +SANITIZE_ALLOW_PATH: Allow path separators and colons. +Without this flag path separators and colons are sanitized. + +SANITIZE_ALLOW_RESERVED: Allow reserved device names. +Without this flag a reserved device name is renamed (COM1 => _COM1) unless it +is in a UNC prefixed path. + +SANITIZE_ALLOW_TRUNCATE: Allow truncating a long filename. +Without this flag if the sanitized filename or path will be too long an error +occurs. With this flag the filename --and not any other parts of the path-- may +be truncated to at least a single character. A filename followed by an +alternate data stream (ADS) cannot be truncated in any case. + +Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. +Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. +*/ +SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name, + int flags) +{ + char *p, *target; + size_t len; + SANITIZEcode sc; + size_t max_sanitized_len; + + if(!sanitized) + return SANITIZE_ERR_BAD_ARGUMENT; + + *sanitized = NULL; + + if(!file_name) + return SANITIZE_ERR_BAD_ARGUMENT; + + if((flags & SANITIZE_ALLOW_PATH)) { +#ifndef MSDOS + if(file_name[0] == '\\' && file_name[1] == '\\') + /* UNC prefixed path \\ (eg \\?\C:\foo) */ + max_sanitized_len = 32767-1; + else +#endif + max_sanitized_len = PATH_MAX-1; + } + else + /* The maximum length of a filename. FILENAME_MAX is often the same as + PATH_MAX, in other words it is 260 and does not discount the path + information therefore we should not use it. */ + max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1; + + len = strlen(file_name); + if(len > max_sanitized_len) { + if(!(flags & SANITIZE_ALLOW_TRUNCATE) || + truncate_dryrun(file_name, max_sanitized_len)) + return SANITIZE_ERR_INVALID_PATH; + + len = max_sanitized_len; + } + + target = malloc(len + 1); + if(!target) + return SANITIZE_ERR_OUT_OF_MEMORY; + + strncpy(target, file_name, len); + target[len] = '\0'; + +#ifndef MSDOS + if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4)) + /* Skip the literal path prefix \\?\ */ + p = target + 4; + else +#endif + p = target; + + /* replace control characters and other banned characters */ + for(; *p; ++p) { + const char *banned; + + if((1 <= *p && *p <= 31) || + (!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *p == ':') || + (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) { + *p = '_'; + continue; + } + + for(banned = "|<>\"?*"; *banned; ++banned) { + if(*p == *banned) { + *p = '_'; + break; + } + } + } + + /* remove trailing spaces and periods if not allowing paths */ + if(!(flags & SANITIZE_ALLOW_PATH) && len) { + char *clip = NULL; + + p = &target[len]; + do { + --p; + if(*p != ' ' && *p != '.') + break; + clip = p; + } while(p != target); + + if(clip) { + *clip = '\0'; + len = clip - target; + } + } + +#ifdef MSDOS + sc = msdosify(&p, target, flags); + free(target); + if(sc) + return sc; + target = p; + len = strlen(target); + + if(len > max_sanitized_len) { + free(target); + return SANITIZE_ERR_INVALID_PATH; + } +#endif + + if(!(flags & SANITIZE_ALLOW_RESERVED)) { + sc = rename_if_reserved_dos_device_name(&p, target, flags); + free(target); + if(sc) + return sc; + target = p; + len = strlen(target); + + if(len > max_sanitized_len) { + free(target); + return SANITIZE_ERR_INVALID_PATH; + } + } + + *sanitized = target; + return SANITIZE_ERR_OK; +} + + +/* +Test if truncating a path to a file will leave at least a single character in +the filename. Filenames suffixed by an alternate data stream cannot be +truncated. This performs a dry run, nothing is modified. + +Good truncate_pos 9: C:\foo\bar => C:\foo\ba +Good truncate_pos 6: C:\foo => C:\foo +Good truncate_pos 5: C:\foo => C:\fo +Bad* truncate_pos 5: C:foo => C:foo +Bad truncate_pos 5: C:\foo:ads => C:\fo +Bad truncate_pos 9: C:\foo:ads => C:\foo:ad +Bad truncate_pos 5: C:\foo\bar => C:\fo +Bad truncate_pos 5: C:\foo\ => C:\fo +Bad truncate_pos 7: C:\foo\ => C:\foo\ +Error truncate_pos 7: C:\foo => (pos out of range) +Bad truncate_pos 1: C:\foo\ => C + +* C:foo is ambiguous, C could end up being a drive or file therefore something + like C:superlongfilename cannot be truncated. + +Returns +SANITIZE_ERR_OK: Good -- 'path' can be truncated +SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated +!= SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error +*/ +SANITIZEcode truncate_dryrun(const char *path, const size_t truncate_pos) +{ + size_t len; + + if(!path) + return SANITIZE_ERR_BAD_ARGUMENT; + + len = strlen(path); + + if(truncate_pos > len) + return SANITIZE_ERR_BAD_ARGUMENT; + + if(!len || !truncate_pos) + return SANITIZE_ERR_INVALID_PATH; + + if(strpbrk(&path[truncate_pos - 1], "\\/:")) + return SANITIZE_ERR_INVALID_PATH; + + /* C:\foo can be truncated but C:\foo:ads cannot */ + if(truncate_pos > 1) { + const char *p = &path[truncate_pos - 1]; + do { + --p; + if(*p == ':') + return SANITIZE_ERR_INVALID_PATH; + } while(p != path && *p != '\\' && *p != '/'); + } + + return SANITIZE_ERR_OK; +} + +/* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function + * were taken with modification from the DJGPP port of tar 1.12. They use + * algorithms originally from DJTAR. + */ + +/* +Extra sanitization MSDOS for file_name. + +This is a supporting function for sanitize_file_name. + +Warning: This is an MSDOS legacy function and was purposely written in a way +that some path information may pass through. For example drive letter names +(C:, D:, etc) are allowed to pass through. For sanitizing a filename use +sanitize_file_name. + +Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. +Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. +*/ +#if defined(MSDOS) || defined(UNITTESTS) +SANITIZEcode msdosify(char **const sanitized, const char *file_name, + int flags) +{ + char dos_name[PATH_MAX]; + static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */ + "|<>/\\\":?*"; /* illegal in DOS & W95 */ + static const char *illegal_chars_w95 = &illegal_chars_dos[8]; + int idx, dot_idx; + const char *s = file_name; + char *d = dos_name; + const char *const dlimit = dos_name + sizeof(dos_name) - 1; + const char *illegal_aliens = illegal_chars_dos; + size_t len = sizeof(illegal_chars_dos) - 1; + + if(!sanitized) + return SANITIZE_ERR_BAD_ARGUMENT; + + *sanitized = NULL; + + if(!file_name) + return SANITIZE_ERR_BAD_ARGUMENT; + + if(strlen(file_name) > PATH_MAX-1 && + (!(flags & SANITIZE_ALLOW_TRUNCATE) || + truncate_dryrun(file_name, PATH_MAX-1))) + return SANITIZE_ERR_INVALID_PATH; + + /* Support for Windows 9X VFAT systems, when available. */ + if(_use_lfn(file_name)) { + illegal_aliens = illegal_chars_w95; + len -= (illegal_chars_w95 - illegal_chars_dos); + } + + /* Get past the drive letter, if any. */ + if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') { + *d++ = *s++; + *d = ((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) ? ':' : '_'; + ++d; ++s; + } + + for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) { + if(memchr(illegal_aliens, *s, len)) { + + if((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *s == ':') + *d = ':'; + else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\')) + *d = *s; + /* Dots are special: DOS does not allow them as the leading character, + and a filename cannot have more than a single dot. We leave the + first non-leading dot alone, unless it comes too close to the + beginning of the name: we want sh.lex.c to become sh_lex.c, not + sh.lex-c. */ + else if(*s == '.') { + if((flags & SANITIZE_ALLOW_PATH) && idx == 0 && + (s[1] == '/' || s[1] == '\\' || + (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) { + /* Copy "./" and "../" verbatim. */ + *d++ = *s++; + if(d == dlimit) + break; + if(*s == '.') { + *d++ = *s++; + if(d == dlimit) + break; + } + *d = *s; + } + else if(idx == 0) + *d = '_'; + else if(dot_idx >= 0) { + if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */ + d[dot_idx - idx] = '_'; /* replace previous dot */ + *d = '.'; + } + else + *d = '-'; + } + else + *d = '.'; + + if(*s == '.') + dot_idx = idx; + } + else if(*s == '+' && s[1] == '+') { + if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */ + *d++ = 'x'; + if(d == dlimit) + break; + *d = 'x'; + } + else { + /* libg++ etc. */ + if(dlimit - d < 4) { + *d++ = 'x'; + if(d == dlimit) + break; + *d = 'x'; + } + else { + memcpy(d, "plus", 4); + d += 3; + } + } + s++; + idx++; + } + else + *d = '_'; + } + else + *d = *s; + if(*s == '/' || *s == '\\') { + idx = 0; + dot_idx = -1; + } + else + idx++; + } + *d = '\0'; + + if(*s) { + /* dos_name is truncated, check that truncation requirements are met, + specifically truncating a filename suffixed by an alternate data stream + or truncating the entire filename is not allowed. */ + if(!(flags & SANITIZE_ALLOW_TRUNCATE) || strpbrk(s, "\\/:") || + truncate_dryrun(dos_name, d - dos_name)) + return SANITIZE_ERR_INVALID_PATH; + } + + *sanitized = strdup(dos_name); + return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY); +} +#endif /* MSDOS || UNITTESTS */ + +/* +Rename file_name if it is a reserved dos device name. + +This is a supporting function for sanitize_file_name. + +Warning: This is an MSDOS legacy function and was purposely written in a way +that some path information may pass through. For example drive letter names +(C:, D:, etc) are allowed to pass through. For sanitizing a filename use +sanitize_file_name. + +Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. +Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. +*/ +SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized, + const char *file_name, + int flags) +{ + /* We could have a file whose name is a device on MS-DOS. Trying to + * retrieve such a file would fail at best and wedge us at worst. We need + * to rename such files. */ + char *p, *base; + char fname[PATH_MAX]; +#ifdef MSDOS + struct_stat st_buf; +#endif + + if(!sanitized) + return SANITIZE_ERR_BAD_ARGUMENT; + + *sanitized = NULL; + + if(!file_name) + return SANITIZE_ERR_BAD_ARGUMENT; + + /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */ +#ifndef MSDOS + if((flags & SANITIZE_ALLOW_PATH) && + file_name[0] == '\\' && file_name[1] == '\\') { + size_t len = strlen(file_name); + *sanitized = malloc(len + 1); + if(!*sanitized) + return SANITIZE_ERR_OUT_OF_MEMORY; + strncpy(*sanitized, file_name, len + 1); + return SANITIZE_ERR_OK; + } +#endif + + if(strlen(file_name) > PATH_MAX-1 && + (!(flags & SANITIZE_ALLOW_TRUNCATE) || + truncate_dryrun(file_name, PATH_MAX-1))) + return SANITIZE_ERR_INVALID_PATH; + + strncpy(fname, file_name, PATH_MAX-1); + fname[PATH_MAX-1] = '\0'; + base = basename(fname); + + /* Rename reserved device names that are known to be accessible without \\.\ + Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS + https://support.microsoft.com/en-us/kb/74496 + https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx + */ + for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) { + size_t p_len; + int x = (curl_strnequal(p, "CON", 3) || + curl_strnequal(p, "PRN", 3) || + curl_strnequal(p, "AUX", 3) || + curl_strnequal(p, "NUL", 3)) ? 3 : + (curl_strnequal(p, "CLOCK$", 6)) ? 6 : + (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ? + (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0; + + if(!x) + continue; + + /* the devices may be accessible with an extension or ADS, for + example CON.AIR and 'CON . AIR' and CON:AIR access console */ + + for(; p[x] == ' '; ++x) + ; + + if(p[x] == '.') { + p[x] = '_'; + continue; + } + else if(p[x] == ':') { + if(!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) { + p[x] = '_'; + continue; + } + ++x; + } + else if(p[x]) /* no match */ + continue; + + /* p points to 'CON' or 'CON ' or 'CON:', etc */ + p_len = strlen(p); + + /* Prepend a '_' */ + if(strlen(fname) == PATH_MAX-1) { + --p_len; + if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(p, p_len)) + return SANITIZE_ERR_INVALID_PATH; + p[p_len] = '\0'; + } + memmove(p + 1, p, p_len + 1); + p[0] = '_'; + ++p_len; + + /* if fname was just modified then the basename pointer must be updated */ + if(p == fname) + base = basename(fname); + } + + /* This is the legacy portion from rename_if_dos_device_name that checks for + reserved device names. It only works on MSDOS. On Windows XP the stat + check errors with EINVAL if the device name is reserved. On Windows + Vista/7/8 it sets mode S_IFREG (regular file or device). According to + MSDN stat doc the latter behavior is correct, but that does not help us + identify whether it is a reserved device name and not a regular + filename. */ +#ifdef MSDOS + if(base && ((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) { + /* Prepend a '_' */ + size_t blen = strlen(base); + if(blen) { + if(strlen(fname) == PATH_MAX-1) { + --blen; + if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(base, blen)) + return SANITIZE_ERR_INVALID_PATH; + base[blen] = '\0'; + } + memmove(base + 1, base, blen + 1); + base[0] = '_'; + } + } +#endif + + *sanitized = strdup(fname); + return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY); +} + +#if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__)) + +/* + * Disable program default argument globbing. We do it on our own. + */ +char **__crt0_glob_function(char *arg) +{ + (void)arg; + return (char **)0; +} + +#endif /* MSDOS && (__DJGPP__ || __GO32__) */ + +#ifdef _WIN32 + +/* + * Function to find CACert bundle on a Win32 platform using SearchPath. + * (SearchPath is already declared via inclusions done in setup header file) + * (Use the ASCII version instead of the unicode one!) + * The order of the directories it searches is: + * 1. application's directory + * 2. current working directory + * 3. Windows System directory (e.g. C:\Windows\System32) + * 4. Windows Directory (e.g. C:\Windows) + * 5. all directories along %PATH% + * + * For WinXP and later search order actually depends on registry value: + * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode + */ + +CURLcode FindWin32CACert(struct OperationConfig *config, + curl_sslbackend backend, + const TCHAR *bundle_file) +{ + CURLcode result = CURLE_OK; + +#ifdef CURL_WINDOWS_APP + (void)config; + (void)backend; + (void)bundle_file; +#else + /* Search and set cert file only if libcurl supports SSL. + * + * If Schannel is the selected SSL backend then these locations are + * ignored. We allow setting CA location for schannel only when explicitly + * specified by the user via CURLOPT_CAINFO / --cacert. + */ + if(feature_ssl && backend != CURLSSLBACKEND_SCHANNEL) { + + DWORD res_len; + TCHAR buf[PATH_MAX]; + TCHAR *ptr = NULL; + + buf[0] = TEXT('\0'); + + res_len = SearchPath(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr); + if(res_len > 0) { + char *mstr = curlx_convert_tchar_to_UTF8(buf); + Curl_safefree(config->cacert); + if(mstr) + config->cacert = strdup(mstr); + curlx_unicodefree(mstr); + if(!config->cacert) + result = CURLE_OUT_OF_MEMORY; + } + } +#endif + + return result; +} + + +/* Get a list of all loaded modules with full paths. + * Returns slist on success or NULL on error. + */ +struct curl_slist *GetLoadedModulePaths(void) +{ + HANDLE hnd = INVALID_HANDLE_VALUE; + MODULEENTRY32 mod = {0}; + struct curl_slist *slist = NULL; + + mod.dwSize = sizeof(MODULEENTRY32); + + do { + hnd = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0); + } while(hnd == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH); + + if(hnd == INVALID_HANDLE_VALUE) + goto error; + + if(!Module32First(hnd, &mod)) + goto error; + + do { + char *path; /* points to stack allocated buffer */ + struct curl_slist *temp; + +#ifdef UNICODE + /* sizeof(mod.szExePath) is the max total bytes of wchars. the max total + bytes of multibyte chars will not be more than twice that. */ + char buffer[sizeof(mod.szExePath) * 2]; + if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1, + buffer, sizeof(buffer), NULL, NULL)) + goto error; + path = buffer; +#else + path = mod.szExePath; +#endif + temp = curl_slist_append(slist, path); + if(!temp) + goto error; + slist = temp; + } while(Module32Next(hnd, &mod)); + + goto cleanup; + +error: + curl_slist_free_all(slist); + slist = NULL; +cleanup: + if(hnd != INVALID_HANDLE_VALUE) + CloseHandle(hnd); + return slist; +} + +bool tool_term_has_bold; + +#ifndef CURL_WINDOWS_APP +/* The terminal settings to restore on exit */ +static struct TerminalSettings { + HANDLE hStdOut; + DWORD dwOutputMode; + LONG valid; +} TerminalSettings; + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + +static void restore_terminal(void) +{ + if(InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE)) + SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode); +} + +/* This is the console signal handler. + * The system calls it in a separate thread. + */ +static BOOL WINAPI signal_handler(DWORD type) +{ + if(type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT) + restore_terminal(); + return FALSE; +} + +static void init_terminal(void) +{ + TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + + /* + * Enable VT (Virtual Terminal) output. + * Note: VT mode flag can be set on any version of Windows, but VT + * processing only performed on Win10 >= version 1709 (OS build 16299) + * Creator's Update. Also, ANSI bold on/off supported since then. + */ + if(TerminalSettings.hStdOut == INVALID_HANDLE_VALUE || + !GetConsoleMode(TerminalSettings.hStdOut, + &TerminalSettings.dwOutputMode) || + !curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) + return; + + if((TerminalSettings.dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + tool_term_has_bold = true; + else { + /* The signal handler is set before attempting to change the console mode + because otherwise a signal would not be caught after the change but + before the handler was installed. */ + (void)InterlockedExchange(&TerminalSettings.valid, (LONG)TRUE); + if(SetConsoleCtrlHandler(signal_handler, TRUE)) { + if(SetConsoleMode(TerminalSettings.hStdOut, + (TerminalSettings.dwOutputMode | + ENABLE_VIRTUAL_TERMINAL_PROCESSING))) { + tool_term_has_bold = true; + atexit(restore_terminal); + } + else { + SetConsoleCtrlHandler(signal_handler, FALSE); + (void)InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE); + } + } + } +} +#endif + +LARGE_INTEGER tool_freq; +bool tool_isVistaOrGreater; + +CURLcode win32_init(void) +{ + /* curlx_verify_windows_version must be called during init at least once + because it has its own initialization routine. */ + if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) + tool_isVistaOrGreater = true; + else + tool_isVistaOrGreater = false; + + QueryPerformanceFrequency(&tool_freq); + +#ifndef CURL_WINDOWS_APP + init_terminal(); +#endif + + return CURLE_OK; +} + +#endif /* _WIN32 */ + +#endif /* _WIN32 || MSDOS */ diff --git a/third_party/curl/src/tool_doswin.h b/third_party/curl/src/tool_doswin.h new file mode 100644 index 0000000000..e07d89d959 --- /dev/null +++ b/third_party/curl/src/tool_doswin.h @@ -0,0 +1,72 @@ +#ifndef HEADER_CURL_TOOL_DOSWIN_H +#define HEADER_CURL_TOOL_DOSWIN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#if defined(_WIN32) || defined(MSDOS) + +#define SANITIZE_ALLOW_COLONS (1<<0) /* Allow colons */ +#define SANITIZE_ALLOW_PATH (1<<1) /* Allow path separators and colons */ +#define SANITIZE_ALLOW_RESERVED (1<<2) /* Allow reserved device names */ +#define SANITIZE_ALLOW_TRUNCATE (1<<3) /* Allow truncating a long filename */ + +typedef enum { + SANITIZE_ERR_OK = 0, /* 0 - OK */ + SANITIZE_ERR_INVALID_PATH, /* 1 - the path is invalid */ + SANITIZE_ERR_BAD_ARGUMENT, /* 2 - bad function parameter */ + SANITIZE_ERR_OUT_OF_MEMORY, /* 3 - out of memory */ + SANITIZE_ERR_LAST /* never use! */ +} SANITIZEcode; + +SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name, + int flags); +#ifdef UNITTESTS +SANITIZEcode truncate_dryrun(const char *path, const size_t truncate_pos); +SANITIZEcode msdosify(char **const sanitized, const char *file_name, + int flags); +SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized, + const char *file_name, + int flags); +#endif /* UNITTESTS */ + +#if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__)) + +char **__crt0_glob_function(char *arg); + +#endif /* MSDOS && (__DJGPP__ || __GO32__) */ + +#ifdef _WIN32 + +CURLcode FindWin32CACert(struct OperationConfig *config, + curl_sslbackend backend, + const TCHAR *bundle_file); +struct curl_slist *GetLoadedModulePaths(void); +CURLcode win32_init(void); + +#endif /* _WIN32 */ + +#endif /* _WIN32 || MSDOS */ + +#endif /* HEADER_CURL_TOOL_DOSWIN_H */ diff --git a/third_party/curl/src/tool_easysrc.c b/third_party/curl/src/tool_easysrc.c new file mode 100644 index 0000000000..6ef2be721c --- /dev/null +++ b/third_party/curl/src/tool_easysrc.c @@ -0,0 +1,238 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#include "slist_wc.h" + +#ifndef CURL_DISABLE_LIBCURL_OPTION + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_cfgable.h" +#include "tool_easysrc.h" +#include "tool_msgs.h" + +#include "memdebug.h" /* keep this as LAST include */ + +/* global variable definitions, for easy-interface source code generation */ + +struct slist_wc *easysrc_decl = NULL; /* Variable declarations */ +struct slist_wc *easysrc_data = NULL; /* Build slists, forms etc. */ +struct slist_wc *easysrc_code = NULL; /* Setopt calls */ +struct slist_wc *easysrc_toohard = NULL; /* Unconvertible setopt */ +struct slist_wc *easysrc_clean = NULL; /* Clean up allocated data */ +int easysrc_mime_count = 0; +int easysrc_slist_count = 0; + +static const char *const srchead[]={ + "/********* Sample code generated by the curl command line tool **********", + " * All curl_easy_setopt() options are documented at:", + " * https://curl.se/libcurl/c/curl_easy_setopt.html", + " ************************************************************************/", + "#include ", + "", + "int main(int argc, char *argv[])", + "{", + " CURLcode ret;", + " CURL *hnd;", + NULL +}; +/* easysrc_decl declarations come here */ +/* easysrc_data initialization come here */ +/* easysrc_code statements come here */ +static const char *const srchard[]={ + "/* Here is a list of options the curl code used that cannot get generated", + " as source easily. You may choose to either not use them or implement", + " them yourself.", + "", + NULL +}; +static const char *const srcend[]={ + "", + " return (int)ret;", + "}", + "/**** End of sample code ****/", + NULL +}; + +/* Clean up all source code if we run out of memory */ +static void easysrc_free(void) +{ + slist_wc_free_all(easysrc_decl); + easysrc_decl = NULL; + slist_wc_free_all(easysrc_data); + easysrc_data = NULL; + slist_wc_free_all(easysrc_code); + easysrc_code = NULL; + slist_wc_free_all(easysrc_toohard); + easysrc_toohard = NULL; + slist_wc_free_all(easysrc_clean); + easysrc_clean = NULL; +} + +/* Add a source line to the main code or remarks */ +CURLcode easysrc_add(struct slist_wc **plist, const char *line) +{ + CURLcode ret = CURLE_OK; + struct slist_wc *list = slist_wc_append(*plist, line); + if(!list) { + easysrc_free(); + ret = CURLE_OUT_OF_MEMORY; + } + else + *plist = list; + return ret; +} + +CURLcode easysrc_addf(struct slist_wc **plist, const char *fmt, ...) +{ + CURLcode ret; + char *bufp; + va_list ap; + va_start(ap, fmt); + bufp = curlx_mvaprintf(fmt, ap); + va_end(ap); + if(!bufp) { + ret = CURLE_OUT_OF_MEMORY; + } + else { + ret = easysrc_add(plist, bufp); + curl_free(bufp); + } + return ret; +} + +#define CHKRET(v) do {CURLcode ret = (v); if(ret) return ret;} while(0) + +CURLcode easysrc_init(void) +{ + CHKRET(easysrc_add(&easysrc_code, + "hnd = curl_easy_init();")); + return CURLE_OK; +} + +CURLcode easysrc_perform(void) +{ + /* Note any setopt calls which we could not convert */ + if(easysrc_toohard) { + int i; + struct curl_slist *ptr; + const char *c; + CHKRET(easysrc_add(&easysrc_code, "")); + /* Preamble comment */ + for(i = 0; ((c = srchard[i]) != NULL); i++) + CHKRET(easysrc_add(&easysrc_code, c)); + /* Each unconverted option */ + if(easysrc_toohard) { + for(ptr = easysrc_toohard->first; ptr; ptr = ptr->next) + CHKRET(easysrc_add(&easysrc_code, ptr->data)); + } + CHKRET(easysrc_add(&easysrc_code, "")); + CHKRET(easysrc_add(&easysrc_code, "*/")); + + slist_wc_free_all(easysrc_toohard); + easysrc_toohard = NULL; + } + + CHKRET(easysrc_add(&easysrc_code, "")); + CHKRET(easysrc_add(&easysrc_code, "ret = curl_easy_perform(hnd);")); + CHKRET(easysrc_add(&easysrc_code, "")); + + return CURLE_OK; +} + +CURLcode easysrc_cleanup(void) +{ + CHKRET(easysrc_add(&easysrc_code, "curl_easy_cleanup(hnd);")); + CHKRET(easysrc_add(&easysrc_code, "hnd = NULL;")); + + return CURLE_OK; +} + +void dumpeasysrc(struct GlobalConfig *config) +{ + struct curl_slist *ptr; + char *o = config->libcurl; + + FILE *out; + bool fopened = FALSE; + if(strcmp(o, "-")) { + out = fopen(o, FOPEN_WRITETEXT); + fopened = TRUE; + } + else + out = stdout; + if(!out) + warnf(config, "Failed to open %s to write libcurl code", o); + else { + int i; + const char *c; + + for(i = 0; ((c = srchead[i]) != NULL); i++) + fprintf(out, "%s\n", c); + + /* Declare variables used for complex setopt values */ + if(easysrc_decl) { + for(ptr = easysrc_decl->first; ptr; ptr = ptr->next) + fprintf(out, " %s\n", ptr->data); + } + + /* Set up complex values for setopt calls */ + if(easysrc_data) { + fprintf(out, "\n"); + + for(ptr = easysrc_data->first; ptr; ptr = ptr->next) + fprintf(out, " %s\n", ptr->data); + } + + fprintf(out, "\n"); + if(easysrc_code) { + for(ptr = easysrc_code->first; ptr; ptr = ptr->next) { + if(ptr->data[0]) { + fprintf(out, " %s\n", ptr->data); + } + else { + fprintf(out, "\n"); + } + } + } + + if(easysrc_clean) { + for(ptr = easysrc_clean->first; ptr; ptr = ptr->next) + fprintf(out, " %s\n", ptr->data); + } + + for(i = 0; ((c = srcend[i]) != NULL); i++) + fprintf(out, "%s\n", c); + + if(fopened) + fclose(out); + } + + easysrc_free(); +} + +#endif /* CURL_DISABLE_LIBCURL_OPTION */ diff --git a/third_party/curl/src/tool_easysrc.h b/third_party/curl/src/tool_easysrc.h new file mode 100644 index 0000000000..f698c8f5cc --- /dev/null +++ b/third_party/curl/src/tool_easysrc.h @@ -0,0 +1,58 @@ +#ifndef HEADER_CURL_TOOL_EASYSRC_H +#define HEADER_CURL_TOOL_EASYSRC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" +#ifndef CURL_DISABLE_LIBCURL_OPTION + +/* global variable declarations, for easy-interface source code generation */ + +extern struct slist_wc *easysrc_decl; /* Variable declarations */ +extern struct slist_wc *easysrc_data; /* Build slists, forms etc. */ +extern struct slist_wc *easysrc_code; /* Setopt calls etc. */ +extern struct slist_wc *easysrc_toohard; /* Unconvertible setopt */ +extern struct slist_wc *easysrc_clean; /* Clean up (reverse order) */ + +extern int easysrc_mime_count; /* Number of curl_mime variables */ +extern int easysrc_slist_count; /* Number of curl_slist variables */ + +extern CURLcode easysrc_init(void); +extern CURLcode easysrc_add(struct slist_wc **plist, const char *bupf); +extern CURLcode easysrc_addf(struct slist_wc **plist, + const char *fmt, ...) CURL_PRINTF(2, 3); +extern CURLcode easysrc_perform(void); +extern CURLcode easysrc_cleanup(void); + +void dumpeasysrc(struct GlobalConfig *config); + +#else /* CURL_DISABLE_LIBCURL_OPTION is defined */ + +#define easysrc_init() CURLE_OK +#define easysrc_cleanup() +#define dumpeasysrc(x) +#define easysrc_perform() CURLE_OK + +#endif /* CURL_DISABLE_LIBCURL_OPTION */ + +#endif /* HEADER_CURL_TOOL_EASYSRC_H */ diff --git a/third_party/curl/src/tool_filetime.c b/third_party/curl/src/tool_filetime.c new file mode 100644 index 0000000000..549b8bea59 --- /dev/null +++ b/third_party/curl/src/tool_filetime.c @@ -0,0 +1,156 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_filetime.h" +#include "tool_cfgable.h" +#include "tool_msgs.h" +#include "curlx.h" + +#ifdef HAVE_UTIME_H +# include +#elif defined(HAVE_SYS_UTIME_H) +# include +#endif + +/* Returns 0 on success, non-zero on file problems */ +int getfiletime(const char *filename, struct GlobalConfig *global, + curl_off_t *stamp) +{ + int rc = 1; + +/* Windows stat() may attempt to adjust the unix GMT file time by a daylight + saving time offset and since it is GMT that is bad behavior. When we have + access to a 64-bit type we can bypass stat and get the times directly. */ +#if defined(_WIN32) && !defined(CURL_WINDOWS_APP) + HANDLE hfile; + TCHAR *tchar_filename = curlx_convert_UTF8_to_tchar((char *)filename); + + hfile = CreateFile(tchar_filename, FILE_READ_ATTRIBUTES, + (FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE), + NULL, OPEN_EXISTING, 0, NULL); + curlx_unicodefree(tchar_filename); + if(hfile != INVALID_HANDLE_VALUE) { + FILETIME ft; + if(GetFileTime(hfile, NULL, NULL, &ft)) { + curl_off_t converted = (curl_off_t)ft.dwLowDateTime + | ((curl_off_t)ft.dwHighDateTime) << 32; + + if(converted < CURL_OFF_T_C(116444736000000000)) + warnf(global, "Failed to get filetime: underflow"); + else { + *stamp = (converted - CURL_OFF_T_C(116444736000000000)) / 10000000; + rc = 0; + } + } + else { + warnf(global, "Failed to get filetime: " + "GetFileTime failed: GetLastError %u", + (unsigned int)GetLastError()); + } + CloseHandle(hfile); + } + else if(GetLastError() != ERROR_FILE_NOT_FOUND) { + warnf(global, "Failed to get filetime: " + "CreateFile failed: GetLastError %u", + (unsigned int)GetLastError()); + } +#else + struct_stat statbuf; + if(-1 != stat(filename, &statbuf)) { + *stamp = (curl_off_t)statbuf.st_mtime; + rc = 0; + } + else + warnf(global, "Failed to get filetime: %s", strerror(errno)); +#endif + return rc; +} + +#if defined(HAVE_UTIME) || defined(HAVE_UTIMES) || defined(_WIN32) +void setfiletime(curl_off_t filetime, const char *filename, + struct GlobalConfig *global) +{ + if(filetime >= 0) { +/* Windows utime() may attempt to adjust the unix GMT file time by a daylight + saving time offset and since it is GMT that is bad behavior. When we have + access to a 64-bit type we can bypass utime and set the times directly. */ +#if defined(_WIN32) && !defined(CURL_WINDOWS_APP) + HANDLE hfile; + TCHAR *tchar_filename = curlx_convert_UTF8_to_tchar((char *)filename); + + /* 910670515199 is the maximum unix filetime that can be used as a + Windows FILETIME without overflow: 30827-12-31T23:59:59. */ + if(filetime > CURL_OFF_T_C(910670515199)) { + warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T + " on outfile: overflow", filetime); + curlx_unicodefree(tchar_filename); + return; + } + + hfile = CreateFile(tchar_filename, FILE_WRITE_ATTRIBUTES, + (FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE), + NULL, OPEN_EXISTING, 0, NULL); + curlx_unicodefree(tchar_filename); + if(hfile != INVALID_HANDLE_VALUE) { + curl_off_t converted = ((curl_off_t)filetime * 10000000) + + CURL_OFF_T_C(116444736000000000); + FILETIME ft; + ft.dwLowDateTime = (DWORD)(converted & 0xFFFFFFFF); + ft.dwHighDateTime = (DWORD)(converted >> 32); + if(!SetFileTime(hfile, NULL, &ft, &ft)) { + warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T + " on outfile: SetFileTime failed: GetLastError %u", + filetime, (unsigned int)GetLastError()); + } + CloseHandle(hfile); + } + else { + warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T + " on outfile: CreateFile failed: GetLastError %u", + filetime, (unsigned int)GetLastError()); + } + +#elif defined(HAVE_UTIMES) + struct timeval times[2]; + times[0].tv_sec = times[1].tv_sec = (time_t)filetime; + times[0].tv_usec = times[1].tv_usec = 0; + if(utimes(filename, times)) { + warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T + " on '%s': %s", filetime, filename, strerror(errno)); + } + +#elif defined(HAVE_UTIME) + struct utimbuf times; + times.actime = (time_t)filetime; + times.modtime = (time_t)filetime; + if(utime(filename, ×)) { + warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T + " on '%s': %s", filetime, filename, strerror(errno)); + } +#endif + } +} +#endif /* defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ + defined(_WIN32) */ diff --git a/third_party/curl/src/tool_filetime.h b/third_party/curl/src/tool_filetime.h new file mode 100644 index 0000000000..205e5cee22 --- /dev/null +++ b/third_party/curl/src/tool_filetime.h @@ -0,0 +1,42 @@ +#ifndef HEADER_CURL_TOOL_FILETIME_H +#define HEADER_CURL_TOOL_FILETIME_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +struct GlobalConfig; + +int getfiletime(const char *filename, struct GlobalConfig *global, + curl_off_t *stamp); + +#if defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ + (defined(_WIN32) && (SIZEOF_CURL_OFF_T >= 8)) +void setfiletime(curl_off_t filetime, const char *filename, + struct GlobalConfig *global); +#else +#define setfiletime(a,b,c) Curl_nop_stmt +#endif /* defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ + (defined(_WIN32) && (SIZEOF_CURL_OFF_T >= 8)) */ + +#endif /* HEADER_CURL_TOOL_FILETIME_H */ diff --git a/third_party/curl/src/tool_findfile.c b/third_party/curl/src/tool_findfile.c new file mode 100644 index 0000000000..a1544a5633 --- /dev/null +++ b/third_party/curl/src/tool_findfile.c @@ -0,0 +1,157 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifdef HAVE_PWD_H +# undef __NO_NET_API /* required for building for AmigaOS */ +# include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#include + +#include "tool_findfile.h" + +#include "memdebug.h" /* keep this as LAST include */ + +struct finder { + const char *env; + const char *append; + bool withoutdot; +}; + +/* The order of the variables below is important, as the index number is used + in the findfile() function */ +static const struct finder conf_list[] = { + { "CURL_HOME", NULL, FALSE }, + { "XDG_CONFIG_HOME", NULL, FALSE }, /* index == 1, used in the code */ + { "HOME", NULL, FALSE }, +#ifdef _WIN32 + { "USERPROFILE", NULL, FALSE }, + { "APPDATA", NULL, FALSE }, + { "USERPROFILE", "\\Application Data", FALSE}, +#endif + /* these are for .curlrc if XDG_CONFIG_HOME is not defined */ + { "CURL_HOME", "/.config", TRUE }, + { "HOME", "/.config", TRUE }, + + { NULL, NULL, FALSE } +}; + +static char *checkhome(const char *home, const char *fname, bool dotscore) +{ + const char pref[2] = { '.', '_' }; + int i; + for(i = 0; i < (dotscore ? 2 : 1); i++) { + char *c; + if(dotscore) + c = curl_maprintf("%s" DIR_CHAR "%c%s", home, pref[i], &fname[1]); + else + c = curl_maprintf("%s" DIR_CHAR "%s", home, fname); + if(c) { + int fd = open(c, O_RDONLY); + if(fd >= 0) { + char *path = strdup(c); + close(fd); + curl_free(c); + return path; + } + curl_free(c); + } + } + return NULL; +} + +/* + * findfile() - return the full path name of the file. + * + * If 'dotscore' is TRUE, then check for the file first with a leading dot + * and then with a leading underscore. + * + * 1. Iterate over the environment variables in order, and if set, check for + * the given file to be accessed there, then it is a match. + * 2. Non-windows: try getpwuid + */ +char *findfile(const char *fname, int dotscore) +{ + int i; + bool xdg = FALSE; + DEBUGASSERT(fname && fname[0]); + DEBUGASSERT((dotscore != 1) || (fname[0] == '.')); + + if(!fname[0]) + return NULL; + + for(i = 0; conf_list[i].env; i++) { + char *home = curl_getenv(conf_list[i].env); + if(home) { + char *path; + const char *filename = fname; + if(i == 1 /* XDG_CONFIG_HOME */) + xdg = TRUE; + if(!home[0]) { + curl_free(home); + continue; + } + if(conf_list[i].append) { + char *c = curl_maprintf("%s%s", home, conf_list[i].append); + curl_free(home); + if(!c) + return NULL; + home = c; + } + if(conf_list[i].withoutdot) { + if(!dotscore || xdg) { + /* this is not looking for .curlrc, or the XDG_CONFIG_HOME was + defined so we skip the extended check */ + curl_free(home); + continue; + } + filename++; /* move past the leading dot */ + dotscore = 0; /* disable it for this check */ + } + path = checkhome(home, filename, dotscore ? dotscore - 1 : 0); + curl_free(home); + if(path) + return path; + } + } +#if defined(HAVE_GETPWUID) && defined(HAVE_GETEUID) + { + struct passwd *pw = getpwuid(geteuid()); + if(pw) { + char *home = pw->pw_dir; + if(home && home[0]) + return checkhome(home, fname, FALSE); + } + } +#endif /* PWD-stuff */ + return NULL; +} diff --git a/third_party/curl/src/tool_findfile.h b/third_party/curl/src/tool_findfile.h new file mode 100644 index 0000000000..63d25195fc --- /dev/null +++ b/third_party/curl/src/tool_findfile.h @@ -0,0 +1,36 @@ +#ifndef HEADER_CURL_TOOL_HOMEDIR_H +#define HEADER_CURL_TOOL_HOMEDIR_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifdef _WIN32 +#define CURLRC_DOTSCORE 2 /* look for underscore-prefixed name too */ +#else +#define CURLRC_DOTSCORE 1 /* regular .curlrc check */ +#endif + +char *findfile(const char *fname, int dotscore); + +#endif /* HEADER_CURL_TOOL_HOMEDIR_H */ diff --git a/third_party/curl/src/tool_formparse.c b/third_party/curl/src/tool_formparse.c new file mode 100644 index 0000000000..61f88396cc --- /dev/null +++ b/third_party/curl/src/tool_formparse.c @@ -0,0 +1,907 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#include "strcase.h" + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_cfgable.h" +#include "tool_msgs.h" +#include "tool_binmode.h" +#include "tool_getparam.h" +#include "tool_paramhlp.h" +#include "tool_formparse.h" + +#include "memdebug.h" /* keep this as LAST include */ + +/* tool_mime functions. */ +static struct tool_mime *tool_mime_new(struct tool_mime *parent, + toolmimekind kind) +{ + struct tool_mime *m = (struct tool_mime *) calloc(1, sizeof(*m)); + + if(m) { + m->kind = kind; + m->parent = parent; + if(parent) { + m->prev = parent->subparts; + parent->subparts = m; + } + } + return m; +} + +static struct tool_mime *tool_mime_new_parts(struct tool_mime *parent) +{ + return tool_mime_new(parent, TOOLMIME_PARTS); +} + +static struct tool_mime *tool_mime_new_data(struct tool_mime *parent, + char *mime_data) +{ + char *mime_data_copy; + struct tool_mime *m = NULL; + + mime_data_copy = strdup(mime_data); + if(mime_data_copy) { + m = tool_mime_new(parent, TOOLMIME_DATA); + if(!m) + free(mime_data_copy); + else + m->data = mime_data_copy; + } + return m; +} + +static struct tool_mime *tool_mime_new_filedata(struct tool_mime *parent, + const char *filename, + bool isremotefile, + CURLcode *errcode) +{ + CURLcode result = CURLE_OK; + struct tool_mime *m = NULL; + + *errcode = CURLE_OUT_OF_MEMORY; + if(strcmp(filename, "-")) { + /* This is a normal file. */ + char *filedup = strdup(filename); + if(filedup) { + m = tool_mime_new(parent, TOOLMIME_FILE); + if(!m) + free(filedup); + else { + m->data = filedup; + if(!isremotefile) + m->kind = TOOLMIME_FILEDATA; + *errcode = CURLE_OK; + } + } + } + else { /* Standard input. */ + int fd = fileno(stdin); + char *data = NULL; + curl_off_t size; + curl_off_t origin; + struct_stat sbuf; + + set_binmode(stdin); + origin = ftell(stdin); + /* If stdin is a regular file, do not buffer data but read it + when needed. */ + if(fd >= 0 && origin >= 0 && !fstat(fd, &sbuf) && +#ifdef __VMS + sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC && +#endif + S_ISREG(sbuf.st_mode)) { + size = sbuf.st_size - origin; + if(size < 0) + size = 0; + } + else { /* Not suitable for direct use, buffer stdin data. */ + size_t stdinsize = 0; + + switch(file2memory(&data, &stdinsize, stdin)) { + case PARAM_NO_MEM: + return m; + case PARAM_READ_ERROR: + result = CURLE_READ_ERROR; + break; + default: + if(!stdinsize) { + /* Zero-length data has been freed. Re-create it. */ + data = strdup(""); + if(!data) + return m; + } + break; + } + size = curlx_uztoso(stdinsize); + origin = 0; + } + m = tool_mime_new(parent, TOOLMIME_STDIN); + if(!m) + Curl_safefree(data); + else { + m->data = data; + m->origin = origin; + m->size = size; + m->curpos = 0; + if(!isremotefile) + m->kind = TOOLMIME_STDINDATA; + *errcode = result; + } + } + return m; +} + +void tool_mime_free(struct tool_mime *mime) +{ + if(mime) { + if(mime->subparts) + tool_mime_free(mime->subparts); + if(mime->prev) + tool_mime_free(mime->prev); + Curl_safefree(mime->name); + Curl_safefree(mime->filename); + Curl_safefree(mime->type); + Curl_safefree(mime->encoder); + Curl_safefree(mime->data); + curl_slist_free_all(mime->headers); + free(mime); + } +} + + +/* Mime part callbacks for stdin. */ +size_t tool_mime_stdin_read(char *buffer, + size_t size, size_t nitems, void *arg) +{ + struct tool_mime *sip = (struct tool_mime *) arg; + curl_off_t bytesleft; + (void) size; /* Always 1: ignored. */ + + if(sip->size >= 0) { + if(sip->curpos >= sip->size) + return 0; /* At eof. */ + bytesleft = sip->size - sip->curpos; + if(curlx_uztoso(nitems) > bytesleft) + nitems = curlx_sotouz(bytesleft); + } + if(nitems) { + if(sip->data) { + /* Return data from memory. */ + memcpy(buffer, sip->data + curlx_sotouz(sip->curpos), nitems); + } + else { + /* Read from stdin. */ + nitems = fread(buffer, 1, nitems, stdin); + if(ferror(stdin)) { + /* Show error only once. */ + if(sip->config) { + warnf(sip->config, "stdin: %s", strerror(errno)); + sip->config = NULL; + } + return CURL_READFUNC_ABORT; + } + } + sip->curpos += curlx_uztoso(nitems); + } + return nitems; +} + +int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) +{ + struct tool_mime *sip = (struct tool_mime *) instream; + + switch(whence) { + case SEEK_CUR: + offset += sip->curpos; + break; + case SEEK_END: + offset += sip->size; + break; + } + if(offset < 0) + return CURL_SEEKFUNC_CANTSEEK; + if(!sip->data) { + if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET)) + return CURL_SEEKFUNC_CANTSEEK; + } + sip->curpos = offset; + return CURL_SEEKFUNC_OK; +} + +/* Translate an internal mime tree into a libcurl mime tree. */ + +static CURLcode tool2curlparts(CURL *curl, struct tool_mime *m, + curl_mime *mime) +{ + CURLcode ret = CURLE_OK; + curl_mimepart *part = NULL; + curl_mime *submime = NULL; + const char *filename = NULL; + + if(m) { + ret = tool2curlparts(curl, m->prev, mime); + if(!ret) { + part = curl_mime_addpart(mime); + if(!part) + ret = CURLE_OUT_OF_MEMORY; + } + if(!ret) { + filename = m->filename; + switch(m->kind) { + case TOOLMIME_PARTS: + ret = tool2curlmime(curl, m, &submime); + if(!ret) { + ret = curl_mime_subparts(part, submime); + if(ret) + curl_mime_free(submime); + } + break; + + case TOOLMIME_DATA: + ret = curl_mime_data(part, m->data, CURL_ZERO_TERMINATED); + break; + + case TOOLMIME_FILE: + case TOOLMIME_FILEDATA: + ret = curl_mime_filedata(part, m->data); + if(!ret && m->kind == TOOLMIME_FILEDATA && !filename) + ret = curl_mime_filename(part, NULL); + break; + + case TOOLMIME_STDIN: + if(!filename) + filename = "-"; + FALLTHROUGH(); + case TOOLMIME_STDINDATA: + ret = curl_mime_data_cb(part, m->size, + (curl_read_callback) tool_mime_stdin_read, + (curl_seek_callback) tool_mime_stdin_seek, + NULL, m); + break; + + default: + /* Other cases not possible in this context. */ + break; + } + } + if(!ret && filename) + ret = curl_mime_filename(part, filename); + if(!ret) + ret = curl_mime_type(part, m->type); + if(!ret) + ret = curl_mime_headers(part, m->headers, 0); + if(!ret) + ret = curl_mime_encoder(part, m->encoder); + if(!ret) + ret = curl_mime_name(part, m->name); + } + return ret; +} + +CURLcode tool2curlmime(CURL *curl, struct tool_mime *m, curl_mime **mime) +{ + CURLcode ret = CURLE_OK; + + *mime = curl_mime_init(curl); + if(!*mime) + ret = CURLE_OUT_OF_MEMORY; + else + ret = tool2curlparts(curl, m->subparts, *mime); + if(ret) { + curl_mime_free(*mime); + *mime = NULL; + } + return ret; +} + +/* + * helper function to get a word from form param + * after call get_parm_word, str either point to string end + * or point to any of end chars. + */ +static char *get_param_word(struct OperationConfig *config, char **str, + char **end_pos, char endchar) +{ + char *ptr = *str; + /* the first non-space char is here */ + char *word_begin = ptr; + char *ptr2; + char *escape = NULL; + + if(*ptr == '"') { + ++ptr; + while(*ptr) { + if(*ptr == '\\') { + if(ptr[1] == '\\' || ptr[1] == '"') { + /* remember the first escape position */ + if(!escape) + escape = ptr; + /* skip escape of back-slash or double-quote */ + ptr += 2; + continue; + } + } + if(*ptr == '"') { + bool trailing_data = FALSE; + *end_pos = ptr; + if(escape) { + /* has escape, we restore the unescaped string here */ + ptr = ptr2 = escape; + do { + if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"')) + ++ptr; + *ptr2++ = *ptr++; + } + while(ptr < *end_pos); + *end_pos = ptr2; + } + ++ptr; + while(*ptr && *ptr != ';' && *ptr != endchar) { + if(!ISSPACE(*ptr)) + trailing_data = TRUE; + ++ptr; + } + if(trailing_data) + warnf(config->global, "Trailing data after quoted form parameter"); + *str = ptr; + return word_begin + 1; + } + ++ptr; + } + /* end quote is missing, treat it as non-quoted. */ + ptr = word_begin; + } + + while(*ptr && *ptr != ';' && *ptr != endchar) + ++ptr; + *str = *end_pos = ptr; + return word_begin; +} + +/* Append slist item and return -1 if failed. */ +static int slist_append(struct curl_slist **plist, const char *data) +{ + struct curl_slist *s = curl_slist_append(*plist, data); + + if(!s) + return -1; + + *plist = s; + return 0; +} + +/* Read headers from a file and append to list. */ +static int read_field_headers(struct OperationConfig *config, + const char *filename, FILE *fp, + struct curl_slist **pheaders) +{ + size_t hdrlen = 0; + size_t pos = 0; + bool incomment = FALSE; + int lineno = 1; + char hdrbuf[999] = ""; /* Max. header length + 1. */ + + for(;;) { + int c = getc(fp); + if(c == EOF || (!pos && !ISSPACE(c))) { + /* Strip and flush the current header. */ + while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1])) + hdrlen--; + if(hdrlen) { + hdrbuf[hdrlen] = '\0'; + if(slist_append(pheaders, hdrbuf)) { + errorf(config->global, "Out of memory for field headers"); + return -1; + } + hdrlen = 0; + } + } + + switch(c) { + case EOF: + if(ferror(fp)) { + errorf(config->global, "Header file %s read error: %s", filename, + strerror(errno)); + return -1; + } + return 0; /* Done. */ + case '\r': + continue; /* Ignore. */ + case '\n': + pos = 0; + incomment = FALSE; + lineno++; + continue; + case '#': + if(!pos) + incomment = TRUE; + break; + } + + pos++; + if(!incomment) { + if(hdrlen == sizeof(hdrbuf) - 1) { + warnf(config->global, "File %s line %d: header too long (truncated)", + filename, lineno); + c = ' '; + } + if(hdrlen <= sizeof(hdrbuf) - 1) + hdrbuf[hdrlen++] = (char) c; + } + } + /* NOTREACHED */ +} + +static int get_param_part(struct OperationConfig *config, char endchar, + char **str, char **pdata, char **ptype, + char **pfilename, char **pencoder, + struct curl_slist **pheaders) +{ + char *p = *str; + char *type = NULL; + char *filename = NULL; + char *encoder = NULL; + char *endpos; + char *tp; + char sep; + char type_major[128] = ""; + char type_minor[128] = ""; + char *endct = NULL; + struct curl_slist *headers = NULL; + + if(ptype) + *ptype = NULL; + if(pfilename) + *pfilename = NULL; + if(pheaders) + *pheaders = NULL; + if(pencoder) + *pencoder = NULL; + while(ISSPACE(*p)) + p++; + tp = p; + *pdata = get_param_word(config, &p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(*pdata == tp) + while(endpos > *pdata && ISSPACE(endpos[-1])) + endpos--; + sep = *p; + *endpos = '\0'; + while(sep == ';') { + while(p++ && ISSPACE(*p)) + ; + + if(!endct && checkprefix("type=", p)) { + for(p += 5; ISSPACE(*p); p++) + ; + /* set type pointer */ + type = p; + + /* verify that this is a fine type specifier */ + if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) { + warnf(config->global, "Illegally formatted content-type field"); + curl_slist_free_all(headers); + return -1; /* illegal content-type syntax! */ + } + + /* now point beyond the content-type specifier */ + p = type + strlen(type_major) + strlen(type_minor) + 1; + for(endct = p; *p && *p != ';' && *p != endchar; p++) + if(!ISSPACE(*p)) + endct = p + 1; + sep = *p; + } + else if(checkprefix("filename=", p)) { + if(endct) { + *endct = '\0'; + endct = NULL; + } + for(p += 9; ISSPACE(*p); p++) + ; + tp = p; + filename = get_param_word(config, &p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(filename == tp) + while(endpos > filename && ISSPACE(endpos[-1])) + endpos--; + sep = *p; + *endpos = '\0'; + } + else if(checkprefix("headers=", p)) { + if(endct) { + *endct = '\0'; + endct = NULL; + } + p += 8; + if(*p == '@' || *p == '<') { + char *hdrfile; + FILE *fp; + /* Read headers from a file. */ + + do { + p++; + } while(ISSPACE(*p)); + tp = p; + hdrfile = get_param_word(config, &p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(hdrfile == tp) + while(endpos > hdrfile && ISSPACE(endpos[-1])) + endpos--; + sep = *p; + *endpos = '\0'; + fp = fopen(hdrfile, FOPEN_READTEXT); + if(!fp) + warnf(config->global, "Cannot read from %s: %s", hdrfile, + strerror(errno)); + else { + int i = read_field_headers(config, hdrfile, fp, &headers); + + fclose(fp); + if(i) { + curl_slist_free_all(headers); + return -1; + } + } + } + else { + char *hdr; + + while(ISSPACE(*p)) + p++; + tp = p; + hdr = get_param_word(config, &p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(hdr == tp) + while(endpos > hdr && ISSPACE(endpos[-1])) + endpos--; + sep = *p; + *endpos = '\0'; + if(slist_append(&headers, hdr)) { + errorf(config->global, "Out of memory for field header"); + curl_slist_free_all(headers); + return -1; + } + } + } + else if(checkprefix("encoder=", p)) { + if(endct) { + *endct = '\0'; + endct = NULL; + } + for(p += 8; ISSPACE(*p); p++) + ; + tp = p; + encoder = get_param_word(config, &p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(encoder == tp) + while(endpos > encoder && ISSPACE(endpos[-1])) + endpos--; + sep = *p; + *endpos = '\0'; + } + else if(endct) { + /* This is part of content type. */ + for(endct = p; *p && *p != ';' && *p != endchar; p++) + if(!ISSPACE(*p)) + endct = p + 1; + sep = *p; + } + else { + /* unknown prefix, skip to next block */ + char *unknown = get_param_word(config, &p, &endpos, endchar); + + sep = *p; + *endpos = '\0'; + if(*unknown) + warnf(config->global, "skip unknown form field: %s", unknown); + } + } + + /* Terminate content type. */ + if(endct) + *endct = '\0'; + + if(ptype) + *ptype = type; + else if(type) + warnf(config->global, "Field content type not allowed here: %s", type); + + if(pfilename) + *pfilename = filename; + else if(filename) + warnf(config->global, + "Field filename not allowed here: %s", filename); + + if(pencoder) + *pencoder = encoder; + else if(encoder) + warnf(config->global, + "Field encoder not allowed here: %s", encoder); + + if(pheaders) + *pheaders = headers; + else if(headers) { + warnf(config->global, + "Field headers not allowed here: %s", headers->data); + curl_slist_free_all(headers); + } + + *str = p; + return sep & 0xFF; +} + + +/*************************************************************************** + * + * formparse() + * + * Reads a 'name=value' parameter and builds the appropriate linked list. + * + * If the value is of the form ''. + * + * If literal_value is set, any initial '@' or '<' in the value string + * loses its special meaning, as does any embedded ';type='. + * + * You may specify more than one file for a single name (field). Specify + * multiple files by writing it like: + * + * 'name=@filename,filename2,filename3' + * + * or use double-quotes quote the filename: + * + * 'name=@"filename","filename2","filename3"' + * + * If you want content-types specified for each too, write them like: + * + * 'name=@filename;type=image/gif,filename2,filename3' + * + * If you want custom headers added for a single part, write them in a separate + * file and do like this: + * + * 'name=foo;headers=@headerfile' or why not + * 'name=@filemame;headers=@headerfile' + * + * To upload a file, but to fake the filename that will be included in the + * formpost, do like this: + * + * 'name=@filename;filename=/dev/null' or quote the faked filename like: + * 'name=@filename;filename="play, play, and play.txt"' + * + * If filename/path contains ',' or ';', it must be quoted by double-quotes, + * else curl will fail to figure out the correct filename. if the filename + * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash. + * + ***************************************************************************/ + +#define SET_TOOL_MIME_PTR(m, field) \ + do { \ + if(field) { \ + (m)->field = strdup(field); \ + if(!(m)->field) \ + goto fail; \ + } \ + } while(0) + +int formparse(struct OperationConfig *config, + const char *input, + struct tool_mime **mimeroot, + struct tool_mime **mimecurrent, + bool literal_value) +{ + /* input MUST be a string in the format 'name=contents' and we will + build a linked list with the info */ + char *name = NULL; + char *contents = NULL; + char *contp; + char *data; + char *type = NULL; + char *filename = NULL; + char *encoder = NULL; + struct curl_slist *headers = NULL; + struct tool_mime *part = NULL; + CURLcode res; + int err = 1; + + /* Allocate the main mime structure if needed. */ + if(!*mimecurrent) { + *mimeroot = tool_mime_new_parts(NULL); + if(!*mimeroot) + goto fail; + *mimecurrent = *mimeroot; + } + + /* Make a copy we can overwrite. */ + contents = strdup(input); + if(!contents) + goto fail; + + /* Scan for the end of the name. */ + contp = strchr(contents, '='); + if(contp) { + int sep = '\0'; + if(contp > contents) + name = contents; + *contp++ = '\0'; + + if(*contp == '(' && !literal_value) { + /* Starting a multipart. */ + sep = get_param_part(config, '\0', + &contp, &data, &type, NULL, NULL, &headers); + if(sep < 0) + goto fail; + part = tool_mime_new_parts(*mimecurrent); + if(!part) + goto fail; + *mimecurrent = part; + part->headers = headers; + headers = NULL; + SET_TOOL_MIME_PTR(part, type); + } + else if(!name && !strcmp(contp, ")") && !literal_value) { + /* Ending a multipart. */ + if(*mimecurrent == *mimeroot) { + warnf(config->global, "no multipart to terminate"); + goto fail; + } + *mimecurrent = (*mimecurrent)->parent; + } + else if('@' == contp[0] && !literal_value) { + + /* we use the @-letter to indicate filename(s) */ + + struct tool_mime *subparts = NULL; + + do { + /* since this was a file, it may have a content-type specifier + at the end too, or a filename. Or both. */ + ++contp; + sep = get_param_part(config, ',', &contp, + &data, &type, &filename, &encoder, &headers); + if(sep < 0) { + goto fail; + } + + /* now contp point to comma or string end. + If more files to come, make sure we have multiparts. */ + if(!subparts) { + if(sep != ',') /* If there is a single file. */ + subparts = *mimecurrent; + else { + subparts = tool_mime_new_parts(*mimecurrent); + if(!subparts) + goto fail; + } + } + + /* Store that file in a part. */ + part = tool_mime_new_filedata(subparts, data, TRUE, &res); + if(!part) + goto fail; + part->headers = headers; + headers = NULL; + part->config = config->global; + if(res == CURLE_READ_ERROR) { + /* An error occurred while reading stdin: if read has started, + issue the error now. Else, delay it until processed by + libcurl. */ + if(part->size > 0) { + warnf(config->global, + "error while reading standard input"); + goto fail; + } + Curl_safefree(part->data); + part->data = NULL; + part->size = -1; + res = CURLE_OK; + } + SET_TOOL_MIME_PTR(part, filename); + SET_TOOL_MIME_PTR(part, type); + SET_TOOL_MIME_PTR(part, encoder); + + /* *contp could be '\0', so we just check with the delimiter */ + } while(sep); /* loop if there is another filename */ + part = (*mimecurrent)->subparts; /* Set name on group. */ + } + else { + if(*contp == '<' && !literal_value) { + ++contp; + sep = get_param_part(config, '\0', &contp, + &data, &type, NULL, &encoder, &headers); + if(sep < 0) + goto fail; + + part = tool_mime_new_filedata(*mimecurrent, data, FALSE, + &res); + if(!part) + goto fail; + part->headers = headers; + headers = NULL; + part->config = config->global; + if(res == CURLE_READ_ERROR) { + /* An error occurred while reading stdin: if read has started, + issue the error now. Else, delay it until processed by + libcurl. */ + if(part->size > 0) { + warnf(config->global, + "error while reading standard input"); + goto fail; + } + Curl_safefree(part->data); + part->data = NULL; + part->size = -1; + res = CURLE_OK; + } + } + else { + if(literal_value) + data = contp; + else { + sep = get_param_part(config, '\0', &contp, + &data, &type, &filename, &encoder, &headers); + if(sep < 0) + goto fail; + } + + part = tool_mime_new_data(*mimecurrent, data); + if(!part) + goto fail; + part->headers = headers; + headers = NULL; + } + + SET_TOOL_MIME_PTR(part, filename); + SET_TOOL_MIME_PTR(part, type); + SET_TOOL_MIME_PTR(part, encoder); + + if(sep) { + *contp = (char) sep; + warnf(config->global, + "garbage at end of field specification: %s", contp); + } + } + + /* Set part name. */ + SET_TOOL_MIME_PTR(part, name); + } + else { + warnf(config->global, "Illegally formatted input field"); + goto fail; + } + err = 0; +fail: + Curl_safefree(contents); + curl_slist_free_all(headers); + return err; +} diff --git a/third_party/curl/src/tool_formparse.h b/third_party/curl/src/tool_formparse.h new file mode 100644 index 0000000000..35d5c952d5 --- /dev/null +++ b/third_party/curl/src/tool_formparse.h @@ -0,0 +1,73 @@ +#ifndef HEADER_CURL_TOOL_FORMPARSE_H +#define HEADER_CURL_TOOL_FORMPARSE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +/* Private structure for mime/parts. */ + +typedef enum { + TOOLMIME_NONE = 0, + TOOLMIME_PARTS, + TOOLMIME_DATA, + TOOLMIME_FILE, + TOOLMIME_FILEDATA, + TOOLMIME_STDIN, + TOOLMIME_STDINDATA +} toolmimekind; + +struct tool_mime { + /* Structural fields. */ + toolmimekind kind; /* Part kind. */ + struct tool_mime *parent; /* Parent item. */ + struct tool_mime *prev; /* Previous sibling (reverse order link). */ + /* Common fields. */ + char *data; /* Actual data or data filename. */ + char *name; /* Part name. */ + char *filename; /* Part's filename. */ + char *type; /* Part's mime type. */ + char *encoder; /* Part's requested encoding. */ + struct curl_slist *headers; /* User-defined headers. */ + /* TOOLMIME_PARTS fields. */ + struct tool_mime *subparts; /* Part's subparts. */ + /* TOOLMIME_STDIN/TOOLMIME_STDINDATA fields. */ + curl_off_t origin; /* Stdin read origin offset. */ + curl_off_t size; /* Stdin data size. */ + curl_off_t curpos; /* Stdin current read position. */ + struct GlobalConfig *config; /* For access from callback. */ +}; + +size_t tool_mime_stdin_read(char *buffer, + size_t size, size_t nitems, void *arg); +int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence); + +int formparse(struct OperationConfig *config, + const char *input, + struct tool_mime **mimeroot, + struct tool_mime **mimecurrent, + bool literal_value); +CURLcode tool2curlmime(CURL *curl, struct tool_mime *m, curl_mime **mime); +void tool_mime_free(struct tool_mime *mime); + +#endif /* HEADER_CURL_TOOL_FORMPARSE_H */ diff --git a/third_party/curl/src/tool_getparam.c b/third_party/curl/src/tool_getparam.c new file mode 100644 index 0000000000..4c9392cc64 --- /dev/null +++ b/third_party/curl/src/tool_getparam.c @@ -0,0 +1,2988 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#include "strcase.h" + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_binmode.h" +#include "tool_cfgable.h" +#include "tool_cb_prg.h" +#include "tool_filetime.h" +#include "tool_formparse.h" +#include "tool_getparam.h" +#include "tool_helpers.h" +#include "tool_libinfo.h" +#include "tool_msgs.h" +#include "tool_paramhlp.h" +#include "tool_parsecfg.h" +#include "tool_main.h" +#include "dynbuf.h" +#include "tool_stderr.h" +#include "var.h" + +#include "memdebug.h" /* keep this as LAST include */ + +#ifdef MSDOS +# define USE_WATT32 +#endif + +#define ALLOW_BLANK TRUE +#define DENY_BLANK FALSE + +static ParameterError getstr(char **str, const char *val, bool allowblank) +{ + if(*str) { + free(*str); + *str = NULL; + } + if(val) { + if(!allowblank && !val[0]) + return PARAM_BLANK_STRING; + + *str = strdup(val); + if(!*str) + return PARAM_NO_MEM; + } + return PARAM_OK; +} + +/* one enum for every command line option. The name is the verbatim long + option name, but in uppercase with periods and minuses replaced with + underscores using a "C_" prefix. */ +typedef enum { + C_ABSTRACT_UNIX_SOCKET, + C_ALPN, + C_ALT_SVC, + C_ANYAUTH, + C_APPEND, + C_AWS_SIGV4, + C_BASIC, + C_BUFFER, + C_CA_NATIVE, + C_CACERT, + C_CAPATH, + C_CERT, + C_CERT_STATUS, + C_CERT_TYPE, + C_CIPHERS, + C_CLOBBER, + C_COMPRESSED, + C_COMPRESSED_SSH, + C_CONFIG, + C_CONNECT_TIMEOUT, + C_CONNECT_TO, + C_CONTINUE_AT, + C_COOKIE, + C_COOKIE_JAR, + C_CREATE_DIRS, + C_CREATE_FILE_MODE, + C_CRLF, + C_CRLFILE, + C_CURVES, + C_DATA, + C_DATA_ASCII, + C_DATA_BINARY, + C_DATA_RAW, + C_DATA_URLENCODE, + C_DELEGATION, + C_DIGEST, + C_DISABLE, + C_DISABLE_EPRT, + C_DISABLE_EPSV, + C_DISALLOW_USERNAME_IN_URL, + C_DNS_INTERFACE, + C_DNS_IPV4_ADDR, + C_DNS_IPV6_ADDR, + C_DNS_SERVERS, + C_DOH_CERT_STATUS, + C_DOH_INSECURE, + C_DOH_URL, + C_DUMP_HEADER, + C_ECH, + C_EGD_FILE, + C_ENGINE, + C_EPRT, + C_EPSV, + C_ETAG_COMPARE, + C_ETAG_SAVE, + C_EXPECT100_TIMEOUT, + C_FAIL, + C_FAIL_EARLY, + C_FAIL_WITH_BODY, + C_FALSE_START, + C_FORM, + C_FORM_ESCAPE, + C_FORM_STRING, + C_FTP_ACCOUNT, + C_FTP_ALTERNATIVE_TO_USER, + C_FTP_CREATE_DIRS, + C_FTP_METHOD, + C_FTP_PASV, + C_FTP_PORT, + C_FTP_PRET, + C_FTP_SKIP_PASV_IP, + C_FTP_SSL, + C_FTP_SSL_CCC, + C_FTP_SSL_CCC_MODE, + C_FTP_SSL_CONTROL, + C_FTP_SSL_REQD, + C_GET, + C_GLOBOFF, + C_HAPPY_EYEBALLS_TIMEOUT_MS, + C_HAPROXY_CLIENTIP, + C_HAPROXY_PROTOCOL, + C_HEAD, + C_HEADER, + C_HELP, + C_HOSTPUBMD5, + C_HOSTPUBSHA256, + C_HSTS, + C_HTTP0_9, + C_HTTP1_0, + C_HTTP1_1, + C_HTTP2, + C_HTTP2_PRIOR_KNOWLEDGE, + C_HTTP3, + C_HTTP3_ONLY, + C_IGNORE_CONTENT_LENGTH, + C_INCLUDE, + C_INSECURE, + C_INTERFACE, + C_IPFS_GATEWAY, + C_IPV4, + C_IPV6, + C_JSON, + C_JUNK_SESSION_COOKIES, + C_KEEPALIVE, + C_KEEPALIVE_CNT, + C_KEEPALIVE_TIME, + C_KEY, + C_KEY_TYPE, + C_KRB, + C_KRB4, + C_LIBCURL, + C_LIMIT_RATE, + C_LIST_ONLY, + C_LOCAL_PORT, + C_LOCATION, + C_LOCATION_TRUSTED, + C_LOGIN_OPTIONS, + C_MAIL_AUTH, + C_MAIL_FROM, + C_MAIL_RCPT, + C_MAIL_RCPT_ALLOWFAILS, + C_MANUAL, + C_MAX_FILESIZE, + C_MAX_REDIRS, + C_MAX_TIME, + C_METALINK, + C_MPTCP, + C_NEGOTIATE, + C_NETRC, + C_NETRC_FILE, + C_NETRC_OPTIONAL, + C_NEXT, + C_NOPROXY, + C_NPN, + C_NTLM, + C_NTLM_WB, + C_OAUTH2_BEARER, + C_OUTPUT, + C_OUTPUT_DIR, + C_PARALLEL, + C_PARALLEL_IMMEDIATE, + C_PARALLEL_MAX, + C_PASS, + C_PATH_AS_IS, + C_PINNEDPUBKEY, + C_POST301, + C_POST302, + C_POST303, + C_PREPROXY, + C_PROGRESS_BAR, + C_PROGRESS_METER, + C_PROTO, + C_PROTO_DEFAULT, + C_PROTO_REDIR, + C_PROXY, + C_PROXY_ANYAUTH, + C_PROXY_BASIC, + C_PROXY_CA_NATIVE, + C_PROXY_CACERT, + C_PROXY_CAPATH, + C_PROXY_CERT, + C_PROXY_CERT_TYPE, + C_PROXY_CIPHERS, + C_PROXY_CRLFILE, + C_PROXY_DIGEST, + C_PROXY_HEADER, + C_PROXY_HTTP2, + C_PROXY_INSECURE, + C_PROXY_KEY, + C_PROXY_KEY_TYPE, + C_PROXY_NEGOTIATE, + C_PROXY_NTLM, + C_PROXY_PASS, + C_PROXY_PINNEDPUBKEY, + C_PROXY_SERVICE_NAME, + C_PROXY_SSL_ALLOW_BEAST, + C_PROXY_SSL_AUTO_CLIENT_CERT, + C_PROXY_TLS13_CIPHERS, + C_PROXY_TLSAUTHTYPE, + C_PROXY_TLSPASSWORD, + C_PROXY_TLSUSER, + C_PROXY_TLSV1, + C_PROXY_USER, + C_PROXY1_0, + C_PROXYTUNNEL, + C_PUBKEY, + C_QUOTE, + C_RANDOM_FILE, + C_RANGE, + C_RATE, + C_RAW, + C_REFERER, + C_REMOTE_HEADER_NAME, + C_REMOTE_NAME, + C_REMOTE_NAME_ALL, + C_REMOTE_TIME, + C_REMOVE_ON_ERROR, + C_REQUEST, + C_REQUEST_TARGET, + C_RESOLVE, + C_RETRY, + C_RETRY_ALL_ERRORS, + C_RETRY_CONNREFUSED, + C_RETRY_DELAY, + C_RETRY_MAX_TIME, + C_SASL_AUTHZID, + C_SASL_IR, + C_SERVICE_NAME, + C_SESSIONID, + C_SHOW_ERROR, + C_SILENT, + C_SOCKS4, + C_SOCKS4A, + C_SOCKS5, + C_SOCKS5_BASIC, + C_SOCKS5_GSSAPI, + C_SOCKS5_GSSAPI_NEC, + C_SOCKS5_GSSAPI_SERVICE, + C_SOCKS5_HOSTNAME, + C_SPEED_LIMIT, + C_SPEED_TIME, + C_SSL, + C_SSL_ALLOW_BEAST, + C_SSL_AUTO_CLIENT_CERT, + C_SSL_NO_REVOKE, + C_SSL_REQD, + C_SSL_REVOKE_BEST_EFFORT, + C_SSLV2, + C_SSLV3, + C_STDERR, + C_STYLED_OUTPUT, + C_SUPPRESS_CONNECT_HEADERS, + C_TCP_FASTOPEN, + C_TCP_NODELAY, + C_TELNET_OPTION, + C_TEST_EVENT, + C_TFTP_BLKSIZE, + C_TFTP_NO_OPTIONS, + C_TIME_COND, + C_TLS_MAX, + C_TLS13_CIPHERS, + C_TLSAUTHTYPE, + C_TLSPASSWORD, + C_TLSUSER, + C_TLSV1, + C_TLSV1_0, + C_TLSV1_1, + C_TLSV1_2, + C_TLSV1_3, + C_TR_ENCODING, + C_TRACE, + C_TRACE_ASCII, + C_TRACE_CONFIG, + C_TRACE_IDS, + C_TRACE_TIME, + C_IP_TOS, + C_UNIX_SOCKET, + C_UPLOAD_FILE, + C_URL, + C_URL_QUERY, + C_USE_ASCII, + C_USER, + C_USER_AGENT, + C_VARIABLE, + C_VERBOSE, + C_VERSION, + C_VLAN_PRIORITY, + C_WDEBUG, + C_WRITE_OUT, + C_XATTR +} cmdline_t; + +struct LongShort { + const char *lname; /* long name option */ + enum { + ARG_NONE, /* stand-alone but not a boolean */ + ARG_BOOL, /* accepts a --no-[name] prefix */ + ARG_STRG, /* requires an argument */ + ARG_FILE /* requires an argument, usually a filename */ + } desc; + char letter; /* short name option or ' ' */ + cmdline_t cmd; +}; + +/* this array MUST be alphasorted based on the 'lname' */ +static const struct LongShort aliases[]= { + {"abstract-unix-socket", ARG_FILE, ' ', C_ABSTRACT_UNIX_SOCKET}, + {"alpn", ARG_BOOL, ' ', C_ALPN}, + {"alt-svc", ARG_STRG, ' ', C_ALT_SVC}, + {"anyauth", ARG_BOOL, ' ', C_ANYAUTH}, + {"append", ARG_BOOL, 'a', C_APPEND}, + {"aws-sigv4", ARG_STRG, ' ', C_AWS_SIGV4}, + {"basic", ARG_BOOL, ' ', C_BASIC}, + {"buffer", ARG_BOOL, 'N', C_BUFFER}, + {"ca-native", ARG_BOOL, ' ', C_CA_NATIVE}, + {"cacert", ARG_FILE, ' ', C_CACERT}, + {"capath", ARG_FILE, ' ', C_CAPATH}, + {"cert", ARG_FILE, 'E', C_CERT}, + {"cert-status", ARG_BOOL, ' ', C_CERT_STATUS}, + {"cert-type", ARG_STRG, ' ', C_CERT_TYPE}, + {"ciphers", ARG_STRG, ' ', C_CIPHERS}, + {"clobber", ARG_BOOL, ' ', C_CLOBBER}, + {"compressed", ARG_BOOL, ' ', C_COMPRESSED}, + {"compressed-ssh", ARG_BOOL, ' ', C_COMPRESSED_SSH}, + {"config", ARG_FILE, 'K', C_CONFIG}, + {"connect-timeout", ARG_STRG, ' ', C_CONNECT_TIMEOUT}, + {"connect-to", ARG_STRG, ' ', C_CONNECT_TO}, + {"continue-at", ARG_STRG, 'C', C_CONTINUE_AT}, + {"cookie", ARG_STRG, 'b', C_COOKIE}, + {"cookie-jar", ARG_STRG, 'c', C_COOKIE_JAR}, + {"create-dirs", ARG_BOOL, ' ', C_CREATE_DIRS}, + {"create-file-mode", ARG_STRG, ' ', C_CREATE_FILE_MODE}, + {"crlf", ARG_BOOL, ' ', C_CRLF}, + {"crlfile", ARG_FILE, ' ', C_CRLFILE}, + {"curves", ARG_STRG, ' ', C_CURVES}, + {"data", ARG_STRG, 'd', C_DATA}, + {"data-ascii", ARG_STRG, ' ', C_DATA_ASCII}, + {"data-binary", ARG_STRG, ' ', C_DATA_BINARY}, + {"data-raw", ARG_STRG, ' ', C_DATA_RAW}, + {"data-urlencode", ARG_STRG, ' ', C_DATA_URLENCODE}, + {"delegation", ARG_STRG, ' ', C_DELEGATION}, + {"digest", ARG_BOOL, ' ', C_DIGEST}, + {"disable", ARG_BOOL, 'q', C_DISABLE}, + {"disable-eprt", ARG_BOOL, ' ', C_DISABLE_EPRT}, + {"disable-epsv", ARG_BOOL, ' ', C_DISABLE_EPSV}, + {"disallow-username-in-url", ARG_BOOL, ' ', C_DISALLOW_USERNAME_IN_URL}, + {"dns-interface", ARG_STRG, ' ', C_DNS_INTERFACE}, + {"dns-ipv4-addr", ARG_STRG, ' ', C_DNS_IPV4_ADDR}, + {"dns-ipv6-addr", ARG_STRG, ' ', C_DNS_IPV6_ADDR}, + {"dns-servers", ARG_STRG, ' ', C_DNS_SERVERS}, + {"doh-cert-status", ARG_BOOL, ' ', C_DOH_CERT_STATUS}, + {"doh-insecure", ARG_BOOL, ' ', C_DOH_INSECURE}, + {"doh-url" , ARG_STRG, ' ', C_DOH_URL}, + {"dump-header", ARG_FILE, 'D', C_DUMP_HEADER}, + {"ech", ARG_STRG, ' ', C_ECH}, + {"egd-file", ARG_STRG, ' ', C_EGD_FILE}, + {"engine", ARG_STRG, ' ', C_ENGINE}, + {"eprt", ARG_BOOL, ' ', C_EPRT}, + {"epsv", ARG_BOOL, ' ', C_EPSV}, + {"etag-compare", ARG_FILE, ' ', C_ETAG_COMPARE}, + {"etag-save", ARG_FILE, ' ', C_ETAG_SAVE}, + {"expect100-timeout", ARG_STRG, ' ', C_EXPECT100_TIMEOUT}, + {"fail", ARG_BOOL, 'f', C_FAIL}, + {"fail-early", ARG_BOOL, ' ', C_FAIL_EARLY}, + {"fail-with-body", ARG_BOOL, ' ', C_FAIL_WITH_BODY}, + {"false-start", ARG_BOOL, ' ', C_FALSE_START}, + {"form", ARG_STRG, 'F', C_FORM}, + {"form-escape", ARG_BOOL, ' ', C_FORM_ESCAPE}, + {"form-string", ARG_STRG, ' ', C_FORM_STRING}, + {"ftp-account", ARG_STRG, ' ', C_FTP_ACCOUNT}, + {"ftp-alternative-to-user", ARG_STRG, ' ', C_FTP_ALTERNATIVE_TO_USER}, + {"ftp-create-dirs", ARG_BOOL, ' ', C_FTP_CREATE_DIRS}, + {"ftp-method", ARG_STRG, ' ', C_FTP_METHOD}, + {"ftp-pasv", ARG_BOOL, ' ', C_FTP_PASV}, + {"ftp-port", ARG_STRG, 'P', C_FTP_PORT}, + {"ftp-pret", ARG_BOOL, ' ', C_FTP_PRET}, + {"ftp-skip-pasv-ip", ARG_BOOL, ' ', C_FTP_SKIP_PASV_IP}, + {"ftp-ssl", ARG_BOOL, ' ', C_FTP_SSL}, + {"ftp-ssl-ccc", ARG_BOOL, ' ', C_FTP_SSL_CCC}, + {"ftp-ssl-ccc-mode", ARG_STRG, ' ', C_FTP_SSL_CCC_MODE}, + {"ftp-ssl-control", ARG_BOOL, ' ', C_FTP_SSL_CONTROL}, + {"ftp-ssl-reqd", ARG_BOOL, ' ', C_FTP_SSL_REQD}, + {"get", ARG_BOOL, 'G', C_GET}, + {"globoff", ARG_BOOL, 'g', C_GLOBOFF}, + {"happy-eyeballs-timeout-ms", ARG_STRG, ' ', C_HAPPY_EYEBALLS_TIMEOUT_MS}, + {"haproxy-clientip", ARG_STRG, ' ', C_HAPROXY_CLIENTIP}, + {"haproxy-protocol", ARG_BOOL, ' ', C_HAPROXY_PROTOCOL}, + {"head", ARG_BOOL, 'I', C_HEAD}, + {"header", ARG_STRG, 'H', C_HEADER}, + {"help", ARG_BOOL, 'h', C_HELP}, + {"hostpubmd5", ARG_STRG, ' ', C_HOSTPUBMD5}, + {"hostpubsha256", ARG_STRG, ' ', C_HOSTPUBSHA256}, + {"hsts", ARG_STRG, ' ', C_HSTS}, + {"http0.9", ARG_BOOL, ' ', C_HTTP0_9}, + {"http1.0", ARG_NONE, '0', C_HTTP1_0}, + {"http1.1", ARG_NONE, ' ', C_HTTP1_1}, + {"http2", ARG_NONE, ' ', C_HTTP2}, + {"http2-prior-knowledge", ARG_NONE, ' ', C_HTTP2_PRIOR_KNOWLEDGE}, + {"http3", ARG_NONE, ' ', C_HTTP3}, + {"http3-only", ARG_NONE, ' ', C_HTTP3_ONLY}, + {"ignore-content-length", ARG_BOOL, ' ', C_IGNORE_CONTENT_LENGTH}, + {"include", ARG_BOOL, 'i', C_INCLUDE}, + {"insecure", ARG_BOOL, 'k', C_INSECURE}, + {"interface", ARG_STRG, ' ', C_INTERFACE}, + {"ip-tos", ARG_STRG, ' ', C_IP_TOS}, + {"ipfs-gateway", ARG_STRG, ' ', C_IPFS_GATEWAY}, + {"ipv4", ARG_NONE, '4', C_IPV4}, + {"ipv6", ARG_NONE, '6', C_IPV6}, + {"json", ARG_STRG, ' ', C_JSON}, + {"junk-session-cookies", ARG_BOOL, 'j', C_JUNK_SESSION_COOKIES}, + {"keepalive", ARG_BOOL, ' ', C_KEEPALIVE}, + {"keepalive-cnt", ARG_STRG, ' ', C_KEEPALIVE_CNT}, + {"keepalive-time", ARG_STRG, ' ', C_KEEPALIVE_TIME}, + {"key", ARG_FILE, ' ', C_KEY}, + {"key-type", ARG_STRG, ' ', C_KEY_TYPE}, + {"krb", ARG_STRG, ' ', C_KRB}, + {"krb4", ARG_STRG, ' ', C_KRB4}, + {"libcurl", ARG_STRG, ' ', C_LIBCURL}, + {"limit-rate", ARG_STRG, ' ', C_LIMIT_RATE}, + {"list-only", ARG_BOOL, 'l', C_LIST_ONLY}, + {"local-port", ARG_STRG, ' ', C_LOCAL_PORT}, + {"location", ARG_BOOL, 'L', C_LOCATION}, + {"location-trusted", ARG_BOOL, ' ', C_LOCATION_TRUSTED}, + {"login-options", ARG_STRG, ' ', C_LOGIN_OPTIONS}, + {"mail-auth", ARG_STRG, ' ', C_MAIL_AUTH}, + {"mail-from", ARG_STRG, ' ', C_MAIL_FROM}, + {"mail-rcpt", ARG_STRG, ' ', C_MAIL_RCPT}, + {"mail-rcpt-allowfails", ARG_BOOL, ' ', C_MAIL_RCPT_ALLOWFAILS}, + {"manual", ARG_BOOL, 'M', C_MANUAL}, + {"max-filesize", ARG_STRG, ' ', C_MAX_FILESIZE}, + {"max-redirs", ARG_STRG, ' ', C_MAX_REDIRS}, + {"max-time", ARG_STRG, 'm', C_MAX_TIME}, + {"metalink", ARG_BOOL, ' ', C_METALINK}, + {"mptcp", ARG_BOOL, ' ', C_MPTCP}, + {"negotiate", ARG_BOOL, ' ', C_NEGOTIATE}, + {"netrc", ARG_BOOL, 'n', C_NETRC}, + {"netrc-file", ARG_FILE, ' ', C_NETRC_FILE}, + {"netrc-optional", ARG_BOOL, ' ', C_NETRC_OPTIONAL}, + {"next", ARG_NONE, ':', C_NEXT}, + {"noproxy", ARG_STRG, ' ', C_NOPROXY}, + {"npn", ARG_BOOL, ' ', C_NPN}, + {"ntlm", ARG_BOOL, ' ', C_NTLM}, + {"ntlm-wb", ARG_BOOL, ' ', C_NTLM_WB}, + {"oauth2-bearer", ARG_STRG, ' ', C_OAUTH2_BEARER}, + {"output", ARG_FILE, 'o', C_OUTPUT}, + {"output-dir", ARG_STRG, ' ', C_OUTPUT_DIR}, + {"parallel", ARG_BOOL, 'Z', C_PARALLEL}, + {"parallel-immediate", ARG_BOOL, ' ', C_PARALLEL_IMMEDIATE}, + {"parallel-max", ARG_STRG, ' ', C_PARALLEL_MAX}, + {"pass", ARG_STRG, ' ', C_PASS}, + {"path-as-is", ARG_BOOL, ' ', C_PATH_AS_IS}, + {"pinnedpubkey", ARG_STRG, ' ', C_PINNEDPUBKEY}, + {"post301", ARG_BOOL, ' ', C_POST301}, + {"post302", ARG_BOOL, ' ', C_POST302}, + {"post303", ARG_BOOL, ' ', C_POST303}, + {"preproxy", ARG_STRG, ' ', C_PREPROXY}, + {"progress-bar", ARG_BOOL, '#', C_PROGRESS_BAR}, + {"progress-meter", ARG_BOOL, ' ', C_PROGRESS_METER}, + {"proto", ARG_STRG, ' ', C_PROTO}, + {"proto-default", ARG_STRG, ' ', C_PROTO_DEFAULT}, + {"proto-redir", ARG_STRG, ' ', C_PROTO_REDIR}, + {"proxy", ARG_STRG, 'x', C_PROXY}, + {"proxy-anyauth", ARG_BOOL, ' ', C_PROXY_ANYAUTH}, + {"proxy-basic", ARG_BOOL, ' ', C_PROXY_BASIC}, + {"proxy-ca-native", ARG_BOOL, ' ', C_PROXY_CA_NATIVE}, + {"proxy-cacert", ARG_FILE, ' ', C_PROXY_CACERT}, + {"proxy-capath", ARG_FILE, ' ', C_PROXY_CAPATH}, + {"proxy-cert", ARG_FILE, ' ', C_PROXY_CERT}, + {"proxy-cert-type", ARG_STRG, ' ', C_PROXY_CERT_TYPE}, + {"proxy-ciphers", ARG_STRG, ' ', C_PROXY_CIPHERS}, + {"proxy-crlfile", ARG_FILE, ' ', C_PROXY_CRLFILE}, + {"proxy-digest", ARG_BOOL, ' ', C_PROXY_DIGEST}, + {"proxy-header", ARG_STRG, ' ', C_PROXY_HEADER}, + {"proxy-http2", ARG_BOOL, ' ', C_PROXY_HTTP2}, + {"proxy-insecure", ARG_BOOL, ' ', C_PROXY_INSECURE}, + {"proxy-key", ARG_FILE, ' ', C_PROXY_KEY}, + {"proxy-key-type", ARG_STRG, ' ', C_PROXY_KEY_TYPE}, + {"proxy-negotiate", ARG_BOOL, ' ', C_PROXY_NEGOTIATE}, + {"proxy-ntlm", ARG_BOOL, ' ', C_PROXY_NTLM}, + {"proxy-pass", ARG_STRG, ' ', C_PROXY_PASS}, + {"proxy-pinnedpubkey", ARG_STRG, ' ', C_PROXY_PINNEDPUBKEY}, + {"proxy-service-name", ARG_STRG, ' ', C_PROXY_SERVICE_NAME}, + {"proxy-ssl-allow-beast", ARG_BOOL, ' ', C_PROXY_SSL_ALLOW_BEAST}, + {"proxy-ssl-auto-client-cert", ARG_BOOL, ' ', C_PROXY_SSL_AUTO_CLIENT_CERT}, + {"proxy-tls13-ciphers", ARG_STRG, ' ', C_PROXY_TLS13_CIPHERS}, + {"proxy-tlsauthtype", ARG_STRG, ' ', C_PROXY_TLSAUTHTYPE}, + {"proxy-tlspassword", ARG_STRG, ' ', C_PROXY_TLSPASSWORD}, + {"proxy-tlsuser", ARG_STRG, ' ', C_PROXY_TLSUSER}, + {"proxy-tlsv1", ARG_NONE, ' ', C_PROXY_TLSV1}, + {"proxy-user", ARG_STRG, 'U', C_PROXY_USER}, + {"proxy1.0", ARG_STRG, ' ', C_PROXY1_0}, + {"proxytunnel", ARG_BOOL, 'p', C_PROXYTUNNEL}, + {"pubkey", ARG_STRG, ' ', C_PUBKEY}, + {"quote", ARG_STRG, 'Q', C_QUOTE}, + {"random-file", ARG_FILE, ' ', C_RANDOM_FILE}, + {"range", ARG_STRG, 'r', C_RANGE}, + {"rate", ARG_STRG, ' ', C_RATE}, + {"raw", ARG_BOOL, ' ', C_RAW}, + {"referer", ARG_STRG, 'e', C_REFERER}, + {"remote-header-name", ARG_BOOL, 'J', C_REMOTE_HEADER_NAME}, + {"remote-name", ARG_BOOL, 'O', C_REMOTE_NAME}, + {"remote-name-all", ARG_BOOL, ' ', C_REMOTE_NAME_ALL}, + {"remote-time", ARG_BOOL, 'R', C_REMOTE_TIME}, + {"remove-on-error", ARG_BOOL, ' ', C_REMOVE_ON_ERROR}, + {"request", ARG_STRG, 'X', C_REQUEST}, + {"request-target", ARG_STRG, ' ', C_REQUEST_TARGET}, + {"resolve", ARG_STRG, ' ', C_RESOLVE}, + {"retry", ARG_STRG, ' ', C_RETRY}, + {"retry-all-errors", ARG_BOOL, ' ', C_RETRY_ALL_ERRORS}, + {"retry-connrefused", ARG_BOOL, ' ', C_RETRY_CONNREFUSED}, + {"retry-delay", ARG_STRG, ' ', C_RETRY_DELAY}, + {"retry-max-time", ARG_STRG, ' ', C_RETRY_MAX_TIME}, + {"sasl-authzid", ARG_STRG, ' ', C_SASL_AUTHZID}, + {"sasl-ir", ARG_BOOL, ' ', C_SASL_IR}, + {"service-name", ARG_STRG, ' ', C_SERVICE_NAME}, + {"sessionid", ARG_BOOL, ' ', C_SESSIONID}, + {"show-error", ARG_BOOL, 'S', C_SHOW_ERROR}, + {"silent", ARG_BOOL, 's', C_SILENT}, + {"socks4", ARG_STRG, ' ', C_SOCKS4}, + {"socks4a", ARG_STRG, ' ', C_SOCKS4A}, + {"socks5", ARG_STRG, ' ', C_SOCKS5}, + {"socks5-basic", ARG_BOOL, ' ', C_SOCKS5_BASIC}, + {"socks5-gssapi", ARG_BOOL, ' ', C_SOCKS5_GSSAPI}, + {"socks5-gssapi-nec", ARG_BOOL, ' ', C_SOCKS5_GSSAPI_NEC}, + {"socks5-gssapi-service", ARG_STRG, ' ', C_SOCKS5_GSSAPI_SERVICE}, + {"socks5-hostname", ARG_STRG, ' ', C_SOCKS5_HOSTNAME}, + {"speed-limit", ARG_STRG, 'Y', C_SPEED_LIMIT}, + {"speed-time", ARG_STRG, 'y', C_SPEED_TIME}, + {"ssl", ARG_BOOL, ' ', C_SSL}, + {"ssl-allow-beast", ARG_BOOL, ' ', C_SSL_ALLOW_BEAST}, + {"ssl-auto-client-cert", ARG_BOOL, ' ', C_SSL_AUTO_CLIENT_CERT}, + {"ssl-no-revoke", ARG_BOOL, ' ', C_SSL_NO_REVOKE}, + {"ssl-reqd", ARG_BOOL, ' ', C_SSL_REQD}, + {"ssl-revoke-best-effort", ARG_BOOL, ' ', C_SSL_REVOKE_BEST_EFFORT}, + {"sslv2", ARG_NONE, '2', C_SSLV2}, + {"sslv3", ARG_NONE, '3', C_SSLV3}, + {"stderr", ARG_FILE, ' ', C_STDERR}, + {"styled-output", ARG_BOOL, ' ', C_STYLED_OUTPUT}, + {"suppress-connect-headers", ARG_BOOL, ' ', C_SUPPRESS_CONNECT_HEADERS}, + {"tcp-fastopen", ARG_BOOL, ' ', C_TCP_FASTOPEN}, + {"tcp-nodelay", ARG_BOOL, ' ', C_TCP_NODELAY}, + {"telnet-option", ARG_STRG, 't', C_TELNET_OPTION}, + {"test-event", ARG_BOOL, ' ', C_TEST_EVENT}, + {"tftp-blksize", ARG_STRG, ' ', C_TFTP_BLKSIZE}, + {"tftp-no-options", ARG_BOOL, ' ', C_TFTP_NO_OPTIONS}, + {"time-cond", ARG_STRG, 'z', C_TIME_COND}, + {"tls-max", ARG_STRG, ' ', C_TLS_MAX}, + {"tls13-ciphers", ARG_STRG, ' ', C_TLS13_CIPHERS}, + {"tlsauthtype", ARG_STRG, ' ', C_TLSAUTHTYPE}, + {"tlspassword", ARG_STRG, ' ', C_TLSPASSWORD}, + {"tlsuser", ARG_STRG, ' ', C_TLSUSER}, + {"tlsv1", ARG_NONE, '1', C_TLSV1}, + {"tlsv1.0", ARG_NONE, ' ', C_TLSV1_0}, + {"tlsv1.1", ARG_NONE, ' ', C_TLSV1_1}, + {"tlsv1.2", ARG_NONE, ' ', C_TLSV1_2}, + {"tlsv1.3", ARG_NONE, ' ', C_TLSV1_3}, + {"tr-encoding", ARG_BOOL, ' ', C_TR_ENCODING}, + {"trace", ARG_FILE, ' ', C_TRACE}, + {"trace-ascii", ARG_FILE, ' ', C_TRACE_ASCII}, + {"trace-config", ARG_STRG, ' ', C_TRACE_CONFIG}, + {"trace-ids", ARG_BOOL, ' ', C_TRACE_IDS}, + {"trace-time", ARG_BOOL, ' ', C_TRACE_TIME}, + {"unix-socket", ARG_FILE, ' ', C_UNIX_SOCKET}, + {"upload-file", ARG_FILE, 'T', C_UPLOAD_FILE}, + {"url", ARG_STRG, ' ', C_URL}, + {"url-query", ARG_STRG, ' ', C_URL_QUERY}, + {"use-ascii", ARG_BOOL, 'B', C_USE_ASCII}, + {"user", ARG_STRG, 'u', C_USER}, + {"user-agent", ARG_STRG, 'A', C_USER_AGENT}, + {"variable", ARG_STRG, ' ', C_VARIABLE}, + {"verbose", ARG_BOOL, 'v', C_VERBOSE}, + {"version", ARG_BOOL, 'V', C_VERSION}, + {"vlan-priority", ARG_STRG, ' ', C_VLAN_PRIORITY}, +#ifdef USE_WATT32 + {"wdebug", ARG_BOOL, ' ', C_WDEBUG}, +#endif + {"write-out", ARG_STRG, 'w', C_WRITE_OUT}, + {"xattr", ARG_BOOL, ' ', C_XATTR}, +}; + +/* Split the argument of -E to 'certname' and 'passphrase' separated by colon. + * We allow ':' and '\' to be escaped by '\' so that we can use certificate + * nicknames containing ':'. See + * for details. */ +#ifndef UNITTESTS +static +#endif +void parse_cert_parameter(const char *cert_parameter, + char **certname, + char **passphrase) +{ + size_t param_length = strlen(cert_parameter); + size_t span; + const char *param_place = NULL; + char *certname_place = NULL; + *certname = NULL; + *passphrase = NULL; + + /* most trivial assumption: cert_parameter is empty */ + if(param_length == 0) + return; + + /* next less trivial: cert_parameter starts 'pkcs11:' and thus + * looks like a RFC7512 PKCS#11 URI which can be used as-is. + * Also if cert_parameter contains no colon nor backslash, this + * means no passphrase was given and no characters escaped */ + if(curl_strnequal(cert_parameter, "pkcs11:", 7) || + !strpbrk(cert_parameter, ":\\")) { + *certname = strdup(cert_parameter); + return; + } + /* deal with escaped chars; find unescaped colon if it exists */ + certname_place = malloc(param_length + 1); + if(!certname_place) + return; + + *certname = certname_place; + param_place = cert_parameter; + while(*param_place) { + span = strcspn(param_place, ":\\"); + strncpy(certname_place, param_place, span); + param_place += span; + certname_place += span; + /* we just ate all the non-special chars. now we are on either a special + * char or the end of the string. */ + switch(*param_place) { + case '\0': + break; + case '\\': + param_place++; + switch(*param_place) { + case '\0': + *certname_place++ = '\\'; + break; + case '\\': + *certname_place++ = '\\'; + param_place++; + break; + case ':': + *certname_place++ = ':'; + param_place++; + break; + default: + *certname_place++ = '\\'; + *certname_place++ = *param_place; + param_place++; + break; + } + break; + case ':': + /* Since we live in a world of weirdness and confusion, the win32 + dudes can use : when using drive letters and thus c:\file:password + needs to work. In order not to break compatibility, we still use : as + separator, but we try to detect when it is used for a filename! On + windows. */ +#ifdef _WIN32 + if((param_place == &cert_parameter[1]) && + (cert_parameter[2] == '\\' || cert_parameter[2] == '/') && + (ISALPHA(cert_parameter[0])) ) { + /* colon in the second column, followed by a backslash, and the + first character is an alphabetic letter: + + this is a drive letter colon */ + *certname_place++ = ':'; + param_place++; + break; + } +#endif + /* escaped colons and Windows drive letter colons were handled + * above; if we are still here, this is a separating colon */ + param_place++; + if(*param_place) { + *passphrase = strdup(param_place); + } + goto done; + } + } +done: + *certname_place = '\0'; +} + +/* Replace (in-place) '%20' by '+' according to RFC1866 */ +static size_t replace_url_encoded_space_by_plus(char *url) +{ + size_t orig_len = strlen(url); + size_t orig_index = 0; + size_t new_index = 0; + + while(orig_index < orig_len) { + if((url[orig_index] == '%') && + (url[orig_index + 1] == '2') && + (url[orig_index + 2] == '0')) { + url[new_index] = '+'; + orig_index += 3; + } + else{ + if(new_index != orig_index) { + url[new_index] = url[orig_index]; + } + orig_index++; + } + new_index++; + } + + url[new_index] = 0; /* terminate string */ + + return new_index; /* new size */ +} + +static void +GetFileAndPassword(char *nextarg, char **file, char **password) +{ + char *certname, *passphrase; + if(nextarg) { + parse_cert_parameter(nextarg, &certname, &passphrase); + Curl_safefree(*file); + *file = certname; + if(passphrase) { + Curl_safefree(*password); + *password = passphrase; + } + } +} + +/* Get a size parameter for '--limit-rate' or '--max-filesize'. + * We support a 'G', 'M' or 'K' suffix too. + */ +static ParameterError GetSizeParameter(struct GlobalConfig *global, + const char *arg, + const char *which, + curl_off_t *value_out) +{ + char *unit; + curl_off_t value; + + if(curlx_strtoofft(arg, &unit, 10, &value)) { + warnf(global, "invalid number specified for %s", which); + return PARAM_BAD_USE; + } + + if(!*unit) + unit = (char *)"b"; + else if(strlen(unit) > 1) + unit = (char *)"w"; /* unsupported */ + + switch(*unit) { + case 'G': + case 'g': + if(value > (CURL_OFF_T_MAX / (1024*1024*1024))) + return PARAM_NUMBER_TOO_LARGE; + value *= 1024*1024*1024; + break; + case 'M': + case 'm': + if(value > (CURL_OFF_T_MAX / (1024*1024))) + return PARAM_NUMBER_TOO_LARGE; + value *= 1024*1024; + break; + case 'K': + case 'k': + if(value > (CURL_OFF_T_MAX / 1024)) + return PARAM_NUMBER_TOO_LARGE; + value *= 1024; + break; + case 'b': + case 'B': + /* for plain bytes, leave as-is */ + break; + default: + warnf(global, "unsupported %s unit. Use G, M, K or B", which); + return PARAM_BAD_USE; + } + *value_out = value; + return PARAM_OK; +} + +#ifdef HAVE_WRITABLE_ARGV +static void cleanarg(argv_item_t str) +{ + /* now that getstr has copied the contents of nextarg, wipe the next + * argument out so that the username:password is not displayed in the + * system process list */ + if(str) { + size_t len = strlen(str); + memset(str, ' ', len); + } +} +#else +#define cleanarg(x) +#endif + +/* --data-urlencode */ +static ParameterError data_urlencode(struct GlobalConfig *global, + char *nextarg, + char **postp, + size_t *lenp) +{ + /* [name]=[content], we encode the content part only + * [name]@[filename] + * + * Case 2: we first load the file using that name and then encode + * the content. + */ + ParameterError err; + const char *p = strchr(nextarg, '='); + size_t nlen; + char is_file; + char *postdata = NULL; + size_t size = 0; + if(!p) + /* there was no '=' letter, check for a '@' instead */ + p = strchr(nextarg, '@'); + if(p) { + nlen = p - nextarg; /* length of the name part */ + is_file = *p++; /* pass the separator */ + } + else { + /* neither @ nor =, so no name and it is not a file */ + nlen = 0; + is_file = 0; + p = nextarg; + } + if('@' == is_file) { + FILE *file; + /* a '@' letter, it means that a filename or - (stdin) follows */ + if(!strcmp("-", p)) { + file = stdin; + set_binmode(stdin); + } + else { + file = fopen(p, "rb"); + if(!file) { + errorf(global, "Failed to open %s", p); + return PARAM_READ_ERROR; + } + } + + err = file2memory(&postdata, &size, file); + + if(file && (file != stdin)) + fclose(file); + if(err) + return err; + } + else { + err = getstr(&postdata, p, ALLOW_BLANK); + if(err) + goto error; + size = strlen(postdata); + } + + if(!postdata) { + /* no data from the file, point to a zero byte string to make this + get sent as a POST anyway */ + postdata = strdup(""); + if(!postdata) + return PARAM_NO_MEM; + size = 0; + } + else { + char *enc = curl_easy_escape(NULL, postdata, (int)size); + Curl_safefree(postdata); /* no matter if it worked or not */ + if(enc) { + char *n; + replace_url_encoded_space_by_plus(enc); + if(nlen > 0) { /* only append '=' if we have a name */ + n = aprintf("%.*s=%s", (int)nlen, nextarg, enc); + curl_free(enc); + if(!n) + return PARAM_NO_MEM; + } + else + n = enc; + + size = strlen(n); + postdata = n; + } + else + return PARAM_NO_MEM; + } + *postp = postdata; + *lenp = size; + return PARAM_OK; +error: + return err; +} + +static void sethttpver(struct GlobalConfig *global, + struct OperationConfig *config, + long httpversion) +{ + if(config->httpversion && + (config->httpversion != httpversion)) + warnf(global, "Overrides previous HTTP version option"); + + config->httpversion = httpversion; +} + +static CURLcode set_trace_config(struct GlobalConfig *global, + const char *config) +{ + CURLcode result = CURLE_OK; + char *token, *tmp, *name; + bool toggle; + + tmp = strdup(config); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + /* Allow strtok() here since this is not used threaded */ + /* !checksrc! disable BANNEDFUNC 2 */ + token = strtok(tmp, ", "); + while(token) { + switch(*token) { + case '-': + toggle = FALSE; + name = token + 1; + break; + case '+': + toggle = TRUE; + name = token + 1; + break; + default: + toggle = TRUE; + name = token; + break; + } + + if(strcasecompare(name, "all")) { + global->traceids = toggle; + global->tracetime = toggle; + result = curl_global_trace(token); + if(result) + goto out; + } + else if(strcasecompare(name, "ids")) { + global->traceids = toggle; + } + else if(strcasecompare(name, "time")) { + global->tracetime = toggle; + } + else { + result = curl_global_trace(token); + if(result) + goto out; + } + token = strtok(NULL, ", "); + } +out: + free(tmp); + return result; +} + +static int findarg(const void *a, const void *b) +{ + const struct LongShort *aa = a; + const struct LongShort *bb = b; + return strcmp(aa->lname, bb->lname); +} + +static const struct LongShort *single(char letter) +{ + static const struct LongShort *singles[128 - ' ']; /* ASCII => pointer */ + static bool singles_done = FALSE; + if((letter >= 127) || (letter <= ' ')) + return NULL; + + if(!singles_done) { + unsigned int j; + for(j = 0; j < sizeof(aliases)/sizeof(aliases[0]); j++) { + if(aliases[j].letter != ' ') { + unsigned char l = (unsigned char)aliases[j].letter; + singles[l - ' '] = &aliases[j]; + } + } + singles_done = TRUE; + } + return singles[letter - ' ']; +} + +struct TOSEntry { + const char *name; + unsigned char value; +}; + +static const struct TOSEntry tos_entries[] = { + {"AF11", 0x28}, + {"AF12", 0x30}, + {"AF13", 0x38}, + {"AF21", 0x48}, + {"AF22", 0x50}, + {"AF23", 0x58}, + {"AF31", 0x68}, + {"AF32", 0x70}, + {"AF33", 0x78}, + {"AF41", 0x88}, + {"AF42", 0x90}, + {"AF43", 0x98}, + {"CE", 0x03}, + {"CS0", 0x00}, + {"CS1", 0x20}, + {"CS2", 0x40}, + {"CS3", 0x60}, + {"CS4", 0x80}, + {"CS5", 0xa0}, + {"CS6", 0xc0}, + {"CS7", 0xe0}, + {"ECT0", 0x02}, + {"ECT1", 0x01}, + {"EF", 0xb8}, + {"LE", 0x04}, + {"LOWCOST", 0x02}, + {"LOWDELAY", 0x10}, + {"MINCOST", 0x02}, + {"RELIABILITY", 0x04}, + {"THROUGHPUT", 0x08}, + {"VOICE-ADMIT", 0xb0} +}; + +static int find_tos(const void *a, const void *b) +{ + const struct TOSEntry *aa = a; + const struct TOSEntry *bb = b; + return strcmp(aa->name, bb->name); +} + +#define MAX_QUERY_LEN 100000 /* larger is not likely to ever work */ +static ParameterError url_query(char *nextarg, + struct GlobalConfig *global, + struct OperationConfig *config) +{ + size_t size = 0; + ParameterError err = PARAM_OK; + char *query; + struct curlx_dynbuf dyn; + curlx_dyn_init(&dyn, MAX_QUERY_LEN); + + if(*nextarg == '+') { + /* use without encoding */ + query = strdup(&nextarg[1]); + if(!query) + err = PARAM_NO_MEM; + } + else + err = data_urlencode(global, nextarg, &query, &size); + + if(!err) { + if(config->query) { + CURLcode result = curlx_dyn_addf(&dyn, "%s&%s", config->query, query); + free(query); + if(result) + err = PARAM_NO_MEM; + else { + free(config->query); + config->query = curlx_dyn_ptr(&dyn); + } + } + else + config->query = query; + } + return err; +} + +static ParameterError set_data(cmdline_t cmd, + char *nextarg, + struct GlobalConfig *global, + struct OperationConfig *config) +{ + char *postdata = NULL; + FILE *file; + size_t size = 0; + ParameterError err = PARAM_OK; + + if(cmd == C_DATA_URLENCODE) { /* --data-urlencode */ + err = data_urlencode(global, nextarg, &postdata, &size); + if(err) + return err; + } + else if('@' == *nextarg && (cmd != C_DATA_RAW)) { + /* the data begins with a '@' letter, it means that a filename + or - (stdin) follows */ + nextarg++; /* pass the @ */ + + if(!strcmp("-", nextarg)) { + file = stdin; + if(cmd == C_DATA_BINARY) /* forced data-binary */ + set_binmode(stdin); + } + else { + file = fopen(nextarg, "rb"); + if(!file) { + errorf(global, "Failed to open %s", nextarg); + return PARAM_READ_ERROR; + } + } + + if((cmd == C_DATA_BINARY) || /* --data-binary */ + (cmd == C_JSON) /* --json */) + /* forced binary */ + err = file2memory(&postdata, &size, file); + else { + err = file2string(&postdata, file); + if(postdata) + size = strlen(postdata); + } + + if(file && (file != stdin)) + fclose(file); + if(err) + return err; + + if(!postdata) { + /* no data from the file, point to a zero byte string to make this + get sent as a POST anyway */ + postdata = strdup(""); + if(!postdata) + return PARAM_NO_MEM; + } + } + else { + err = getstr(&postdata, nextarg, ALLOW_BLANK); + if(err) + return err; + size = strlen(postdata); + } + if(cmd == C_JSON) + config->jsoned = TRUE; + + if(curlx_dyn_len(&config->postdata)) { + /* skip separator append for --json */ + if(!err && (cmd != C_JSON) && + curlx_dyn_addn(&config->postdata, "&", 1)) + err = PARAM_NO_MEM; + } + + if(!err && curlx_dyn_addn(&config->postdata, postdata, size)) + err = PARAM_NO_MEM; + + Curl_safefree(postdata); + + config->postfields = curlx_dyn_ptr(&config->postdata); + return err; +} + +static ParameterError set_rate(struct GlobalConfig *global, + char *nextarg) +{ + /* --rate */ + /* support a few different suffixes, extract the suffix first, then + get the number and convert to per hour. + /s == per second + /m == per minute + /h == per hour (default) + /d == per day (24 hours) + */ + ParameterError err = PARAM_OK; + char *div = strchr(nextarg, '/'); + char number[26]; + long denominator; + long numerator = 60*60*1000; /* default per hour */ + size_t numlen = div ? (size_t)(div - nextarg) : strlen(nextarg); + if(numlen > sizeof(number) -1) + return PARAM_NUMBER_TOO_LARGE; + + strncpy(number, nextarg, numlen); + number[numlen] = 0; + err = str2unum(&denominator, number); + if(err) + return err; + + if(denominator < 1) + return PARAM_BAD_USE; + + if(div) { + char unit = div[1]; + switch(unit) { + case 's': /* per second */ + numerator = 1000; + break; + case 'm': /* per minute */ + numerator = 60*1000; + break; + case 'h': /* per hour */ + break; + case 'd': /* per day */ + numerator = 24*60*60*1000; + break; + default: + errorf(global, "unsupported --rate unit"); + err = PARAM_BAD_USE; + break; + } + } + + if(err) + ; + else if(denominator > numerator) + err = PARAM_NUMBER_TOO_LARGE; + else + global->ms_per_transfer = numerator/denominator; + + return err; +} + + +ParameterError getparameter(const char *flag, /* f or -long-flag */ + char *nextarg, /* NULL if unset */ + argv_item_t cleararg, + bool *usedarg, /* set to TRUE if the arg + has been used */ + struct GlobalConfig *global, + struct OperationConfig *config) +{ + int rc; + const char *parse = NULL; + time_t now; + bool longopt = FALSE; + bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */ + ParameterError err = PARAM_OK; + bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled + by using --OPTION or --no-OPTION */ + bool nextalloc = FALSE; /* if nextarg is allocated */ + struct getout *url; + static const char *redir_protos[] = { + "http", + "https", + "ftp", + "ftps", + NULL + }; + const struct LongShort *a = NULL; + curl_off_t value; +#ifdef HAVE_WRITABLE_ARGV + argv_item_t clearthis = NULL; +#else + (void)cleararg; +#endif + + *usedarg = FALSE; /* default is that we do not use the arg */ + + if(('-' != flag[0]) || ('-' == flag[1])) { + /* this should be a long name */ + const char *word = ('-' == flag[0]) ? flag + 2 : flag; + bool noflagged = FALSE; + bool expand = FALSE; + struct LongShort key; + + if(!strncmp(word, "no-", 3)) { + /* disable this option but ignore the "no-" part when looking for it */ + word += 3; + toggle = FALSE; + noflagged = TRUE; + } + else if(!strncmp(word, "expand-", 7)) { + /* variable expansions is to be done on the argument */ + word += 7; + expand = TRUE; + } + key.lname = word; + + a = bsearch(&key, aliases, sizeof(aliases)/sizeof(aliases[0]), + sizeof(aliases[0]), findarg); + if(a) { + longopt = TRUE; + } + else { + err = PARAM_OPTION_UNKNOWN; + goto error; + } + if(noflagged && (a->desc != ARG_BOOL)) { + /* --no- prefixed an option that is not boolean! */ + err = PARAM_NO_NOT_BOOLEAN; + goto error; + } + else if(expand && nextarg) { + struct curlx_dynbuf nbuf; + bool replaced; + + if((a->desc != ARG_STRG) && + (a->desc != ARG_FILE)) { + /* --expand on an option that is not a string or a filename */ + err = PARAM_EXPAND_ERROR; + goto error; + } + err = varexpand(global, nextarg, &nbuf, &replaced); + if(err) { + curlx_dyn_free(&nbuf); + goto error; + } + if(replaced) { + nextarg = curlx_dyn_ptr(&nbuf); + nextalloc = TRUE; + } + } + } + else { + flag++; /* prefixed with one dash, pass it */ + parse = flag; + } + + do { + /* we can loop here if we have multiple single-letters */ + char letter; + cmdline_t cmd; + + if(!longopt && !a) { + a = single(*parse); + if(!a) { + err = PARAM_OPTION_UNKNOWN; + break; + } + } + letter = a->letter; + cmd = a->cmd; + if(a->desc >= ARG_STRG) { + /* this option requires an extra parameter */ + if(!longopt && parse[1]) { + nextarg = (char *)&parse[1]; /* this is the actual extra parameter */ + singleopt = TRUE; /* do not loop anymore after this */ + } + else if(!nextarg) { + err = PARAM_REQUIRES_PARAMETER; + break; + } + else { +#ifdef HAVE_WRITABLE_ARGV + clearthis = cleararg; +#endif + *usedarg = TRUE; /* mark it as used */ + } + + if((a->desc == ARG_FILE) && + (nextarg[0] == '-') && nextarg[1]) { + /* if the filename looks like a command line option */ + warnf(global, "The filename argument '%s' looks like a flag.", + nextarg); + } + else if(!strncmp("\xe2\x80\x9c", nextarg, 3)) { + warnf(global, "The argument '%s' starts with a unicode quote where " + "maybe an ASCII \" was intended?", + nextarg); + } + } + else if((a->desc == ARG_NONE) && !toggle) { + err = PARAM_NO_PREFIX; + break; + } + + if(!nextarg) + /* this is a precaution mostly to please scan-build, as all arguments + that use nextarg should be marked as such and they will check that + nextarg is set before continuing, but code analyzers are not always + that aware of that state */ + nextarg = (char *)""; + + switch(cmd) { + case C_RANDOM_FILE: /* --random-file */ + case C_EGD_FILE: /* --egd-file */ + case C_NTLM_WB: /* --ntlm-wb */ + warnf(global, "--%s is deprecated and has no function anymore", + a->lname); + break; + case C_DNS_IPV4_ADDR: /* --dns-ipv4-addr */ + if(!curlinfo->ares_num) /* c-ares is needed for this */ + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + /* addr in dot notation */ + err = getstr(&config->dns_ipv4_addr, nextarg, DENY_BLANK); + break; + case C_DNS_IPV6_ADDR: /* --dns-ipv6-addr */ + if(!curlinfo->ares_num) /* c-ares is needed for this */ + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + /* addr in dot notation */ + err = getstr(&config->dns_ipv6_addr, nextarg, DENY_BLANK); + break; + case C_OAUTH2_BEARER: /* --oauth2-bearer */ + err = getstr(&config->oauth_bearer, nextarg, DENY_BLANK); + if(!err) { + cleanarg(clearthis); + config->authtype |= CURLAUTH_BEARER; + } + break; + case C_CONNECT_TIMEOUT: /* --connect-timeout */ + err = secs2ms(&config->connecttimeout_ms, nextarg); + break; + case C_DOH_URL: /* --doh-url */ + err = getstr(&config->doh_url, nextarg, ALLOW_BLANK); + if(!err && config->doh_url && !config->doh_url[0]) + /* if given a blank string, make it NULL again */ + Curl_safefree(config->doh_url); + break; + case C_CIPHERS: /* -- ciphers */ + err = getstr(&config->cipher_list, nextarg, DENY_BLANK); + break; + case C_DNS_INTERFACE: /* --dns-interface */ + if(!curlinfo->ares_num) /* c-ares is needed for this */ + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + /* interface name */ + err = getstr(&config->dns_interface, nextarg, DENY_BLANK); + break; + case C_DISABLE_EPSV: /* --disable-epsv */ + config->disable_epsv = toggle; + break; + case C_DISALLOW_USERNAME_IN_URL: /* --disallow-username-in-url */ + config->disallow_username_in_url = toggle; + break; + case C_EPSV: /* --epsv */ + config->disable_epsv = (!toggle)?TRUE:FALSE; + break; + case C_DNS_SERVERS: /* --dns-servers */ + if(!curlinfo->ares_num) /* c-ares is needed for this */ + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + /* IP addrs of DNS servers */ + err = getstr(&config->dns_servers, nextarg, DENY_BLANK); + break; + case C_TRACE: /* --trace */ + err = getstr(&global->trace_dump, nextarg, DENY_BLANK); + if(!err) { + if(global->tracetype && (global->tracetype != TRACE_BIN)) + warnf(global, "--trace overrides an earlier trace/verbose option"); + global->tracetype = TRACE_BIN; + } + break; + case C_NPN: /* --npn */ + warnf(global, "--npn is no longer supported"); + break; + case C_TRACE_ASCII: /* --trace-ascii */ + err = getstr(&global->trace_dump, nextarg, DENY_BLANK); + if(!err) { + if(global->tracetype && (global->tracetype != TRACE_ASCII)) + warnf(global, + "--trace-ascii overrides an earlier trace/verbose option"); + global->tracetype = TRACE_ASCII; + } + break; + case C_ALPN: /* --alpn */ + config->noalpn = (!toggle)?TRUE:FALSE; + break; + case C_LIMIT_RATE: /* --limit-rate */ + err = GetSizeParameter(global, nextarg, "rate", &value); + if(!err) { + config->recvpersecond = value; + config->sendpersecond = value; + } + break; + case C_RATE: + err = set_rate(global, nextarg); + break; + case C_COMPRESSED: /* --compressed */ + if(toggle && !(feature_libz || feature_brotli || feature_zstd)) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + config->encoding = toggle; + break; + case C_TR_ENCODING: /* --tr-encoding */ + config->tr_encoding = toggle; + break; + case C_DIGEST: /* --digest */ + if(toggle) + config->authtype |= CURLAUTH_DIGEST; + else + config->authtype &= ~CURLAUTH_DIGEST; + break; + case C_NEGOTIATE: /* --negotiate */ + if(!toggle) + config->authtype &= ~CURLAUTH_NEGOTIATE; + else if(feature_spnego) + config->authtype |= CURLAUTH_NEGOTIATE; + else + err = PARAM_LIBCURL_DOESNT_SUPPORT; + break; + case C_NTLM: /* --ntlm */ + if(!toggle) + config->authtype &= ~CURLAUTH_NTLM; + else if(feature_ntlm) + config->authtype |= CURLAUTH_NTLM; + else + err = PARAM_LIBCURL_DOESNT_SUPPORT; + break; + case C_BASIC: /* --basic */ + if(toggle) + config->authtype |= CURLAUTH_BASIC; + else + config->authtype &= ~CURLAUTH_BASIC; + break; + case C_ANYAUTH: /* --anyauth */ + if(toggle) + config->authtype = CURLAUTH_ANY; + /* --no-anyauth simply does not touch it */ + break; +#ifdef USE_WATT32 + case C_WDEBUG: /* --wdebug */ + dbug_init(); + break; +#endif + case C_FTP_CREATE_DIRS: /* --ftp-create-dirs */ + config->ftp_create_dirs = toggle; + break; + case C_CREATE_DIRS: /* --create-dirs */ + config->create_dirs = toggle; + break; + case C_CREATE_FILE_MODE: /* --create-file-mode */ + err = oct2nummax(&config->create_file_mode, nextarg, 0777); + break; + case C_MAX_REDIRS: /* --max-redirs */ + /* specified max no of redirects (http(s)), this accepts -1 as a + special condition */ + err = str2num(&config->maxredirs, nextarg); + if(!err && (config->maxredirs < -1)) + err = PARAM_BAD_NUMERIC; + break; + case C_IPFS_GATEWAY: /* --ipfs-gateway */ + err = getstr(&config->ipfs_gateway, nextarg, DENY_BLANK); + break; + case C_PROXY_NTLM: /* --proxy-ntlm */ + if(!feature_ntlm) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + config->proxyntlm = toggle; + break; + case C_CRLF: /* --crlf */ + /* LF -> CRLF conversion? */ + config->crlf = toggle; + break; + case C_AWS_SIGV4: /* --aws-sigv4 */ + config->authtype |= CURLAUTH_AWS_SIGV4; + err = getstr(&config->aws_sigv4, nextarg, DENY_BLANK); + break; + case C_STDERR: /* --stderr */ + tool_set_stderr_file(global, nextarg); + break; + case C_INTERFACE: /* --interface */ + /* interface */ + err = getstr(&config->iface, nextarg, DENY_BLANK); + break; + case C_KRB: /* --krb */ + /* kerberos level string */ + if(!feature_spnego) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->krblevel, nextarg, DENY_BLANK); + break; + case C_HAPROXY_PROTOCOL: /* --haproxy-protocol */ + config->haproxy_protocol = toggle; + break; + case C_HAPROXY_CLIENTIP: /* --haproxy-clientip */ + err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK); + break; + case C_MAX_FILESIZE: /* --max-filesize */ + err = GetSizeParameter(global, nextarg, "max-filesize", &value); + if(!err) + config->max_filesize = value; + break; + case C_DISABLE_EPRT: /* --disable-eprt */ + config->disable_eprt = toggle; + break; + case C_EPRT: /* --eprt */ + config->disable_eprt = (!toggle)?TRUE:FALSE; + break; + case C_XATTR: /* --xattr */ + config->xattr = toggle; + break; + case C_URL: /* --url */ + if(!config->url_get) + config->url_get = config->url_list; + + if(config->url_get) { + /* there is a node here, if it already is filled-in continue to find + an "empty" node */ + while(config->url_get && (config->url_get->flags & GETOUT_URL)) + config->url_get = config->url_get->next; + } + + /* now there might or might not be an available node to fill in! */ + + if(config->url_get) + /* existing node */ + url = config->url_get; + else + /* there was no free node, create one! */ + config->url_get = url = new_getout(config); + + if(!url) + err = PARAM_NO_MEM; + else { + /* fill in the URL */ + err = getstr(&url->url, nextarg, DENY_BLANK); + url->flags |= GETOUT_URL; + } + break; + case C_FTP_SSL: /* --ftp-ssl */ + case C_SSL: /* --ssl */ + if(toggle && !feature_ssl) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else { + config->ftp_ssl = toggle; + if(config->ftp_ssl) + warnf(global, + "--%s is an insecure option, consider --ssl-reqd instead", + a->lname); + } + break; + case C_FTP_PASV: /* --ftp-pasv */ + Curl_safefree(config->ftpport); + break; + case C_SOCKS5: /* --socks5 */ + /* socks5 proxy to use, and resolves the name locally and passes on the + resolved address */ + err = getstr(&config->proxy, nextarg, DENY_BLANK); + config->proxyver = CURLPROXY_SOCKS5; + break; + case C_SOCKS4: /* --socks4 */ + err = getstr(&config->proxy, nextarg, DENY_BLANK); + config->proxyver = CURLPROXY_SOCKS4; + break; + case C_SOCKS4A: /* --socks4a */ + err = getstr(&config->proxy, nextarg, DENY_BLANK); + config->proxyver = CURLPROXY_SOCKS4A; + break; + case C_SOCKS5_HOSTNAME: /* --socks5-hostname */ + err = getstr(&config->proxy, nextarg, DENY_BLANK); + config->proxyver = CURLPROXY_SOCKS5_HOSTNAME; + break; + case C_TCP_NODELAY: /* --tcp-nodelay */ + config->tcp_nodelay = toggle; + break; + case C_IP_TOS: { /* --ip-tos */ + struct TOSEntry find; + const struct TOSEntry *entry; + find.name = nextarg; + entry = bsearch(&find, tos_entries, + sizeof(tos_entries)/sizeof(*tos_entries), + sizeof(*tos_entries), find_tos); + if(entry) + config->ip_tos = entry->value; + else /* numeric tos value */ + err = str2unummax(&config->ip_tos, nextarg, 0xFF); + break; + } + case C_VLAN_PRIORITY: /* --vlan-priority */ + err = str2unummax(&config->vlan_priority, nextarg, 7); + break; + case C_PROXY_DIGEST: /* --proxy-digest */ + config->proxydigest = toggle; + break; + case C_PROXY_BASIC: /* --proxy-basic */ + config->proxybasic = toggle; + break; + case C_RETRY: /* --retry */ + err = str2unum(&config->req_retry, nextarg); + break; + case C_RETRY_CONNREFUSED: /* --retry-connrefused */ + config->retry_connrefused = toggle; + break; + case C_RETRY_DELAY: /* --retry-delay */ + err = str2unummax(&config->retry_delay, nextarg, LONG_MAX/1000); + break; + case C_RETRY_MAX_TIME: /* --retry-max-time */ + err = str2unummax(&config->retry_maxtime, nextarg, LONG_MAX/1000); + break; + case C_RETRY_ALL_ERRORS: /* --retry-all-errors */ + config->retry_all_errors = toggle; + break; + case C_PROXY_NEGOTIATE: /* --proxy-negotiate */ + if(!feature_spnego) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + config->proxynegotiate = toggle; + break; + case C_FORM_ESCAPE: /* --form-escape */ + config->mime_options &= ~CURLMIMEOPT_FORMESCAPE; + if(toggle) + config->mime_options |= CURLMIMEOPT_FORMESCAPE; + break; + case C_FTP_ACCOUNT: /* --ftp-account */ + err = getstr(&config->ftp_account, nextarg, DENY_BLANK); + break; + case C_PROXY_ANYAUTH: /* --proxy-anyauth */ + config->proxyanyauth = toggle; + break; + case C_TRACE_TIME: /* --trace-time */ + global->tracetime = toggle; + break; + case C_IGNORE_CONTENT_LENGTH: /* --ignore-content-length */ + config->ignorecl = toggle; + break; + case C_FTP_SKIP_PASV_IP: /* --ftp-skip-pasv-ip */ + config->ftp_skip_ip = toggle; + break; + case C_FTP_METHOD: /* --ftp-method */ + config->ftp_filemethod = ftpfilemethod(config, nextarg); + break; + case C_LOCAL_PORT: { /* --local-port */ + /* 16bit base 10 is 5 digits, but we allow 6 so that this catches + overflows, not just truncates */ + char lrange[7]=""; + char *p = nextarg; + while(ISDIGIT(*p)) + p++; + if(*p) { + /* if there is anything more than a plain decimal number */ + rc = sscanf(p, " - %6s", lrange); + *p = 0; /* null-terminate to make str2unum() work below */ + } + else + rc = 0; + + err = str2unum(&config->localport, nextarg); + if(err || (config->localport > 65535)) { + err = PARAM_BAD_USE; + break; + } + if(!rc) + config->localportrange = 1; /* default number of ports to try */ + else { + err = str2unum(&config->localportrange, lrange); + if(err || (config->localportrange > 65535)) + err = PARAM_BAD_USE; + else { + config->localportrange -= (config->localport-1); + if(config->localportrange < 1) + err = PARAM_BAD_USE; + } + } + break; + } + case C_FTP_ALTERNATIVE_TO_USER: /* --ftp-alternative-to-user */ + err = getstr(&config->ftp_alternative_to_user, nextarg, DENY_BLANK); + break; + case C_FTP_SSL_REQD: /* --ftp-ssl-reqd */ + case C_SSL_REQD: /* --ssl-reqd */ + if(toggle && !feature_ssl) { + err = PARAM_LIBCURL_DOESNT_SUPPORT; + break; + } + config->ftp_ssl_reqd = toggle; + break; + case C_SESSIONID: /* --sessionid */ + config->disable_sessionid = (!toggle)?TRUE:FALSE; + break; + case C_FTP_SSL_CONTROL: /* --ftp-ssl-control */ + if(toggle && !feature_ssl) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + config->ftp_ssl_control = toggle; + break; + case C_FTP_SSL_CCC: /* --ftp-ssl-ccc */ + config->ftp_ssl_ccc = toggle; + if(!config->ftp_ssl_ccc_mode) + config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE; + break; + case C_FTP_SSL_CCC_MODE: /* --ftp-ssl-ccc-mode */ + config->ftp_ssl_ccc = TRUE; + config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg); + break; + case C_LIBCURL: /* --libcurl */ +#ifdef CURL_DISABLE_LIBCURL_OPTION + warnf(global, + "--libcurl option was disabled at build-time"); + err = PARAM_OPTION_UNKNOWN; +#else + err = getstr(&global->libcurl, nextarg, DENY_BLANK); +#endif + break; + case C_RAW: /* --raw */ + config->raw = toggle; + break; + case C_KEEPALIVE: /* --keepalive */ + config->nokeepalive = (!toggle)?TRUE:FALSE; + break; + case C_KEEPALIVE_TIME: /* --keepalive-time */ + err = str2unum(&config->alivetime, nextarg); + break; + case C_KEEPALIVE_CNT: /* --keepalive-cnt */ + err = str2unum(&config->alivecnt, nextarg); + break; + case C_POST301: /* --post301 */ + config->post301 = toggle; + break; + case C_POST302: /* --post302 */ + config->post302 = toggle; + break; + case C_POST303: /* --post303 */ + config->post303 = toggle; + break; + case C_NOPROXY: /* --noproxy */ + /* This specifies the noproxy list */ + err = getstr(&config->noproxy, nextarg, ALLOW_BLANK); + break; + case C_SOCKS5_GSSAPI_NEC: /* --socks5-gssapi-nec */ + config->socks5_gssapi_nec = toggle; + break; + case C_PROXY1_0: /* --proxy1.0 */ + /* http 1.0 proxy */ + err = getstr(&config->proxy, nextarg, DENY_BLANK); + config->proxyver = CURLPROXY_HTTP_1_0; + break; + case C_TFTP_BLKSIZE: /* --tftp-blksize */ + err = str2unum(&config->tftp_blksize, nextarg); + break; + case C_MAIL_FROM: /* --mail-from */ + err = getstr(&config->mail_from, nextarg, DENY_BLANK); + break; + case C_MAIL_RCPT: /* --mail-rcpt */ + /* append receiver to a list */ + err = add2list(&config->mail_rcpt, nextarg); + break; + case C_FTP_PRET: /* --ftp-pret */ + config->ftp_pret = toggle; + break; + case C_PROTO: /* --proto */ + config->proto_present = TRUE; + err = proto2num(config, built_in_protos, &config->proto_str, nextarg); + break; + case C_PROTO_REDIR: /* --proto-redir */ + config->proto_redir_present = TRUE; + if(proto2num(config, redir_protos, &config->proto_redir_str, + nextarg)) + err = PARAM_BAD_USE; + break; + case C_RESOLVE: /* --resolve */ + err = add2list(&config->resolve, nextarg); + break; + case C_DELEGATION: /* --delegation */ + config->gssapi_delegation = delegation(config, nextarg); + break; + case C_MAIL_AUTH: /* --mail-auth */ + err = getstr(&config->mail_auth, nextarg, DENY_BLANK); + break; + case C_METALINK: /* --metalink */ + errorf(global, "--metalink is disabled"); + err = PARAM_BAD_USE; + break; + case C_SASL_AUTHZID: /* --sasl-authzid */ + err = getstr(&config->sasl_authzid, nextarg, DENY_BLANK); + break; + case C_SASL_IR: /* --sasl-ir */ + config->sasl_ir = toggle; + break; + case C_TEST_EVENT: /* --test-event */ +#ifdef DEBUGBUILD + global->test_event_based = toggle; +#else + warnf(global, "--test-event is ignored unless a debug build"); +#endif + break; + case C_UNIX_SOCKET: /* --unix-socket */ + config->abstract_unix_socket = FALSE; + err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); + break; + case C_PATH_AS_IS: /* --path-as-is */ + config->path_as_is = toggle; + break; + case C_PROXY_SERVICE_NAME: /* --proxy-service-name */ + err = getstr(&config->proxy_service_name, nextarg, DENY_BLANK); + break; + case C_SERVICE_NAME: /* --service-name */ + err = getstr(&config->service_name, nextarg, DENY_BLANK); + break; + case C_PROTO_DEFAULT: /* --proto-default */ + err = getstr(&config->proto_default, nextarg, DENY_BLANK); + if(!err) + err = check_protocol(config->proto_default); + break; + case C_EXPECT100_TIMEOUT: /* --expect100-timeout */ + err = secs2ms(&config->expect100timeout_ms, nextarg); + break; + case C_TFTP_NO_OPTIONS: /* --tftp-no-options */ + config->tftp_no_options = toggle; + break; + case C_CONNECT_TO: /* --connect-to */ + err = add2list(&config->connect_to, nextarg); + break; + case C_ABSTRACT_UNIX_SOCKET: /* --abstract-unix-socket */ + config->abstract_unix_socket = TRUE; + err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); + break; + case C_TLS_MAX: /* --tls-max */ + err = str2tls_max(&config->ssl_version_max, nextarg); + break; + case C_SUPPRESS_CONNECT_HEADERS: /* --suppress-connect-headers */ + config->suppress_connect_headers = toggle; + break; + case C_COMPRESSED_SSH: /* --compressed-ssh */ + config->ssh_compression = toggle; + break; + case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */ + err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg); + /* 0 is a valid value for this timeout */ + break; + case C_TRACE_IDS: /* --trace-ids */ + global->traceids = toggle; + break; + case C_TRACE_CONFIG: /* --trace-config */ + if(set_trace_config(global, nextarg)) + err = PARAM_NO_MEM; + break; + case C_PROGRESS_METER: /* --progress-meter */ + global->noprogress = !toggle; + break; + case C_PROGRESS_BAR: /* --progress-bar */ + global->progressmode = toggle ? CURL_PROGRESS_BAR : CURL_PROGRESS_STATS; + break; + case C_VARIABLE: /* --variable */ + err = setvariable(global, nextarg); + break; + case C_NEXT: /* --next */ + err = PARAM_NEXT_OPERATION; + break; + case C_HTTP1_0: /* --http1.0 */ + /* HTTP version 1.0 */ + sethttpver(global, config, CURL_HTTP_VERSION_1_0); + break; + case C_HTTP1_1: /* --http1.1 */ + /* HTTP version 1.1 */ + sethttpver(global, config, CURL_HTTP_VERSION_1_1); + break; + case C_HTTP2: /* --http2 */ + /* HTTP version 2.0 */ + if(!feature_http2) + return PARAM_LIBCURL_DOESNT_SUPPORT; + sethttpver(global, config, CURL_HTTP_VERSION_2_0); + break; + case C_HTTP2_PRIOR_KNOWLEDGE: /* --http2-prior-knowledge */ + /* HTTP version 2.0 over clean TCP */ + if(!feature_http2) + return PARAM_LIBCURL_DOESNT_SUPPORT; + sethttpver(global, config, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); + break; + case C_HTTP3: /* --http3: */ + /* Try HTTP/3, allow fallback */ + if(!feature_http3) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + sethttpver(global, config, CURL_HTTP_VERSION_3); + break; + case C_HTTP3_ONLY: /* --http3-only */ + /* Try HTTP/3 without fallback */ + if(!feature_http3) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + sethttpver(global, config, CURL_HTTP_VERSION_3ONLY); + break; + case C_HTTP0_9: /* --http0.9 */ + /* Allow HTTP/0.9 responses! */ + config->http09_allowed = toggle; + break; + case C_PROXY_HTTP2: /* --proxy-http2 */ + if(!feature_httpsproxy || !feature_http2) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + config->proxyver = CURLPROXY_HTTPS2; + break; + case C_TLSV1: /* --tlsv1 */ + config->ssl_version = CURL_SSLVERSION_TLSv1; + break; + case C_TLSV1_0: /* --tlsv1.0 */ + config->ssl_version = CURL_SSLVERSION_TLSv1_0; + break; + case C_TLSV1_1: /* --tlsv1.1 */ + config->ssl_version = CURL_SSLVERSION_TLSv1_1; + break; + case C_TLSV1_2: /* --tlsv1.2 */ + config->ssl_version = CURL_SSLVERSION_TLSv1_2; + break; + case C_TLSV1_3: /* --tlsv1.3 */ + config->ssl_version = CURL_SSLVERSION_TLSv1_3; + break; + case C_TLS13_CIPHERS: /* --tls13-ciphers */ + err = getstr(&config->cipher13_list, nextarg, DENY_BLANK); + break; + case C_PROXY_TLS13_CIPHERS: /* --proxy-tls13-ciphers */ + err = getstr(&config->proxy_cipher13_list, nextarg, DENY_BLANK); + break; + case C_SSLV2: /* --sslv2 */ + warnf(global, "Ignores instruction to use SSLv2"); + break; + case C_SSLV3: /* --sslv3 */ + warnf(global, "Ignores instruction to use SSLv3"); + break; + case C_IPV4: /* --ipv4 */ + config->ip_version = CURL_IPRESOLVE_V4; + break; + case C_IPV6: /* --ipv6 */ + config->ip_version = CURL_IPRESOLVE_V6; + break; + case C_APPEND: /* --append */ + /* This makes the FTP sessions use APPE instead of STOR */ + config->ftp_append = toggle; + break; + case C_USER_AGENT: /* --user-agent */ + err = getstr(&config->useragent, nextarg, ALLOW_BLANK); + break; + case C_ALT_SVC: /* --alt-svc */ + if(!feature_altsvc) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->altsvc, nextarg, ALLOW_BLANK); + break; + case C_HSTS: /* --hsts */ + if(!feature_hsts) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->hsts, nextarg, ALLOW_BLANK); + break; + case C_COOKIE: /* --cookie */ + if(strchr(nextarg, '=')) { + /* A cookie string must have a =-letter */ + err = add2list(&config->cookies, nextarg); + break; + } + else { + /* We have a cookie file to read from! */ + err = add2list(&config->cookiefiles, nextarg); + } + break; + case C_USE_ASCII: /* --use-ascii */ + config->use_ascii = toggle; + break; + case C_COOKIE_JAR: /* --cookie-jar */ + err = getstr(&config->cookiejar, nextarg, DENY_BLANK); + break; + case C_CONTINUE_AT: /* --continue-at */ + /* This makes us continue an ftp transfer at given position */ + if(strcmp(nextarg, "-")) { + err = str2offset(&config->resume_from, nextarg); + config->resume_from_current = FALSE; + } + else { + config->resume_from_current = TRUE; + config->resume_from = 0; + } + config->use_resume = TRUE; + break; + case C_DATA: /* --data */ + case C_DATA_ASCII: /* --data-ascii */ + case C_DATA_BINARY: /* --data-binary */ + case C_DATA_URLENCODE: /* --data-urlencode */ + case C_JSON: /* --json */ + case C_DATA_RAW: /* --data-raw */ + err = set_data(cmd, nextarg, global, config); + break; + case C_URL_QUERY: /* --url-query */ + err = url_query(nextarg, global, config); + break; + case C_DUMP_HEADER: /* --dump-header */ + err = getstr(&config->headerfile, nextarg, DENY_BLANK); + break; + case C_REFERER: { /* --referer */ + char *ptr = strstr(nextarg, ";auto"); + if(ptr) { + /* Automatic referer requested, this may be combined with a + set initial one */ + config->autoreferer = TRUE; + *ptr = 0; /* null-terminate here */ + } + else + config->autoreferer = FALSE; + ptr = *nextarg ? nextarg : NULL; + err = getstr(&config->referer, ptr, ALLOW_BLANK); + } + break; + case C_CERT: /* --cert */ + cleanarg(clearthis); + GetFileAndPassword(nextarg, &config->cert, &config->key_passwd); + break; + case C_CACERT: /* --cacert */ + err = getstr(&config->cacert, nextarg, DENY_BLANK); + break; + case C_CA_NATIVE: /* --ca-native */ + config->native_ca_store = toggle; + break; + case C_PROXY_CA_NATIVE: /* --proxy-ca-native */ + config->proxy_native_ca_store = toggle; + break; + case C_CERT_TYPE: /* --cert-type */ + err = getstr(&config->cert_type, nextarg, DENY_BLANK); + break; + case C_KEY: /* --key */ + err = getstr(&config->key, nextarg, DENY_BLANK); + break; + case C_KEY_TYPE: /* --key-type */ + err = getstr(&config->key_type, nextarg, DENY_BLANK); + break; + case C_PASS: /* --pass */ + err = getstr(&config->key_passwd, nextarg, DENY_BLANK); + cleanarg(clearthis); + break; + case C_ENGINE: /* --engine */ + err = getstr(&config->engine, nextarg, DENY_BLANK); + if(!err && + config->engine && !strcmp(config->engine, "list")) { + err = PARAM_ENGINES_REQUESTED; + } + break; +#ifndef USE_ECH + case C_ECH: /* --ech, not implemented by default */ + err = PARAM_LIBCURL_DOESNT_SUPPORT; + break; +#else + case C_ECH: /* --ech */ + if(strlen(nextarg) > 4 && strncasecompare("pn:", nextarg, 3)) { + /* a public_name */ + err = getstr(&config->ech_public, nextarg, DENY_BLANK); + } + else if(strlen(nextarg) > 5 && strncasecompare("ecl:", nextarg, 4)) { + /* an ECHConfigList */ + if('@' != *(nextarg + 4)) { + err = getstr(&config->ech_config, nextarg, DENY_BLANK); + } + else { + /* Indirect case: @filename or @- for stdin */ + char *tmpcfg = NULL; + FILE *file; + + nextarg++; /* skip over '@' */ + if(!strcmp("-", nextarg)) { + file = stdin; + } + else { + file = fopen(nextarg, FOPEN_READTEXT); + } + if(!file) { + warnf(global, + "Couldn't read file \"%s\" " + "specified for \"--ech ecl:\" option", + nextarg); + return PARAM_BAD_USE; /* */ + } + err = file2string(&tmpcfg, file); + if(file != stdin) + fclose(file); + if(err) + return err; + config->ech_config = aprintf("ecl:%s",tmpcfg); + if(!config->ech_config) + return PARAM_NO_MEM; + free(tmpcfg); + } /* file done */ + } + else { + /* Simple case: just a string, with a keyword */ + err = getstr(&config->ech, nextarg, DENY_BLANK); + } + break; +#endif + case C_CAPATH: /* --capath */ + err = getstr(&config->capath, nextarg, DENY_BLANK); + break; + case C_PUBKEY: /* --pubkey */ + err = getstr(&config->pubkey, nextarg, DENY_BLANK); + break; + case C_HOSTPUBMD5: /* --hostpubmd5 */ + err = getstr(&config->hostpubmd5, nextarg, DENY_BLANK); + if(!err) { + if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32) + err = PARAM_BAD_USE; + } + break; + case C_HOSTPUBSHA256: /* --hostpubsha256 */ + err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK); + break; + case C_CRLFILE: /* --crlfile */ + err = getstr(&config->crlfile, nextarg, DENY_BLANK); + break; + case C_TLSUSER: /* --tlsuser */ + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->tls_username, nextarg, DENY_BLANK); + cleanarg(clearthis); + break; + case C_TLSPASSWORD: /* --tlspassword */ + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->tls_password, nextarg, ALLOW_BLANK); + cleanarg(clearthis); + break; + case C_TLSAUTHTYPE: /* --tlsauthtype */ + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else { + err = getstr(&config->tls_authtype, nextarg, DENY_BLANK); + if(!err && strcmp(config->tls_authtype, "SRP")) + err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ + } + break; + case C_SSL_ALLOW_BEAST: /* --ssl-allow-beast */ + if(feature_ssl) + config->ssl_allow_beast = toggle; + break; + case C_SSL_AUTO_CLIENT_CERT: /* --ssl-auto-client-cert */ + if(feature_ssl) + config->ssl_auto_client_cert = toggle; + break; + case C_PROXY_SSL_AUTO_CLIENT_CERT: /* --proxy-ssl-auto-client-cert */ + if(feature_ssl) + config->proxy_ssl_auto_client_cert = toggle; + break; + case C_PINNEDPUBKEY: /* --pinnedpubkey */ + err = getstr(&config->pinnedpubkey, nextarg, DENY_BLANK); + break; + case C_PROXY_PINNEDPUBKEY: /* --proxy-pinnedpubkey */ + err = getstr(&config->proxy_pinnedpubkey, nextarg, DENY_BLANK); + break; + case C_CERT_STATUS: /* --cert-status */ + config->verifystatus = TRUE; + break; + case C_DOH_CERT_STATUS: /* --doh-cert-status */ + config->doh_verifystatus = TRUE; + break; + case C_FALSE_START: /* --false-start */ + config->falsestart = TRUE; + break; + case C_SSL_NO_REVOKE: /* --ssl-no-revoke */ + if(feature_ssl) + config->ssl_no_revoke = TRUE; + break; + case C_SSL_REVOKE_BEST_EFFORT: /* --ssl-revoke-best-effort */ + if(feature_ssl) + config->ssl_revoke_best_effort = TRUE; + break; + case C_TCP_FASTOPEN: /* --tcp-fastopen */ + config->tcp_fastopen = TRUE; + break; + case C_PROXY_TLSUSER: /* --proxy-tlsuser */ + cleanarg(clearthis); + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->proxy_tls_username, nextarg, ALLOW_BLANK); + break; + case C_PROXY_TLSPASSWORD: /* --proxy-tlspassword */ + cleanarg(clearthis); + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->proxy_tls_password, nextarg, DENY_BLANK); + break; + case C_PROXY_TLSAUTHTYPE: /* --proxy-tlsauthtype */ + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else { + err = getstr(&config->proxy_tls_authtype, nextarg, DENY_BLANK); + if(!err && strcmp(config->proxy_tls_authtype, "SRP")) + err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ + } + break; + case C_PROXY_CERT: /* --proxy-cert */ + cleanarg(clearthis); + GetFileAndPassword(nextarg, &config->proxy_cert, + &config->proxy_key_passwd); + break; + case C_PROXY_CERT_TYPE: /* --proxy-cert-type */ + err = getstr(&config->proxy_cert_type, nextarg, DENY_BLANK); + break; + case C_PROXY_KEY: /* --proxy-key */ + err = getstr(&config->proxy_key, nextarg, ALLOW_BLANK); + break; + case C_PROXY_KEY_TYPE: /* --proxy-key-type */ + err = getstr(&config->proxy_key_type, nextarg, DENY_BLANK); + break; + case C_PROXY_PASS: /* --proxy-pass */ + err = getstr(&config->proxy_key_passwd, nextarg, ALLOW_BLANK); + cleanarg(clearthis); + break; + case C_PROXY_CIPHERS: /* --proxy-ciphers */ + err = getstr(&config->proxy_cipher_list, nextarg, DENY_BLANK); + break; + case C_PROXY_CRLFILE: /* --proxy-crlfile */ + err = getstr(&config->proxy_crlfile, nextarg, DENY_BLANK); + break; + case C_PROXY_SSL_ALLOW_BEAST: /* --proxy-ssl-allow-beast */ + if(feature_ssl) + config->proxy_ssl_allow_beast = toggle; + break; + case C_LOGIN_OPTIONS: /* --login-options */ + err = getstr(&config->login_options, nextarg, ALLOW_BLANK); + break; + case C_PROXY_CACERT: /* --proxy-cacert */ + err = getstr(&config->proxy_cacert, nextarg, DENY_BLANK); + break; + case C_PROXY_CAPATH: /* --proxy-capath */ + err = getstr(&config->proxy_capath, nextarg, DENY_BLANK); + break; + case C_PROXY_INSECURE: /* --proxy-insecure */ + config->proxy_insecure_ok = toggle; + break; + case C_PROXY_TLSV1: /* --proxy-tlsv1 */ + /* TLS version 1 for proxy */ + config->proxy_ssl_version = CURL_SSLVERSION_TLSv1; + break; + case C_SOCKS5_BASIC: /* --socks5-basic */ + if(toggle) + config->socks5_auth |= CURLAUTH_BASIC; + else + config->socks5_auth &= ~CURLAUTH_BASIC; + break; + case C_SOCKS5_GSSAPI: /* --socks5-gssapi */ + if(toggle) + config->socks5_auth |= CURLAUTH_GSSAPI; + else + config->socks5_auth &= ~CURLAUTH_GSSAPI; + break; + case C_ETAG_SAVE: /* --etag-save */ + err = getstr(&config->etag_save_file, nextarg, DENY_BLANK); + break; + case C_ETAG_COMPARE: /* --etag-compare */ + err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK); + break; + case C_CURVES: /* --curves */ + err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK); + break; + case C_FAIL_EARLY: /* --fail-early */ + global->fail_early = toggle; + break; + case C_STYLED_OUTPUT: /* --styled-output */ + global->styled_output = toggle; + break; + case C_MAIL_RCPT_ALLOWFAILS: /* --mail-rcpt-allowfails */ + config->mail_rcpt_allowfails = toggle; + break; + case C_FAIL_WITH_BODY: /* --fail-with-body */ + config->failwithbody = toggle; + if(config->failonerror && config->failwithbody) { + errorf(config->global, "You must select either --fail or " + "--fail-with-body, not both."); + err = PARAM_BAD_USE; + } + break; + case C_REMOVE_ON_ERROR: /* --remove-on-error */ + config->rm_partial = toggle; + break; + case C_FAIL: /* --fail */ + config->failonerror = toggle; + if(config->failonerror && config->failwithbody) { + errorf(config->global, "You must select either --fail or " + "--fail-with-body, not both."); + err = PARAM_BAD_USE; + } + break; + case C_FORM: /* --form */ + case C_FORM_STRING: /* --form-string */ + /* "form data" simulation, this is a little advanced so lets do our best + to sort this out slowly and carefully */ + if(formparse(config, + nextarg, + &config->mimeroot, + &config->mimecurrent, + (cmd == C_FORM_STRING)?TRUE:FALSE)) /* literal string */ + err = PARAM_BAD_USE; + else if(SetHTTPrequest(config, HTTPREQ_MIMEPOST, &config->httpreq)) + err = PARAM_BAD_USE; + break; + case C_GLOBOFF: /* --globoff */ + config->globoff = toggle; + break; + case C_GET: /* --get */ + config->use_httpget = toggle; + break; + case C_REQUEST_TARGET: /* --request-target */ + err = getstr(&config->request_target, nextarg, DENY_BLANK); + break; + case C_HELP: /* --help */ + if(toggle) { + if(*nextarg) { + global->help_category = strdup(nextarg); + if(!global->help_category) { + err = PARAM_NO_MEM; + break; + } + } + err = PARAM_HELP_REQUESTED; + } + /* we now actually support --no-help too! */ + break; + case C_HEADER: /* --header */ + case C_PROXY_HEADER: /* --proxy-header */ + /* A custom header to append to a list */ + if(nextarg[0] == '@') { + /* read many headers from a file or stdin */ + char *string; + size_t len; + bool use_stdin = !strcmp(&nextarg[1], "-"); + FILE *file = use_stdin?stdin:fopen(&nextarg[1], FOPEN_READTEXT); + if(!file) { + errorf(global, "Failed to open %s", &nextarg[1]); + err = PARAM_READ_ERROR; + } + else { + err = file2memory(&string, &len, file); + if(!err && string) { + /* Allow strtok() here since this is not used threaded */ + /* !checksrc! disable BANNEDFUNC 2 */ + char *h = strtok(string, "\r\n"); + while(h) { + if(cmd == C_PROXY_HEADER) /* --proxy-header */ + err = add2list(&config->proxyheaders, h); + else + err = add2list(&config->headers, h); + if(err) + break; + h = strtok(NULL, "\r\n"); + } + free(string); + } + if(!use_stdin) + fclose(file); + } + } + else { + if(cmd == C_PROXY_HEADER) /* --proxy-header */ + err = add2list(&config->proxyheaders, nextarg); + else + err = add2list(&config->headers, nextarg); + } + break; + case C_INCLUDE: /* --include */ + config->show_headers = toggle; /* show the headers as well in the + general output stream */ + break; + case C_JUNK_SESSION_COOKIES: /* --junk-session-cookies */ + config->cookiesession = toggle; + break; + case C_HEAD: /* --head */ + config->no_body = toggle; + config->show_headers = toggle; + if(SetHTTPrequest(config, + (config->no_body)?HTTPREQ_HEAD:HTTPREQ_GET, + &config->httpreq)) + err = PARAM_BAD_USE; + break; + case C_REMOTE_HEADER_NAME: /* --remote-header-name */ + config->content_disposition = toggle; + break; + case C_INSECURE: /* --insecure */ + config->insecure_ok = toggle; + break; + case C_DOH_INSECURE: /* --doh-insecure */ + config->doh_insecure_ok = toggle; + break; + case C_CONFIG: /* --config */ + if(parseconfig(nextarg, global)) { + errorf(global, "cannot read config from '%s'", nextarg); + err = PARAM_READ_ERROR; + } + break; + case C_LIST_ONLY: /* --list-only */ + config->dirlistonly = toggle; /* only list the names of the FTP dir */ + break; + case C_LOCATION_TRUSTED: /* --location-trusted */ + /* Continue to send authentication (user+password) when following + * locations, even when hostname changed */ + config->unrestricted_auth = toggle; + FALLTHROUGH(); + case C_LOCATION: /* --location */ + config->followlocation = toggle; /* Follow Location: HTTP headers */ + break; + case C_MAX_TIME: /* --max-time */ + /* specified max time */ + err = secs2ms(&config->timeout_ms, nextarg); + break; + case C_MANUAL: /* --manual */ + if(toggle) { /* --no-manual shows no manual... */ +#ifndef USE_MANUAL + warnf(global, + "built-in manual was disabled at build-time"); +#endif + err = PARAM_MANUAL_REQUESTED; + } + break; + case C_NETRC_OPTIONAL: /* --netrc-optional */ + config->netrc_opt = toggle; + break; + case C_NETRC_FILE: /* --netrc-file */ + err = getstr(&config->netrc_file, nextarg, DENY_BLANK); + break; + case C_NETRC: /* --netrc */ + /* pick info from .netrc, if this is used for http, curl will + automatically enforce user+password with the request */ + config->netrc = toggle; + break; + case C_BUFFER: /* --buffer */ + /* disable the output I/O buffering. note that the option is called + --buffer but is mostly used in the negative form: --no-buffer */ + config->nobuffer = longopt ? !toggle : TRUE; + break; + case C_REMOTE_NAME_ALL: /* --remote-name-all */ + config->default_node_flags = toggle?GETOUT_USEREMOTE:0; + break; + case C_OUTPUT_DIR: /* --output-dir */ + err = getstr(&config->output_dir, nextarg, DENY_BLANK); + break; + case C_CLOBBER: /* --clobber */ + config->file_clobber_mode = toggle ? CLOBBER_ALWAYS : CLOBBER_NEVER; + break; + case C_OUTPUT: /* --output */ + case C_REMOTE_NAME: /* --remote-name */ + /* output file */ + if(!config->url_out) + config->url_out = config->url_list; + if(config->url_out) { + /* there is a node here, if it already is filled-in continue to find + an "empty" node */ + while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE)) + config->url_out = config->url_out->next; + } + + /* now there might or might not be an available node to fill in! */ + + if(config->url_out) + /* existing node */ + url = config->url_out; + else { + if(!toggle && !config->default_node_flags) + break; + /* there was no free node, create one! */ + config->url_out = url = new_getout(config); + } + + if(!url) { + err = PARAM_NO_MEM; + break; + } + + /* fill in the outfile */ + if('o' == letter) { + err = getstr(&url->outfile, nextarg, DENY_BLANK); + url->flags &= ~GETOUT_USEREMOTE; /* switch off */ + } + else { + url->outfile = NULL; /* leave it */ + if(toggle) + url->flags |= GETOUT_USEREMOTE; /* switch on */ + else + url->flags &= ~GETOUT_USEREMOTE; /* switch off */ + } + url->flags |= GETOUT_OUTFILE; + break; + case C_FTP_PORT: /* --ftp-port */ + /* This makes the FTP sessions use PORT instead of PASV */ + /* use or <192.168.10.10> style addresses. Anything except + this will make us try to get the "default" address. + NOTE: this is a changed behavior since the released 4.1! + */ + err = getstr(&config->ftpport, nextarg, DENY_BLANK); + break; + case C_PROXYTUNNEL: /* --proxytunnel */ + /* proxy tunnel for non-http protocols */ + config->proxytunnel = toggle; + break; + + case C_DISABLE: /* --disable */ + /* if used first, already taken care of, we do it like this so we do not + cause an error! */ + break; + case C_QUOTE: /* --quote */ + /* QUOTE command to send to FTP server */ + switch(nextarg[0]) { + case '-': + /* prefixed with a dash makes it a POST TRANSFER one */ + nextarg++; + err = add2list(&config->postquote, nextarg); + break; + case '+': + /* prefixed with a plus makes it a just-before-transfer one */ + nextarg++; + err = add2list(&config->prequote, nextarg); + break; + default: + err = add2list(&config->quote, nextarg); + break; + } + break; + case C_RANGE: /* --range */ + /* Specifying a range WITHOUT A DASH will create an illegal HTTP range + (and will not actually be range by definition). The manpage + previously claimed that to be a good way, why this code is added to + work-around it. */ + if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) { + char buffer[32]; + if(curlx_strtoofft(nextarg, NULL, 10, &value)) { + warnf(global, "unsupported range point"); + err = PARAM_BAD_USE; + } + else { + warnf(global, + "A specified range MUST include at least one dash (-). " + "Appending one for you"); + msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", + value); + Curl_safefree(config->range); + config->range = strdup(buffer); + if(!config->range) + err = PARAM_NO_MEM; + } + } + else { + /* byte range requested */ + const char *tmp_range = nextarg; + while(*tmp_range) { + if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') { + warnf(global, "Invalid character is found in given range. " + "A specified range MUST have only digits in " + "\'start\'-\'stop\'. The server's response to this " + "request is uncertain."); + break; + } + tmp_range++; + } + err = getstr(&config->range, nextarg, DENY_BLANK); + } + break; + case C_REMOTE_TIME: /* --remote-time */ + /* use remote file's time */ + config->remote_time = toggle; + break; + case C_SILENT: /* --silent */ + global->silent = toggle; + break; + case C_SHOW_ERROR: /* --show-error */ + global->showerror = toggle; + break; + case C_TELNET_OPTION: /* --telnet-option */ + /* Telnet options */ + err = add2list(&config->telnet_options, nextarg); + break; + case C_UPLOAD_FILE: /* --upload-file */ + /* we are uploading */ + if(!config->url_ul) + config->url_ul = config->url_list; + if(config->url_ul) { + /* there is a node here, if it already is filled-in continue to find + an "empty" node */ + while(config->url_ul && (config->url_ul->flags & GETOUT_UPLOAD)) + config->url_ul = config->url_ul->next; + } + + /* now there might or might not be an available node to fill in! */ + + if(config->url_ul) + /* existing node */ + url = config->url_ul; + else + /* there was no free node, create one! */ + config->url_ul = url = new_getout(config); + + if(!url) { + err = PARAM_NO_MEM; + break; + } + + url->flags |= GETOUT_UPLOAD; /* mark -T used */ + if(!*nextarg) + url->flags |= GETOUT_NOUPLOAD; + else { + /* "-" equals stdin, but keep the string around for now */ + err = getstr(&url->infile, nextarg, DENY_BLANK); + } + break; + case C_USER: /* --user */ + /* user:password */ + err = getstr(&config->userpwd, nextarg, ALLOW_BLANK); + cleanarg(clearthis); + break; + case C_PROXY_USER: /* --proxy-user */ + /* Proxy user:password */ + err = getstr(&config->proxyuserpwd, nextarg, ALLOW_BLANK); + cleanarg(clearthis); + break; + case C_VERBOSE: /* --verbose */ + if(toggle) { + /* the '%' thing here will cause the trace get sent to stderr */ + Curl_safefree(global->trace_dump); + global->trace_dump = strdup("%"); + if(!global->trace_dump) + err = PARAM_NO_MEM; + else { + if(global->tracetype && (global->tracetype != TRACE_PLAIN)) + warnf(global, + "-v, --verbose overrides an earlier trace/verbose option"); + global->tracetype = TRACE_PLAIN; + } + } + else + /* verbose is disabled here */ + global->tracetype = TRACE_NONE; + break; + case C_VERSION: /* --version */ + if(toggle) /* --no-version yields no output! */ + err = PARAM_VERSION_INFO_REQUESTED; + break; + case C_WRITE_OUT: /* --write-out */ + /* get the output string */ + if('@' == *nextarg) { + /* the data begins with a '@' letter, it means that a filename + or - (stdin) follows */ + FILE *file; + const char *fname; + nextarg++; /* pass the @ */ + if(!strcmp("-", nextarg)) { + fname = ""; + file = stdin; + } + else { + fname = nextarg; + file = fopen(fname, FOPEN_READTEXT); + if(!file) { + errorf(global, "Failed to open %s", fname); + err = PARAM_READ_ERROR; + break; + } + } + Curl_safefree(config->writeout); + err = file2string(&config->writeout, file); + if(file && (file != stdin)) + fclose(file); + if(err) + break; + if(!config->writeout) + warnf(global, "Failed to read %s", fname); + } + else + err = getstr(&config->writeout, nextarg, ALLOW_BLANK); + break; + case C_PREPROXY: /* --preproxy */ + err = getstr(&config->preproxy, nextarg, DENY_BLANK); + break; + case C_PROXY: /* --proxy */ + /* --proxy */ + err = getstr(&config->proxy, nextarg, ALLOW_BLANK); + if(config->proxyver != CURLPROXY_HTTPS2) + config->proxyver = CURLPROXY_HTTP; + break; + case C_REQUEST: /* --request */ + /* set custom request */ + err = getstr(&config->customrequest, nextarg, DENY_BLANK); + break; + case C_SPEED_TIME: /* --speed-time */ + /* low speed time */ + err = str2unum(&config->low_speed_time, nextarg); + if(!err && !config->low_speed_limit) + config->low_speed_limit = 1; + break; + case C_SPEED_LIMIT: /* --speed-limit */ + /* low speed limit */ + err = str2unum(&config->low_speed_limit, nextarg); + if(!err && !config->low_speed_time) + config->low_speed_time = 30; + break; + case C_PARALLEL: /* --parallel */ + global->parallel = toggle; + break; + case C_PARALLEL_MAX: { /* --parallel-max */ + long val; + err = str2unum(&val, nextarg); + if(err) + break; + if(val > MAX_PARALLEL) + global->parallel_max = MAX_PARALLEL; + else if(val < 1) + global->parallel_max = PARALLEL_DEFAULT; + else + global->parallel_max = (unsigned short)val; + break; + } + case C_PARALLEL_IMMEDIATE: /* --parallel-immediate */ + global->parallel_connect = toggle; + break; + case C_TIME_COND: /* --time-cond */ + switch(*nextarg) { + case '+': + nextarg++; + FALLTHROUGH(); + default: + /* If-Modified-Since: (section 14.28 in RFC2068) */ + config->timecond = CURL_TIMECOND_IFMODSINCE; + break; + case '-': + /* If-Unmodified-Since: (section 14.24 in RFC2068) */ + config->timecond = CURL_TIMECOND_IFUNMODSINCE; + nextarg++; + break; + case '=': + /* Last-Modified: (section 14.29 in RFC2068) */ + config->timecond = CURL_TIMECOND_LASTMOD; + nextarg++; + break; + } + now = time(NULL); + config->condtime = (curl_off_t)curl_getdate(nextarg, &now); + if(-1 == config->condtime) { + /* now let's see if it is a filename to get the time from instead! */ + rc = getfiletime(nextarg, global, &value); + if(!rc) + /* pull the time out from the file */ + config->condtime = value; + else { + /* failed, remove time condition */ + config->timecond = CURL_TIMECOND_NONE; + warnf(global, + "Illegal date format for -z, --time-cond (and not " + "a filename). Disabling time condition. " + "See curl_getdate(3) for valid date syntax."); + } + } + break; + case C_MPTCP: /* --mptcp */ + config->mptcp = TRUE; + break; + default: /* unknown flag */ + err = PARAM_OPTION_UNKNOWN; + break; + } + a = NULL; + + } while(!longopt && !singleopt && *++parse && !*usedarg && !err); + +error: + if(nextalloc) + free(nextarg); + return err; +} + +ParameterError parse_args(struct GlobalConfig *global, int argc, + argv_item_t argv[]) +{ + int i; + bool stillflags; + char *orig_opt = NULL; + ParameterError result = PARAM_OK; + struct OperationConfig *config = global->first; + + for(i = 1, stillflags = TRUE; i < argc && !result; i++) { + orig_opt = curlx_convert_tchar_to_UTF8(argv[i]); + if(!orig_opt) + return PARAM_NO_MEM; + + if(stillflags && ('-' == orig_opt[0])) { + bool passarg; + + if(!strcmp("--", orig_opt)) + /* This indicates the end of the flags and thus enables the + following (URL) argument to start with -. */ + stillflags = FALSE; + else { + char *nextarg = NULL; + if(i < (argc - 1)) { + nextarg = curlx_convert_tchar_to_UTF8(argv[i + 1]); + if(!nextarg) { + curlx_unicodefree(orig_opt); + return PARAM_NO_MEM; + } + } + + result = getparameter(orig_opt, nextarg, argv[i + 1], &passarg, + global, config); + + curlx_unicodefree(nextarg); + config = global->last; + if(result == PARAM_NEXT_OPERATION) { + /* Reset result as PARAM_NEXT_OPERATION is only used here and not + returned from this function */ + result = PARAM_OK; + + if(config->url_list && config->url_list->url) { + /* Allocate the next config */ + config->next = malloc(sizeof(struct OperationConfig)); + if(config->next) { + /* Initialise the newly created config */ + config_init(config->next); + + /* Set the global config pointer */ + config->next->global = global; + + /* Update the last config pointer */ + global->last = config->next; + + /* Move onto the new config */ + config->next->prev = config; + config = config->next; + } + else + result = PARAM_NO_MEM; + } + else { + errorf(global, "missing URL before --next"); + result = PARAM_BAD_USE; + } + } + else if(!result && passarg) + i++; /* we are supposed to skip this */ + } + } + else { + bool used; + + /* Just add the URL please */ + result = getparameter("--url", orig_opt, argv[i], &used, global, config); + } + + if(!result) + curlx_unicodefree(orig_opt); + } + + if(!result && config->content_disposition) { + if(config->show_headers) + result = PARAM_CONTDISP_SHOW_HEADER; + else if(config->resume_from_current) + result = PARAM_CONTDISP_RESUME_FROM; + } + + if(result && result != PARAM_HELP_REQUESTED && + result != PARAM_MANUAL_REQUESTED && + result != PARAM_VERSION_INFO_REQUESTED && + result != PARAM_ENGINES_REQUESTED) { + const char *reason = param2text(result); + + if(orig_opt && strcmp(":", orig_opt)) + helpf(tool_stderr, "option %s: %s", orig_opt, reason); + else + helpf(tool_stderr, "%s", reason); + } + + curlx_unicodefree(orig_opt); + return result; +} diff --git a/third_party/curl/src/tool_getparam.h b/third_party/curl/src/tool_getparam.h new file mode 100644 index 0000000000..12a971d027 --- /dev/null +++ b/third_party/curl/src/tool_getparam.h @@ -0,0 +1,74 @@ +#ifndef HEADER_CURL_TOOL_GETPARAM_H +#define HEADER_CURL_TOOL_GETPARAM_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +typedef enum { + PARAM_OK = 0, + PARAM_OPTION_AMBIGUOUS, + PARAM_OPTION_UNKNOWN, + PARAM_REQUIRES_PARAMETER, + PARAM_BAD_USE, + PARAM_HELP_REQUESTED, + PARAM_MANUAL_REQUESTED, + PARAM_VERSION_INFO_REQUESTED, + PARAM_ENGINES_REQUESTED, + PARAM_GOT_EXTRA_PARAMETER, + PARAM_BAD_NUMERIC, + PARAM_NEGATIVE_NUMERIC, + PARAM_LIBCURL_DOESNT_SUPPORT, + PARAM_LIBCURL_UNSUPPORTED_PROTOCOL, + PARAM_NO_MEM, + PARAM_NEXT_OPERATION, + PARAM_NO_PREFIX, + PARAM_NUMBER_TOO_LARGE, + PARAM_NO_NOT_BOOLEAN, + PARAM_CONTDISP_SHOW_HEADER, /* --include and --remote-header-name */ + PARAM_CONTDISP_RESUME_FROM, /* --continue-at and --remote-header-name */ + PARAM_READ_ERROR, + PARAM_EXPAND_ERROR, /* --expand problem */ + PARAM_BLANK_STRING, + PARAM_LAST +} ParameterError; + +struct GlobalConfig; +struct OperationConfig; + +ParameterError getparameter(const char *flag, char *nextarg, + argv_item_t cleararg, + bool *usedarg, + struct GlobalConfig *global, + struct OperationConfig *operation); + +#ifdef UNITTESTS +void parse_cert_parameter(const char *cert_parameter, + char **certname, + char **passphrase); +#endif + +ParameterError parse_args(struct GlobalConfig *config, int argc, + argv_item_t argv[]); + +#endif /* HEADER_CURL_TOOL_GETPARAM_H */ diff --git a/third_party/curl/src/tool_getpass.c b/third_party/curl/src/tool_getpass.c new file mode 100644 index 0000000000..412c50a114 --- /dev/null +++ b/third_party/curl/src/tool_getpass.c @@ -0,0 +1,207 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#if defined(__AMIGA__) && !defined(__amigaos4__) +# undef HAVE_TERMIOS_H +#endif + +#ifndef HAVE_GETPASS_R +/* this file is only for systems without getpass_r() */ + +#ifdef HAVE_FCNTL_H +# include +#endif + +#ifdef HAVE_TERMIOS_H +# include +#elif defined(HAVE_TERMIO_H) +# include +#endif + +#ifdef __VMS +# include descrip +# include starlet +# include iodef +#endif + +#ifdef _WIN32 +# include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#include "tool_getpass.h" + +#include "memdebug.h" /* keep this as LAST include */ + +#ifdef __VMS +/* VMS implementation */ +char *getpass_r(const char *prompt, char *buffer, size_t buflen) +{ + long sts; + short chan; + + /* MSK, 23-JAN-2004, iosbdef.h was not in VAX V7.2 or CC 6.4 */ + /* distribution so I created this. May revert back later to */ + /* struct _iosb iosb; */ + struct _iosb + { + short int iosb$w_status; /* status */ + short int iosb$w_bcnt; /* byte count */ + int unused; /* unused */ + } iosb; + + $DESCRIPTOR(ttdesc, "TT"); + + buffer[0] = '\0'; + sts = sys$assign(&ttdesc, &chan, 0, 0); + if(sts & 1) { + sts = sys$qiow(0, chan, + IO$_READPROMPT | IO$M_NOECHO, + &iosb, 0, 0, buffer, buflen, 0, 0, + prompt, strlen(prompt)); + + if((sts & 1) && (iosb.iosb$w_status & 1)) + buffer[iosb.iosb$w_bcnt] = '\0'; + + sys$dassgn(chan); + } + return buffer; /* we always return success */ +} +#define DONE +#endif /* __VMS */ + +#if defined(_WIN32) + +char *getpass_r(const char *prompt, char *buffer, size_t buflen) +{ + size_t i; + fputs(prompt, tool_stderr); + + for(i = 0; i < buflen; i++) { + buffer[i] = (char)getch(); + if(buffer[i] == '\r' || buffer[i] == '\n') { + buffer[i] = '\0'; + break; + } + else + if(buffer[i] == '\b') + /* remove this letter and if this is not the first key, remove the + previous one as well */ + i = i - (i >= 1 ? 2 : 1); + } + /* since echo is disabled, print a newline */ + fputs("\n", tool_stderr); + /* if user did not hit ENTER, terminate buffer */ + if(i == buflen) + buffer[buflen-1] = '\0'; + + return buffer; /* we always return success */ +} +#define DONE +#endif /* _WIN32 */ + +#ifndef DONE /* not previously provided */ + +#ifdef HAVE_TERMIOS_H +# define struct_term struct termios +#elif defined(HAVE_TERMIO_H) +# define struct_term struct termio +#else +# undef struct_term +#endif + +static bool ttyecho(bool enable, int fd) +{ +#ifdef struct_term + static struct_term withecho; + static struct_term noecho; +#endif + if(!enable) { + /* disable echo by extracting the current 'withecho' mode and remove the + ECHO bit and set back the struct */ +#ifdef HAVE_TERMIOS_H + tcgetattr(fd, &withecho); + noecho = withecho; + noecho.c_lflag &= ~(tcflag_t)ECHO; + tcsetattr(fd, TCSANOW, &noecho); +#elif defined(HAVE_TERMIO_H) + ioctl(fd, TCGETA, &withecho); + noecho = withecho; + noecho.c_lflag &= ~(tcflag_t)ECHO; + ioctl(fd, TCSETA, &noecho); +#else + /* neither HAVE_TERMIO_H nor HAVE_TERMIOS_H, we cannot disable echo! */ + (void)fd; + return FALSE; /* not disabled */ +#endif + return TRUE; /* disabled */ + } + /* re-enable echo, assumes we disabled it before (and set the structs we + now use to reset the terminal status) */ +#ifdef HAVE_TERMIOS_H + tcsetattr(fd, TCSAFLUSH, &withecho); +#elif defined(HAVE_TERMIO_H) + ioctl(fd, TCSETA, &withecho); +#else + return FALSE; /* not enabled */ +#endif + return TRUE; /* enabled */ +} + +char *getpass_r(const char *prompt, /* prompt to display */ + char *password, /* buffer to store password in */ + size_t buflen) /* size of buffer to store password in */ +{ + ssize_t nread; + bool disabled; + int fd = open("/dev/tty", O_RDONLY); + if(-1 == fd) + fd = STDIN_FILENO; /* use stdin if the tty could not be used */ + + disabled = ttyecho(FALSE, fd); /* disable terminal echo */ + + fputs(prompt, tool_stderr); + nread = read(fd, password, buflen); + if(nread > 0) + password[--nread] = '\0'; /* null-terminate where enter is stored */ + else + password[0] = '\0'; /* got nothing */ + + if(disabled) { + /* if echo actually was disabled, add a newline */ + fputs("\n", tool_stderr); + (void)ttyecho(TRUE, fd); /* enable echo */ + } + + if(STDIN_FILENO != fd) + close(fd); + + return password; /* return pointer to buffer */ +} + +#endif /* DONE */ +#endif /* HAVE_GETPASS_R */ diff --git a/third_party/curl/src/tool_getpass.h b/third_party/curl/src/tool_getpass.h new file mode 100644 index 0000000000..0a4d6d5a83 --- /dev/null +++ b/third_party/curl/src/tool_getpass.h @@ -0,0 +1,38 @@ +#ifndef HEADER_CURL_TOOL_GETPASS_H +#define HEADER_CURL_TOOL_GETPASS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#ifndef HAVE_GETPASS_R +/* If there is a system-provided function named like this, we trust it is + also found in one of the standard headers. */ + +/* + * Returning NULL will abort the continued operation! + */ +char *getpass_r(const char *prompt, char *buffer, size_t buflen); +#endif + +#endif /* HEADER_CURL_TOOL_GETPASS_H */ diff --git a/third_party/curl/src/tool_help.c b/third_party/curl/src/tool_help.c new file mode 100644 index 0000000000..a12626eff3 --- /dev/null +++ b/third_party/curl/src/tool_help.c @@ -0,0 +1,278 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_help.h" +#include "tool_libinfo.h" +#include "tool_util.h" +#include "tool_version.h" +#include "tool_cb_prg.h" +#include "terminal.h" + +#include "memdebug.h" /* keep this as LAST include */ + +#ifdef MSDOS +# define USE_WATT32 +#endif + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +struct category_descriptors { + const char *opt; + const char *desc; + unsigned int category; +}; + +static const struct category_descriptors categories[] = { + /* important is left out because it is the default help page */ + {"auth", "Authentication methods", CURLHELP_AUTH}, + {"connection", "Manage connections", CURLHELP_CONNECTION}, + {"curl", "The command line tool itself", CURLHELP_CURL}, + {"deprecated", "Legacy", CURLHELP_DEPRECATED}, + {"dns", "Names and resolving", CURLHELP_DNS}, + {"file", "FILE protocol", CURLHELP_FILE}, + {"ftp", "FTP protocol", CURLHELP_FTP}, + {"global", "Global options", CURLHELP_GLOBAL}, + {"http", "HTTP and HTTPS protocol", CURLHELP_HTTP}, + {"imap", "IMAP protocol", CURLHELP_IMAP}, + {"ldap", "LDAP protocol", CURLHELP_LDAP}, + {"output", "Filesystem output", CURLHELP_OUTPUT}, + {"pop3", "POP3 protocol", CURLHELP_POP3}, + {"post", "HTTP POST specific", CURLHELP_POST}, + {"proxy", "Options for proxies", CURLHELP_PROXY}, + {"scp", "SCP protocol", CURLHELP_SCP}, + {"sftp", "SFTP protocol", CURLHELP_SFTP}, + {"smtp", "SMTP protocol", CURLHELP_SMTP}, + {"ssh", "SSH protocol", CURLHELP_SSH}, + {"telnet", "TELNET protocol", CURLHELP_TELNET}, + {"tftp", "TFTP protocol", CURLHELP_TFTP}, + {"timeout", "Timeouts and delays", CURLHELP_TIMEOUT}, + {"tls", "TLS/SSL related", CURLHELP_TLS}, + {"upload", "Upload, sending data", CURLHELP_UPLOAD}, + {"verbose", "Tracing, logging etc", CURLHELP_VERBOSE} +}; + +static void print_category(unsigned int category, unsigned int cols) +{ + unsigned int i; + size_t longopt = 5; + size_t longdesc = 5; + + for(i = 0; helptext[i].opt; ++i) { + size_t len; + if(!(helptext[i].categories & category)) + continue; + len = strlen(helptext[i].opt); + if(len > longopt) + longopt = len; + len = strlen(helptext[i].desc); + if(len > longdesc) + longdesc = len; + } + if(longopt + longdesc > cols) + longopt = cols - longdesc; + + for(i = 0; helptext[i].opt; ++i) + if(helptext[i].categories & category) { + size_t opt = longopt; + size_t desclen = strlen(helptext[i].desc); + if(opt + desclen >= (cols - 2)) { + if(desclen < (cols - 2)) + opt = (cols - 3) - desclen; + else + opt = 0; + } + printf(" %-*s %s\n", (int)opt, helptext[i].opt, helptext[i].desc); + } +} + +/* Prints category if found. If not, it returns 1 */ +static int get_category_content(const char *category, unsigned int cols) +{ + unsigned int i; + for(i = 0; i < ARRAYSIZE(categories); ++i) + if(curl_strequal(categories[i].opt, category)) { + printf("%s: %s\n", categories[i].opt, categories[i].desc); + print_category(categories[i].category, cols); + return 0; + } + return 1; +} + +/* Prints all categories and their description */ +static void get_categories(void) +{ + unsigned int i; + for(i = 0; i < ARRAYSIZE(categories); ++i) + printf(" %-11s %s\n", categories[i].opt, categories[i].desc); +} + +/* Prints all categories as a comma-separated list of given width */ +static void get_categories_list(unsigned int width) +{ + unsigned int i; + size_t col = 0; + for(i = 0; i < ARRAYSIZE(categories); ++i) { + size_t len = strlen(categories[i].opt); + if(i == ARRAYSIZE(categories) - 1) { + /* final category */ + if(col + len + 1 < width) + printf("%s.\n", categories[i].opt); + else + /* start a new line first */ + printf("\n%s.\n", categories[i].opt); + } + else if(col + len + 2 < width) { + printf("%s, ", categories[i].opt); + col += len + 2; + } + else { + /* start a new line first */ + printf("\n%s, ", categories[i].opt); + col = len + 2; + } + } +} + + +void tool_help(char *category) +{ + unsigned int cols = get_terminal_columns(); + puts("Usage: curl [options...] "); + /* If no category was provided */ + if(!category) { + const char *category_note = "\nThis is not the full help; this " + "menu is split into categories.\nUse \"--help category\" to get " + "an overview of all categories, which are:"; + const char *category_note2 = "For all options use the manual" + " or \"--help all\"."; + print_category(CURLHELP_IMPORTANT, cols); + puts(category_note); + get_categories_list(cols); + puts(category_note2); + } + /* Lets print everything if "all" was provided */ + else if(curl_strequal(category, "all")) + /* Print everything */ + print_category(CURLHELP_ALL, cols); + /* Lets handle the string "category" differently to not print an errormsg */ + else if(curl_strequal(category, "category")) + get_categories(); + /* Otherwise print category and handle the case if the cat was not found */ + else if(get_category_content(category, cols)) { + puts("Unknown category provided, here is a list of all categories:\n"); + get_categories(); + } + free(category); +} + +static bool is_debug(void) +{ + const char *const *builtin; + for(builtin = feature_names; *builtin; ++builtin) + if(curl_strequal("debug", *builtin)) + return TRUE; + return FALSE; +} + +void tool_version_info(void) +{ + const char *const *builtin; + if(is_debug()) + fprintf(tool_stderr, "WARNING: this libcurl is Debug-enabled, " + "do not use in production\n\n"); + + printf(CURL_ID "%s\n", curl_version()); +#ifdef CURL_PATCHSTAMP + printf("Release-Date: %s, security patched: %s\n", + LIBCURL_TIMESTAMP, CURL_PATCHSTAMP); +#else + printf("Release-Date: %s\n", LIBCURL_TIMESTAMP); +#endif + if(built_in_protos[0]) { + const char *insert = NULL; + /* we have ipfs and ipns support if libcurl has http support */ + for(builtin = built_in_protos; *builtin; ++builtin) { + if(insert) { + /* update insertion so ipfs will be printed in alphabetical order */ + if(strcmp(*builtin, "ipfs") < 0) + insert = *builtin; + else + break; + } + else if(!strcmp(*builtin, "http")) { + insert = *builtin; + } + } + printf("Protocols:"); + for(builtin = built_in_protos; *builtin; ++builtin) { + /* Special case: do not list rtmp?* protocols. + They may only appear together with "rtmp" */ + if(!curl_strnequal(*builtin, "rtmp", 4) || !builtin[0][4]) + printf(" %s", *builtin); + if(insert && insert == *builtin) { + printf(" ipfs ipns"); + insert = NULL; + } + } + puts(""); /* newline */ + } + if(feature_names[0]) { + printf("Features:"); + for(builtin = feature_names; *builtin; ++builtin) + printf(" %s", *builtin); + puts(""); /* newline */ + } + if(strcmp(CURL_VERSION, curlinfo->version)) { + printf("WARNING: curl and libcurl versions do not match. " + "Functionality may be affected.\n"); + } +} + +void tool_list_engines(void) +{ + CURL *curl = curl_easy_init(); + struct curl_slist *engines = NULL; + + /* Get the list of engines */ + curl_easy_getinfo(curl, CURLINFO_SSL_ENGINES, &engines); + + puts("Build-time engines:"); + if(engines) { + for(; engines; engines = engines->next) + printf(" %s\n", engines->data); + } + else { + puts(" "); + } + + /* Cleanup the list of engines */ + curl_slist_free_all(engines); + curl_easy_cleanup(curl); +} diff --git a/third_party/curl/src/tool_help.h b/third_party/curl/src/tool_help.h new file mode 100644 index 0000000000..299da1db8c --- /dev/null +++ b/third_party/curl/src/tool_help.h @@ -0,0 +1,75 @@ +#ifndef HEADER_CURL_TOOL_HELP_H +#define HEADER_CURL_TOOL_HELP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +void tool_help(char *category); +void tool_list_engines(void); +void tool_version_info(void); + +struct helptxt { + const char *opt; + const char *desc; + unsigned int categories; +}; + +/* + * The bitmask output is generated with the following command + ------------------------------------------------------------ + make -C docs/cmdline-opts listcats + */ + +#define CURLHELP_AUTH (1u << 0u) +#define CURLHELP_CONNECTION (1u << 1u) +#define CURLHELP_CURL (1u << 2u) +#define CURLHELP_DEPRECATED (1u << 3u) +#define CURLHELP_DNS (1u << 4u) +#define CURLHELP_FILE (1u << 5u) +#define CURLHELP_FTP (1u << 6u) +#define CURLHELP_GLOBAL (1u << 7u) +#define CURLHELP_HTTP (1u << 8u) +#define CURLHELP_IMAP (1u << 9u) +#define CURLHELP_IMPORTANT (1u << 10u) +#define CURLHELP_LDAP (1u << 11u) +#define CURLHELP_OUTPUT (1u << 12u) +#define CURLHELP_POP3 (1u << 13u) +#define CURLHELP_POST (1u << 14u) +#define CURLHELP_PROXY (1u << 15u) +#define CURLHELP_SCP (1u << 16u) +#define CURLHELP_SFTP (1u << 17u) +#define CURLHELP_SMTP (1u << 18u) +#define CURLHELP_SSH (1u << 19u) +#define CURLHELP_TELNET (1u << 20u) +#define CURLHELP_TFTP (1u << 21u) +#define CURLHELP_TIMEOUT (1u << 22u) +#define CURLHELP_TLS (1u << 23u) +#define CURLHELP_UPLOAD (1u << 24u) +#define CURLHELP_VERBOSE (1u << 25u) + +#define CURLHELP_ALL (0xfffffffu) + +extern const struct helptxt helptext[]; + +#endif /* HEADER_CURL_TOOL_HELP_H */ diff --git a/third_party/curl/src/tool_helpers.c b/third_party/curl/src/tool_helpers.c new file mode 100644 index 0000000000..0f9ac45a8d --- /dev/null +++ b/third_party/curl/src/tool_helpers.c @@ -0,0 +1,135 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +#include "strcase.h" + +#define ENABLE_CURLX_PRINTF +/* use our own printf() functions */ +#include "curlx.h" + +#include "tool_cfgable.h" +#include "tool_msgs.h" +#include "tool_getparam.h" +#include "tool_helpers.h" + +#include "memdebug.h" /* keep this as LAST include */ + +/* +** Helper functions that are used from more than one source file. +*/ + +const char *param2text(ParameterError error) +{ + switch(error) { + case PARAM_GOT_EXTRA_PARAMETER: + return "had unsupported trailing garbage"; + case PARAM_OPTION_UNKNOWN: + return "is unknown"; + case PARAM_OPTION_AMBIGUOUS: + return "is ambiguous"; + case PARAM_REQUIRES_PARAMETER: + return "requires parameter"; + case PARAM_BAD_USE: + return "is badly used here"; + case PARAM_BAD_NUMERIC: + return "expected a proper numerical parameter"; + case PARAM_NEGATIVE_NUMERIC: + return "expected a positive numerical parameter"; + case PARAM_LIBCURL_DOESNT_SUPPORT: + return "the installed libcurl version does not support this"; + case PARAM_LIBCURL_UNSUPPORTED_PROTOCOL: + return "a specified protocol is unsupported by libcurl"; + case PARAM_NO_MEM: + return "out of memory"; + case PARAM_NO_PREFIX: + return "the given option cannot be reversed with a --no- prefix"; + case PARAM_NUMBER_TOO_LARGE: + return "too large number"; + case PARAM_NO_NOT_BOOLEAN: + return "used '--no-' for option that is not a boolean"; + case PARAM_CONTDISP_SHOW_HEADER: + return "showing headers and --remote-header-name cannot be combined"; + case PARAM_CONTDISP_RESUME_FROM: + return "--continue-at and --remote-header-name cannot be combined"; + case PARAM_READ_ERROR: + return "error encountered when reading a file"; + case PARAM_EXPAND_ERROR: + return "variable expansion failure"; + case PARAM_BLANK_STRING: + return "blank argument where content is expected"; + default: + return "unknown error"; + } +} + +int SetHTTPrequest(struct OperationConfig *config, HttpReq req, HttpReq *store) +{ + /* this mirrors the HttpReq enum in tool_sdecls.h */ + const char *reqname[]= { + "", /* unspec */ + "GET (-G, --get)", + "HEAD (-I, --head)", + "multipart formpost (-F, --form)", + "POST (-d, --data)", + "PUT (-T, --upload-file)" + }; + + if((*store == HTTPREQ_UNSPEC) || + (*store == req)) { + *store = req; + return 0; + } + warnf(config->global, "You can only select one HTTP request method! " + "You asked for both %s and %s.", + reqname[req], reqname[*store]); + + return 1; +} + +void customrequest_helper(struct OperationConfig *config, HttpReq req, + char *method) +{ + /* this mirrors the HttpReq enum in tool_sdecls.h */ + const char *dflt[]= { + "GET", + "GET", + "HEAD", + "POST", + "POST", + "PUT" + }; + + if(!method) + ; + else if(curl_strequal(method, dflt[req])) { + notef(config->global, "Unnecessary use of -X or --request, %s is already " + "inferred.", dflt[req]); + } + else if(curl_strequal(method, "head")) { + warnf(config->global, + "Setting custom HTTP method to HEAD with -X/--request may not work " + "the way you want. Consider using -I/--head instead."); + } +} diff --git a/third_party/curl/src/tool_helpers.h b/third_party/curl/src/tool_helpers.h new file mode 100644 index 0000000000..dd085e2cc7 --- /dev/null +++ b/third_party/curl/src/tool_helpers.h @@ -0,0 +1,36 @@ +#ifndef HEADER_CURL_TOOL_HELPERS_H +#define HEADER_CURL_TOOL_HELPERS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "tool_setup.h" + +const char *param2text(ParameterError error); + +int SetHTTPrequest(struct OperationConfig *config, HttpReq req, + HttpReq *store); + +void customrequest_helper(struct OperationConfig *config, HttpReq req, + char *method); + +#endif /* HEADER_CURL_TOOL_HELPERS_H */ diff --git a/third_party/curl/src/tool_hugehelp.c b/third_party/curl/src/tool_hugehelp.c new file mode 100644 index 0000000000..13ebe95ad3 --- /dev/null +++ b/third_party/curl/src/tool_hugehelp.c @@ -0,0 +1,10871 @@ +#include "tool_setup.h" +#ifndef HAVE_LIBZ +/* + * NEVER EVER edit this manually, fix the mkhelp.pl script instead! + */ +#ifdef USE_MANUAL +#include "tool_hugehelp.h" +static const char * const curlman[] = { + "\t _ _ ____ _", + " ___| | | | _ \\| |", + " / __| | | | |_) | |", + " | (__| |_| | _ <| |___", + " \\___|\\___/|_| \\_\\_____|", + "NAME", + "\n curl - transfer a URL", + "\nSYNOPSIS", + "\n curl [options / URLs]", + "\nDESCRIPTION", + "\n curl is a tool for transferring data from or to a server using URLs. It", + " supports these protocols: DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP,", + " HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, RTMP, RTMPS, RTSP,", + " SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS.", + "\n curl is powered by libcurl for all transfer-related features. See", + " libcurl(3) for details.", + "\nURL", + "\n The URL syntax is protocol-dependent. You find a detailed description in", + " RFC 3986.", + "\n If you provide a URL without a leading protocol:// scheme, curl guesses", + " what protocol you want. It then defaults to HTTP but assumes others based", + " on often-used hostname prefixes. For example, for hostnames starting with", + " \"ftp.\" curl assumes you want FTP.", + "\n You can specify any amount of URLs on the command line. They are fetched", + " in a sequential manner in the specified order unless you use --parallel.", + " You can specify command line options and URLs mixed and in any order on", + " the command line.", + "\n curl attempts to reuse connections when doing multiple transfers, so that", + " getting many files from the same server do not use multiple connects and", + " setup handshakes. This improves speed. Connection reuse can only be done", + " for URLs specified for a single command line invocation and cannot be", + " performed between separate curl runs.", + "\n Provide an IPv6 zone id in the URL with an escaped percentage sign. Like", + " in", + "\n\t\"http://[fe80::3%25eth0]/\"", + "\n Everything provided on the command line that is not a command line option", + " or its argument, curl assumes is a URL and treats it as such.", + "\nGLOBBING", + "\n You can specify multiple URLs or parts of URLs by writing lists within", + " braces or ranges within brackets. We call this \"globbing\".", + "\n Provide a list with three different names like this:", + "\n\t\"http://site.{one,two,three}.com\"", + "\n Do sequences of alphanumeric series by using [] as in:", + "\n\t\"ftp://ftp.example.com/file[1-100].txt\"", + "\n With leading zeroes:", + "\n\t\"ftp://ftp.example.com/file[001-100].txt\"", + "\n With letters through the alphabet:", + "\n\t\"ftp://ftp.example.com/file[a-z].txt\"", + "\n Nested sequences are not supported, but you can use several ones next to", + " each other:", + "\n\t\"http://example.com/archive[1996-1999]/vol[1-4]/part{a,b,c}.html\"", + "\n You can specify a step counter for the ranges to get every Nth number or", + " letter:", + "\n\t\"http://example.com/file[1-100:10].txt\"", + "\n\t\"http://example.com/file[a-z:2].txt\"", + "\n When using [] or {} sequences when invoked from a command line prompt, you", + " probably have to put the full URL within double quotes to avoid the shell", + " from interfering with it. This also goes for other characters treated", + " special, like for example '&', '?' and '*'.", + "\n Switch off globbing with --globoff.", + "\nVARIABLES", + "\n curl supports command line variables (added in 8.3.0). Set variables with", + " --variable name=content or --variable name@file (where \"file\" can be stdin", + " if set to a single dash (-)).", + "\n Variable contents can be expanded in option parameters using \"{{name}}\" if", + " the option name is prefixed with \"--expand-\". This gets the contents of", + " the variable \"name\" inserted, or a blank if the name does not exist as a", + " variable. Insert \"{{\" verbatim in the string by prefixing it with a", + " backslash, like \"\\{{\".", + "\n You an access and expand environment variables by first importing them.", + " You can select to either require the environment variable to be set or you", + " can provide a default value in case it is not already set. Plain", + " --variable %name imports the variable called 'name' but exits with an", + " error if that environment variable is not already set. To provide a", + " default value if it is not set, use --variable %name=content or --variable", + " %name@content.", + "\n Example. Get the USER environment variable into the URL, fail if USER is", + " not set:", + "\n\t--variable '%USER'", + "\t--expand-url = \"https://example.com/api/{{USER}}/method\"", + "\n When expanding variables, curl supports a set of functions that can make", + " the variable contents more convenient to use. It can trim leading and", + " trailing white space with \"trim\", it can output the contents as a JSON", + " quoted string with \"json\", URL encode the string with \"url\" or base64", + " encode it with \"b64\". To apply functions to a variable expansion, add them", + " colon separated to the right side of the variable. Variable content", + " holding null bytes that are not encoded when expanded cause error.", + "\n Example: get the contents of a file called $HOME/.secret into a variable", + " called \"fix\". Make sure that the content is trimmed and percent-encoded", + " when sent as POST data:", + "\n\t--variable %HOME", + "\t--expand-variable fix@{{HOME}}/.secret", + "\t--expand-data \"{{fix:trim:url}}\"", + "\thttps://example.com/", + "\n Command line variables and expansions were added in 8.3.0.", + "\nOUTPUT", + "\n If not told otherwise, curl writes the received data to stdout. It can be", + " instructed to instead save that data into a local file, using the --output", + " or --remote-name options. If curl is given multiple URLs to transfer on", + " the command line, it similarly needs multiple options for where to save", + " them.", + "\n curl does not parse or otherwise \"understand\" the content it gets or", + " writes as output. It does no encoding or decoding, unless explicitly asked", + " to with dedicated command line options.", + "\nPROTOCOLS", + "\n curl supports numerous protocols, or put in URL terms: schemes. Your", + " particular build may not support them all.", + "\n DICT", + "\n\tLets you lookup words using online dictionaries.", + "\n FILE", + "\n\tRead or write local files. curl does not support accessing file:// URL", + "\tremotely, but when running on Microsoft Windows using the native UNC", + "\tapproach works.", + "\n FTP(S)", + "\n\tcurl supports the File Transfer Protocol with a lot of tweaks and", + "\tlevers. With or without using TLS.", + "\n GOPHER(S)", + "\n\tRetrieve files.", + "\n HTTP(S)", + "\n\tcurl supports HTTP with numerous options and variations. It can speak", + "\tHTTP version 0.9, 1.0, 1.1, 2 and 3 depending on build options and the", + "\tcorrect command line options.", + "\n IMAP(S)", + "\n\tUsing the mail reading protocol, curl can download emails for you.", + "\tWith or without using TLS.", + "\n LDAP(S)", + "\n\tcurl can do directory lookups for you, with or without TLS.", + "\n MQTT", + "\n\tcurl supports MQTT version 3. Downloading over MQTT equals subscribe", + "\tto a topic while uploading/posting equals publish on a topic. MQTT", + "\tover TLS is not supported (yet).", + "\n POP3(S)", + "\n\tDownloading from a pop3 server means getting a mail. With or without", + "\tusing TLS.", + "\n RTMP(S)", + "\n\tThe Realtime Messaging Protocol is primarily used to serve streaming", + "\tmedia and curl can download it.", + "\n RTSP", + "\n\tcurl supports RTSP 1.0 downloads.", + "\n SCP", + "\n\tcurl supports SSH version 2 scp transfers.", + "\n SFTP", + "\n\tcurl supports SFTP (draft 5) done over SSH version 2.", + "\n SMB(S)", + "\n\tcurl supports SMB version 1 for upload and download.", + "\n SMTP(S)", + "\n\tUploading contents to an SMTP server means sending an email. With or", + "\twithout TLS.", + "\n TELNET", + "\n\tFetching a telnet URL starts an interactive session where it sends", + "\twhat it reads on stdin and outputs what the server sends it.", + "\n TFTP", + "\n\tcurl can do TFTP downloads and uploads.", + "\n WS(S)", + "\n\tWebSocket done over HTTP/1. WSS implies that it works over HTTPS.", + "\nPROGRESS METER", + "\n curl normally displays a progress meter during operations, indicating the", + " amount of transferred data, transfer speeds and estimated time left, etc.", + " The progress meter displays the transfer rate in bytes per second. The", + " suffixes (k, M, G, T, P) are 1024 based. For example 1k is 1024 bytes. 1M", + " is 1048576 bytes.", + "\n curl displays this data to the terminal by default, so if you invoke curl", + " to do an operation and it is about to write data to the terminal, it", + " disables the progress meter as otherwise it would mess up the output", + " mixing progress meter and response data.", + "\n If you want a progress meter for HTTP POST or PUT requests, you need to", + " redirect the response output to a file, using shell redirect (>), --output", + " or similar.", + "\n This does not apply to FTP upload as that operation does not spit out any", + " response data to the terminal.", + "\n If you prefer a progress bar instead of the regular meter, --progress-bar", + " is your friend. You can also disable the progress meter completely with", + " the --silent option.", + "\nVERSION", + "\n This man page describes curl 8.9.1. If you use a later version, chances", + " are this man page does not fully document it. If you use an earlier", + " version, this document tries to include version information about which", + " specific version that introduced changes.", + "\n You can always learn which the latest curl version is by running", + "\n\tcurl https://curl.se/info", + "\n The online version of this man page is always showing the latest", + " incarnation: https://curl.se/docs/manpage.html", + "\nOPTIONS", + "\n Options start with one or two dashes. Many of the options require an", + " additional value next to them. If provided text does not start with a", + " dash, it is presumed to be and treated as a URL.", + "\n The short \"single-dash\" form of the options, -d for example, may be used", + " with or without a space between it and its value, although a space is a", + " recommended separator. The long double-dash form, --data for example,", + " requires a space between it and its value.", + "\n Short version options that do not need any additional values can be used", + " immediately next to each other, like for example you can specify all the", + " options -O, -L and -v at once as -OLv.", + "\n In general, all boolean options are enabled with --option and yet again", + " disabled with --no-option. That is, you use the same option name but", + " prefix it with \"no-\". However, in this list we mostly only list and show", + " the --option version of them.", + "\n When --next is used, it resets the parser state and you start again with a", + " clean option state, except for the options that are global. Global options", + " retain their values and meaning even after --next.", + "\n The following options are global: --fail-early, --libcurl,", + " --parallel-immediate, --parallel-max, --parallel, --progress-bar, --rate,", + " --show-error, --stderr, --styled-output, --trace-ascii, --trace-config,", + " --trace-ids, --trace-time, --trace and --verbose.", + "\n --abstract-unix-socket ", + "\t (HTTP) Connect through an abstract Unix domain socket, instead of", + "\t using the network. Note: netstat shows the path of an abstract", + "\t socket prefixed with \"@\", however the argument should not", + "\t have this leading character. If --abstract-unix-socket is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --abstract-unix-socket socketpath https://example.com", + "\n\t See also --unix-socket.", + "\n --alt-svc ", + "\t (HTTPS) Enable the alt-svc parser. If the filename points to an", + "\t existing alt-svc cache file, that gets used. After a completed", + "\t transfer, the cache is saved to the filename again if it has been", + "\t modified.", + "\n\t Specify a \"\" filename (zero length) to avoid loading/saving and", + "\t make curl just handle the cache in memory.", + "\n\t If this option is used several times, curl loads contents from all", + "\t the files but the last one is used for saving. --alt-svc can be", + "\t used several times in a command line", + "\n\t Example:", + "\t curl --alt-svc svc.txt https://example.com", + "\n\t Added in 7.64.1. See also --resolve and --connect-to.", + "\n --anyauth", + "\t (HTTP) Figure out authentication method automatically, and use the", + "\t most secure one the remote site claims to support. This is done by", + "\t first doing a request and checking the response-headers, thus", + "\t possibly inducing an extra network round-trip. This option is used", + "\t instead of setting a specific authentication method, which you can", + "\t do with --basic, --digest, --ntlm, and --negotiate.", + "\n\t Using --anyauth is not recommended if you do uploads from stdin,", + "\t since it may require data to be sent twice and then the client", + "\t must be able to rewind. If the need should arise when uploading", + "\t from stdin, the upload operation fails.", + "\n\t Used together with --user. Providing --anyauth multiple times has", + "\t no extra effect.", + "\n\t Example:", + "\t curl --anyauth --user me:pwd https://example.com", + "\n\t See also --proxy-anyauth, --basic and --digest.", + "\n -a, --append", + "\t (FTP SFTP) When used in an upload, this option makes curl append", + "\t to the target file instead of overwriting it. If the remote file", + "\t does not exist, it is created. Note that this flag is ignored by", + "\t some SFTP servers (including OpenSSH). Providing --append multiple", + "\t times has no extra effect. Disable it again with --no-append.", + "\n\t Example:", + "\t curl --upload-file local --append ftp://example.com/", + "\n\t See also --range and --continue-at.", + "\n --aws-sigv4 ", + "\t (HTTP) Use AWS V4 signature authentication in the transfer.", + "\n\t The provider argument is a string that is used by the algorithm", + "\t when creating outgoing authentication headers.", + "\n\t The region argument is a string that points to a geographic area", + "\t of a resources collection (region-code) when the region name is", + "\t omitted from the endpoint.", + "\n\t The service argument is a string that points to a function", + "\t provided by a cloud (service-code) when the service name is", + "\t omitted from the endpoint. If --aws-sigv4 is provided several", + "\t times, the last set value is used.", + "\n\t Example:", + "\t curl --aws-sigv4 \"aws:amz:us-east-2:es\" --user \"key:secret\" https://example.com", + "\n\t Added in 7.75.0. See also --basic and --user.", + "\n --basic", + "\t (HTTP) Use HTTP Basic authentication with the remote host. This", + "\t method is the default and this option is usually pointless, unless", + "\t you use it to override a previously set option that sets a", + "\t different authentication method (such as --ntlm, --digest, or", + "\t --negotiate).", + "\n\t Used together with --user. Providing --basic multiple times has no", + "\t extra effect.", + "\n\t Example:", + "\t curl -u name:password --basic https://example.com", + "\n\t See also --proxy-basic.", + "\n --ca-native", + "\t (TLS) Use the CA store from the native operating system to verify", + "\t the peer. By default, curl otherwise uses a CA store provided in a", + "\t single file or directory, but when using this option it interfaces", + "\t the operating system's own vault.", + "\n\t This option works for curl on Windows when built to use OpenSSL,", + "\t wolfSSL (added in 8.3.0) or GnuTLS (added in 8.5.0). When curl on", + "\t Windows is built to use Schannel, this feature is implied and curl", + "\t then only uses the native CA store. Providing --ca-native multiple", + "\t times has no extra effect. Disable it again with --no-ca-native.", + "\n\t Example:", + "\t curl --ca-native https://example.com", + "\n\t Added in 8.2.0. See also --cacert, --capath and --insecure.", + "\n --cacert ", + "\t (TLS) Use the specified certificate file to verify the peer. The", + "\t file may contain multiple CA certificates. The certificate(s) must", + "\t be in PEM format. Normally curl is built to use a default file for", + "\t this, so this option is typically used to alter that default file.", + "\n\t curl recognizes the environment variable named 'CURL_CA_BUNDLE' if", + "\t it is set and the TLS backend is not Schannel, and uses the given", + "\t path as a path to a CA cert bundle. This option overrides that", + "\t variable.", + "\n\t The windows version of curl automatically looks for a CA certs", + "\t file named 'curl-ca-bundle.crt', either in the same directory as", + "\t curl.exe, or in the Current Working Directory, or in any folder", + "\t along your PATH.", + "\n\t (iOS and macOS only) If curl is built against Secure Transport,", + "\t then this option is supported for backward compatibility with", + "\t other SSL engines, but it should not be set. If the option is not", + "\t set, then curl uses the certificates in the system and user", + "\t Keychain to verify the peer, which is the preferred method of", + "\t verifying the peer's certificate chain.", + "\n\t (Schannel only) This option is supported for Schannel in Windows 7", + "\t or later (added in 7.60.0). This option is supported for backward", + "\t compatibility with other SSL engines; instead it is recommended to", + "\t use Windows' store of root certificates (the default for", + "\t Schannel). If --cacert is provided several times, the last set", + "\t value is used.", + "\n\t Example:", + "\t curl --cacert CA-file.txt https://example.com", + "\n\t See also --capath and --insecure.", + "\n --capath ", + "\t (TLS) Use the specified certificate directory to verify the peer.", + "\t Multiple paths can be provided by separated with colon (\":\") (e.g.", + "\t \"path1:path2:path3\"). The certificates must be in PEM format, and", + "\t if curl is built against OpenSSL, the directory must have been", + "\t processed using the c_rehash utility supplied with OpenSSL. Using", + "\t --capath can allow OpenSSL-powered curl to make SSL-connections", + "\t much more efficiently than using --cacert if the --cacert file", + "\t contains many CA certificates.", + "\n\t If this option is set, the default capath value is ignored. If", + "\t --capath is provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --capath /local/directory https://example.com", + "\n\t See also --cacert and --insecure.", + "\n -E, --cert ", + "\t (TLS) Use the specified client certificate file when getting a", + "\t file with HTTPS, FTPS or another SSL-based protocol. The", + "\t certificate must be in PKCS#12 format if using Secure Transport,", + "\t or PEM format if using any other engine. If the optional password", + "\t is not specified, it is queried for on the terminal. Note that", + "\t this option assumes a certificate file that is the private key and", + "\t the client certificate concatenated. See --cert and --key to", + "\t specify them independently.", + "\n\t In the portion of the argument, you must escape the", + "\t character \":\" as \"\\:\" so that it is not recognized as the password", + "\t delimiter. Similarly, you must escape the double quote character", + "\t as \\\" so that it is not recognized as an escape character.", + "\n\t If curl is built against OpenSSL library, and the engine pkcs11 is", + "\t available, then a PKCS#11 URI (RFC 7512) can be used to specify a", + "\t certificate located in a PKCS#11 device. A string beginning with", + "\t \"pkcs11:\" is interpreted as a PKCS#11 URI. If a PKCS#11 URI is", + "\t provided, then the --engine option is set as \"pkcs11\" if none was", + "\t provided and the --cert-type option is set as \"ENG\" if none was", + "\t provided.", + "\n\t (iOS and macOS only) If curl is built against Secure Transport,", + "\t then the certificate string can either be the name of a", + "\t certificate/private key in the system or user keychain, or the", + "\t path to a PKCS#12-encoded certificate and private key. If you want", + "\t to use a file from the current directory, please precede it with", + "\t \"./\" prefix, in order to avoid confusion with a nickname.", + "\n\t (Schannel only) Client certificates must be specified by a path", + "\t expression to a certificate store. (Loading PFX is not supported;", + "\t you can import it to a store first). You can use \"\\\\\" to refer to a certificate in", + "\t the system certificates store, for example,", + "\t \"CurrentUser\\MY\\934a7ac6f8a5d579285a74fa61e19f23ddfe8d7a\".", + "\t Thumbprint is usually a SHA-1 hex string which you can see in", + "\t certificate details. Following store locations are supported:", + "\t CurrentUser, LocalMachine, CurrentService, Services,", + "\t CurrentUserGroupPolicy,\t LocalMachineGroupPolicy\t and", + "\t LocalMachineEnterprise. If --cert is provided several times, the", + "\t last set value is used.", + "\n\t Example:", + "\t curl --cert certfile --key keyfile https://example.com", + "\n\t See also --cert-type, --key and --key-type.", + "\n --cert-status", + "\t (TLS) Verify the status of the server certificate by using the", + "\t Certificate Status Request (aka. OCSP stapling) TLS extension.", + "\n\t If this option is enabled and the server sends an invalid (e.g.", + "\t expired) response, if the response suggests that the server", + "\t certificate has been revoked, or no response at all is received,", + "\t the verification fails.", + "\n\t This support is currently only implemented in the OpenSSL and", + "\t GnuTLS backends. Providing --cert-status multiple times has no", + "\t extra effect. Disable it again with --no-cert-status.", + "\n\t Example:", + "\t curl --cert-status https://example.com", + "\n\t See also --pinnedpubkey.", + "\n --cert-type ", + "\t (TLS) Set type of the provided client certificate. PEM, DER, ENG", + "\t and P12 are recognized types.", + "\n\t The default type depends on the TLS backend and is usually PEM,", + "\t however for Secure Transport and Schannel it is P12. If --cert is", + "\t a pkcs11: URI then ENG is the default type. If --cert-type is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --cert-type PEM --cert file https://example.com", + "\n\t See also --cert, --key and --key-type.", + "\n --ciphers ", + "\t (TLS) Specifies which ciphers to use in the connection. The list", + "\t of ciphers must specify valid ciphers. Read up on SSL cipher list", + "\t details on this URL:", + "\n\t https://curl.se/docs/ssl-ciphers.html If --ciphers is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --ciphers ECDHE-ECDSA-AES256-CCM8 https://example.com", + "\n\t See also --tlsv1.3, --tls13-ciphers and --proxy-ciphers.", + "\n --compressed", + "\t (HTTP) Request a compressed response using one of the algorithms", + "\t curl supports, and automatically decompress the content.", + "\n\t Response headers are not modified when saved, so if they are", + "\t \"interpreted\" separately again at a later point they might appear", + "\t to be saying that the content is (still) compressed; while in fact", + "\t it has already been decompressed.", + "\n\t If this option is used and the server sends an unsupported", + "\t encoding, curl reports an error. This is a request, not an order;", + "\t the server may or may not deliver data compressed. Providing", + "\t --compressed multiple times has no extra effect. Disable it again", + "\t with --no-compressed.", + "\n\t Example:", + "\t curl --compressed https://example.com", + "\n\t See also --compressed-ssh.", + "\n --compressed-ssh", + "\t (SCP SFTP) Enables built-in SSH compression. This is a request,", + "\t not an order; the server may or may not do it. Providing", + "\t --compressed-ssh multiple times has no extra effect. Disable it", + "\t again with --no-compressed-ssh.", + "\n\t Example:", + "\t curl --compressed-ssh sftp://example.com/", + "\n\t See also --compressed.", + "\n -K, --config ", + "\t Specify a text file to read curl arguments from. The command line", + "\t arguments found in the text file are used as if they were provided", + "\t on the command line.", + "\n\t Options and their parameters must be specified on the same line in", + "\t the file, separated by whitespace, colon, or the equals sign. Long", + "\t option names can optionally be given in the config file without", + "\t the initial double dashes and if so, the colon or equals", + "\t characters can be used as separators. If the option is specified", + "\t with one or two dashes, there can be no colon or equals character", + "\t between the option and its parameter.", + "\n\t If the parameter contains whitespace or starts with a colon (:) or", + "\t equals sign (=), it must be specified enclosed within double", + "\t quotes (\"like this\"). Within double quotes the following escape", + "\t sequences are available: \\\\, \\\", \\t, \\n, \\r and \\v. A backslash", + "\t preceding any other letter is ignored.", + "\n\t If the first non-blank column of a config line is a '#' character,", + "\t that line is treated as a comment.", + "\n\t Only write one option per physical line in the config file. A", + "\t single line is required to be no more than 10 megabytes (since", + "\t 8.2.0).", + "\n\t Specify the filename to --config as minus \"-\" to make curl read", + "\t the file from stdin.", + "\n\t Note that to be able to specify a URL in the config file, you need", + "\t to specify it using the --url option, and not by simply writing", + "\t the URL on its own line. So, it could look similar to this:", + "\n\t\turl = \"https://curl.se/docs/\"", + "\n\t\t# --- Example file ---", + "\t\t# this is a comment", + "\t\turl = \"example.com\"", + "\t\toutput = \"curlhere.html\"", + "\t\tuser-agent = \"superagent/1.0\"", + "\n\t\t# and fetch another URL too", + "\t\turl = \"example.com/docs/manpage.html\"", + "\t\t-O", + "\t\treferer = \"http://nowhereatall.example.com/\"", + "\t\t# --- End of example file ---", + "\n\t When curl is invoked, it (unless --disable is used) checks for a", + "\t default config file and uses it if found, even when --config is", + "\t used. The default config file is checked for in the following", + "\t places in this order:", + "\n\t 1) \"$CURL_HOME/.curlrc\"", + "\n\t 2) \"$XDG_CONFIG_HOME/curlrc\" (Added in 7.73.0)", + "\n\t 3) \"$HOME/.curlrc\"", + "\n\t 4) Windows: \"%USERPROFILE%\\.curlrc\"", + "\n\t 5) Windows: \"%APPDATA%\\.curlrc\"", + "\n\t 6) Windows: \"%USERPROFILE%\\Application Data\\.curlrc\"", + "\n\t 7) Non-Windows: use getpwuid to find the home directory", + "\n\t 8) On Windows, if it finds no .curlrc file in the sequence", + "\t described above, it checks for one in the same directory the curl", + "\t executable is placed.", + "\n\t On Windows two filenames are checked per location: .curlrc and", + "\t _curlrc, preferring the former. Older versions on Windows checked", + "\t for _curlrc only. --config can be used several times in a command", + "\t line", + "\n\t Example:", + "\t curl --config file.txt https://example.com", + "\n\t See also --disable.", + "\n --connect-timeout ", + "\t Maximum time in seconds that you allow curl's connection to take.", + "\t This only limits the connection phase, so if curl connects within", + "\t the given period it continues - if not it exits.", + "\n\t This option accepts decimal values. The decimal value needs to be", + "\t provided using a dot (.) as decimal separator - not the local", + "\t version even if it might be using another separator.", + "\n\t The connection phase is considered complete when the DNS lookup", + "\t and requested TCP, TLS or QUIC handshakes are done. If", + "\t --connect-timeout is provided several times, the last set value is", + "\t used.", + "\n\t Examples:", + "\t curl --connect-timeout 20 https://example.com", + "\t curl --connect-timeout 3.14 https://example.com", + "\n\t See also --max-time.", + "\n --connect-to ", + "\t For a request intended for the \"HOST1:PORT1\" pair, connect to", + "\t \"HOST2:PORT2\" instead. This option is only used to establish the", + "\t network connection. It does NOT affect the hostname/port number", + "\t that is used for TLS/SSL (e.g. SNI, certificate verification) or", + "\t for the application protocols.", + "\n\t \"HOST1\" and \"PORT1\" may be empty strings, meaning any host or any", + "\t port number. \"HOST2\" and \"PORT2\" may also be empty strings,", + "\t meaning use the request's original hostname and port number.", + "\n\t A hostname specified to this option is compared as a string, so it", + "\t needs to match the name used in request URL. It can be either", + "\t numerical such as \"127.0.0.1\" or the full host name such as", + "\t \"example.org\".", + "\n\t Example: redirect connects from the example.com hostname to", + "\t 127.0.0.1 independently of port number:", + "\n\t\tcurl --connect-to example.com::127.0.0.1: https://example.com/", + "\n\t Example: redirect connects from all hostnames to 127.0.0.1", + "\t independently of port number:", + "\n\t\tcurl --connect-to ::127.0.0.1: http://example.com/", + "\n\t --connect-to can be used several times in a command line", + "\n\t Example:", + "\t curl --connect-to example.com:443:example.net:8443 https://example.com", + "\n\t See also --resolve and --header.", + "\n -C, --continue-at ", + "\t Resume a previous transfer from the given byte offset. The given", + "\t offset is the exact number of bytes that are skipped, counting", + "\t from the beginning of the source file before it is transferred to", + "\t the destination. If used with uploads, the FTP server command SIZE", + "\t is not used by curl.", + "\n\t Use \"-C -\" to instruct curl to automatically find out where/how to", + "\t resume the transfer. It then uses the given output/input files to", + "\t figure that out. If --continue-at is provided several times, the", + "\t last set value is used.", + "\n\t Examples:", + "\t curl -C - https://example.com", + "\t curl -C 400 https://example.com", + "\n\t See also --range.", + "\n -b, --cookie ", + "\t (HTTP) Pass the data to the HTTP server in the Cookie header. It", + "\t is supposedly the data previously received from the server in a", + "\t \"Set-Cookie:\" line. The data should be in the format", + "\t \"NAME1=VALUE1; NAME2=VALUE2\" or as a single filename.", + "\n\t When given a set of specific cookies and not a filename, it makes", + "\t curl use the cookie header with this content explicitly in all", + "\t outgoing request(s). If multiple requests are done due to", + "\t authentication, followed redirects or similar, they all get this", + "\t cookie header passed on.", + "\n\t If no \"=\" symbol is used in the argument, it is instead treated as", + "\t a filename to read previously stored cookie from. This option also", + "\t activates the cookie engine which makes curl record incoming", + "\t cookies, which may be handy if you are using this in combination", + "\t with the --location option or do multiple URL transfers on the", + "\t same invoke.", + "\n\t If the filename is a single minus (\"-\"), curl reads the contents", + "\t from stdin. If the filename is an empty string (\"\") and is the", + "\t only cookie input, curl activates the cookie engine without any", + "\t cookies.", + "\n\t The file format of the file to read cookies from should be plain", + "\t HTTP headers (Set-Cookie style) or the Netscape/Mozilla cookie", + "\t file format.", + "\n\t The file specified with --cookie is only used as input. No cookies", + "\t are written to that file. To store cookies, use the --cookie-jar", + "\t option.", + "\n\t If you use the Set-Cookie file format and do not specify a domain", + "\t then the cookie is not sent since the domain never matches. To", + "\t address this, set a domain in Set-Cookie line (doing that includes", + "\t subdomains) or preferably: use the Netscape format.", + "\n\t Users often want to both read cookies from a file and write", + "\t updated cookies back to a file, so using both --cookie and", + "\t --cookie-jar in the same command line is common.", + "\n\t If curl is built with PSL (Public Suffix List) support, it detects", + "\t and discards cookies that are specified for such suffix domains", + "\t that should not be allowed to have cookies. If curl is not built", + "\t with PSL support, it has no ability to stop super cookies.", + "\t --cookie can be used several times in a command line", + "\n\t Examples:", + "\t curl -b \"\" https://example.com", + "\t curl -b cookiefile https://example.com", + "\t curl -b cookiefile -c cookiefile https://example.com", + "\t curl -b name=Jane https://example.com", + "\n\t See also --cookie-jar and --junk-session-cookies.", + "\n -c, --cookie-jar ", + "\t (HTTP) Specify to which file you want curl to write all cookies", + "\t after a completed operation. Curl writes all cookies from its", + "\t in-memory cookie storage to the given file at the end of", + "\t operations. Even if no cookies are known, a file is created so", + "\t that it removes any formerly existing cookies from the file. The", + "\t file uses the Netscape cookie file format. If you set the filename", + "\t to a single minus, \"-\", the cookies are written to stdout.", + "\n\t The file specified with --cookie-jar is only used for output. No", + "\t cookies are read from the file. To read cookies, use the --cookie", + "\t option. Both options can specify the same file.", + "\n\t This command line option activates the cookie engine that makes", + "\t curl record and use cookies. The --cookie option also activates", + "\t it.", + "\n\t If the cookie jar cannot be created or written to, the whole curl", + "\t operation does not fail or even report an error clearly. Using", + "\t --verbose gets a warning displayed, but that is the only visible", + "\t feedback you get about this possibly lethal situation. If", + "\t --cookie-jar is provided several times, the last set value is", + "\t used.", + "\n\t Examples:", + "\t curl -c store-here.txt https://example.com", + "\t curl -c store-here.txt -b read-these https://example.com", + "\n\t See also --cookie and --junk-session-cookies.", + "\n --create-dirs", + "\t When used in conjunction with the --output option, curl creates", + "\t the necessary local directory hierarchy as needed. This option", + "\t creates the directories mentioned with the --output option", + "\t combined with the path possibly set with --output-dir. If the", + "\t combined output filename uses no directory, or if the directories", + "\t it mentions already exist, no directories are created.", + "\n\t Created directories are made with mode 0750 on unix style file", + "\t systems.", + "\n\t To create remote directories when using FTP or SFTP, try", + "\t --ftp-create-dirs. Providing --create-dirs multiple times has no", + "\t extra effect. Disable it again with --no-create-dirs.", + "\n\t Example:", + "\t curl --create-dirs --output local/dir/file https://example.com", + "\n\t See also --ftp-create-dirs and --output-dir.", + "\n --create-file-mode ", + "\t (SFTP SCP FILE) When curl is used to create files remotely using", + "\t one of the supported protocols, this option allows the user to set", + "\t which 'mode' to set on the file at creation time, instead of the", + "\t default 0644.", + "\n\t This option takes an octal number as argument. If", + "\t --create-file-mode is provided several times, the last set value", + "\t is used.", + "\n\t Example:", + "\t curl --create-file-mode 0777 -T localfile sftp://example.com/new", + "\n\t Added in 7.75.0. See also --ftp-create-dirs.", + "\n --crlf", + "\t (FTP SMTP) Convert line feeds to carriage return plus line feeds", + "\t in upload. Useful for MVS (OS/390). Providing --crlf multiple", + "\t times has no extra effect. Disable it again with --no-crlf.", + "\n\t Example:", + "\t curl --crlf -T file ftp://example.com/", + "\n\t See also --use-ascii.", + "\n --crlfile ", + "\t (TLS) Provide a file using PEM format with a Certificate", + "\t Revocation List that may specify peer certificates that are to be", + "\t considered revoked. If --crlfile is provided several times, the", + "\t last set value is used.", + "\n\t Example:", + "\t curl --crlfile rejects.txt https://example.com", + "\n\t See also --cacert and --capath.", + "\n --curves ", + "\t (TLS) Set specific curves to use during SSL session establishment", + "\t according to RFC 8422, 5.1. Multiple algorithms can be provided by", + "\t separating them with \":\" (e.g. \"X25519:P-521\"). The parameter is", + "\t available identically in the OpenSSL \"s_client\" and \"s_server\"", + "\t utilities.", + "\n\t --curves allows a OpenSSL powered curl to make SSL-connections", + "\t with exactly the (EC) curve requested by the client, avoiding", + "\t nontransparent client/server negotiations.", + "\n\t If this option is set, the default curves list built into OpenSSL", + "\t are ignored. If --curves is provided several times, the last set", + "\t value is used.", + "\n\t Example:", + "\t curl --curves X25519 https://example.com", + "\n\t Added in 7.73.0. See also --ciphers.", + "\n -d, --data ", + "\t (HTTP MQTT) Sends the specified data in a POST request to the HTTP", + "\t server, in the same way that a browser does when a user has filled", + "\t in an HTML form and presses the submit button. This option makes", + "\t curl pass the data to the server using the content-type", + "\t application/x-www-form-urlencoded. Compare to --form.", + "\n\t --data-raw is almost the same but does not have a special", + "\t interpretation of the @ character. To post data purely binary, you", + "\t should instead use the --data-binary option. To URL-encode the", + "\t value of a form field you may use --data-urlencode.", + "\n\t If any of these options is used more than once on the same command", + "\t line, the data pieces specified are merged with a separating", + "\t &-symbol. Thus, using '-d name=daniel -d skill=lousy' would", + "\t generate a post chunk that looks like 'name=daniel&skill=lousy'.", + "\n\t If you start the data with the letter @, the rest should be a", + "\t filename to read the data from, or - if you want curl to read the", + "\t data from stdin. Posting data from a file named 'foobar' would", + "\t thus be done with --data @foobar. When --data is told to read from", + "\t a file like that, carriage returns, newlines and null bytes are", + "\t stripped out. If you do not want the @ character to have a special", + "\t interpretation use --data-raw instead.", + "\n\t The data for this option is passed on to the server exactly as", + "\t provided on the command line. curl does not convert, change or", + "\t improve it. It is up to the user to provide the data in the", + "\t correct form. --data can be used several times in a command line", + "\n\t Examples:", + "\t curl -d \"name=curl\" https://example.com", + "\t curl -d \"name=curl\" -d \"tool=cmdline\" https://example.com", + "\t curl -d @filename https://example.com", + "\n\t This option is mutually exclusive with --form, --head and", + "\t --upload-file. See also --data-binary, --data-urlencode and", + "\t --data-raw.", + "\n --data-ascii ", + "\t (HTTP) This option is just an alias for --data. --data-ascii can", + "\t be used several times in a command line", + "\n\t Example:", + "\t curl --data-ascii @file https://example.com", + "\n\t See also --data-binary, --data-raw and --data-urlencode.", + "\n --data-binary ", + "\t (HTTP) Post data exactly as specified with no extra processing", + "\t whatsoever.", + "\n\t If you start the data with the letter @, the rest should be a", + "\t filename. Data is posted in a similar manner as --data does,", + "\t except that newlines and carriage returns are preserved and", + "\t conversions are never done.", + "\n\t Like --data the default content-type sent to the server is", + "\t application/x-www-form-urlencoded. If you want the data to be", + "\t treated as arbitrary binary data by the server then set the", + "\t content-type to octet-stream: -H \"Content-Type:", + "\t application/octet-stream\".", + "\n\t If this option is used several times, the ones following the first", + "\t append data as described in --data. --data-binary can be used", + "\t several times in a command line", + "\n\t Example:", + "\t curl --data-binary @filename https://example.com", + "\n\t See also --data-ascii.", + "\n --data-raw ", + "\t (HTTP) Post data similarly to --data but without the special", + "\t interpretation of the @ character. --data-raw can be used several", + "\t times in a command line", + "\n\t Examples:", + "\t curl --data-raw \"hello\" https://example.com", + "\t curl --data-raw \"@at@at@\" https://example.com", + "\n\t See also --data.", + "\n --data-urlencode ", + "\t (HTTP) Post data, similar to the other --data options with the", + "\t exception that this performs URL-encoding.", + "\n\t To be CGI-compliant, the part should begin with a name", + "\t followed by a separator and a content specification. The ", + "\t part can be passed to curl using one of the following syntaxes:", + "\n\t content", + "\n\t\tURL-encode the content and pass that on. Just be careful so", + "\t\tthat the content does not contain any \"=\" or \"@\" symbols, as", + "\t\tthat makes the syntax match one of the other cases below!", + "\n\t =content", + "\n\t\tURL-encode the content and pass that on. The preceding \"=\"", + "\t\tsymbol is not included in the data.", + "\n\t name=content", + "\n\t\tURL-encode the content part and pass that on. Note that the", + "\t\tname part is expected to be URL-encoded already.", + "\n\t @filename", + "\n\t\tload data from the given file (including any newlines),", + "\t\tURL-encode that data and pass it on in the POST.", + "\n\t name@filename", + "\n\t\tload data from the given file (including any newlines),", + "\t\tURL-encode that data and pass it on in the POST. The name part", + "\t\tgets an equal sign appended, resulting in", + "\t\tname=urlencoded-file-content. Note that the name is expected", + "\t\tto be URL-encoded already.", + "\n\t --data-urlencode can be used several times in a command line", + "\n\t Examples:", + "\t curl --data-urlencode name=val https://example.com", + "\t curl --data-urlencode =encodethis https://example.com", + "\t curl --data-urlencode name@file https://example.com", + "\t curl --data-urlencode @fileonly https://example.com", + "\n\t See also --data and --data-raw.", + "\n --delegation ", + "\t (GSS/kerberos) Set LEVEL what curl is allowed to delegate when it", + "\t comes to user credentials.", + "\n\t none", + "\n\t\tDo not allow any delegation.", + "\n\t policy", + "\n\t\tDelegates if and only if the OK-AS-DELEGATE flag is set in the", + "\t\tKerberos service ticket, which is a matter of realm policy.", + "\n\t always", + "\n\t\tUnconditionally allow the server to delegate.", + "\n\t If --delegation is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --delegation \"none\" https://example.com", + "\n\t See also --insecure and --ssl.", + "\n --digest", + "\t (HTTP) Enables HTTP Digest authentication. This authentication", + "\t scheme avoids sending the password over the wire in clear text.", + "\t Use this in combination with the normal --user option to set", + "\t username and password. Providing --digest multiple times has no", + "\t extra effect. Disable it again with --no-digest.", + "\n\t Example:", + "\t curl -u name:password --digest https://example.com", + "\n\t This option is mutually exclusive with --basic, --ntlm and", + "\t --negotiate. See also --user, --proxy-digest and --anyauth.", + "\n -q, --disable", + "\t If used as the first parameter on the command line, the curlrc", + "\t config file is not read or used. See the --config for details on", + "\t the default config file search path. Providing --disable multiple", + "\t times has no extra effect. Disable it again with --no-disable.", + "\n\t Example:", + "\t curl -q https://example.com", + "\n\t See also --config.", + "\n --disable-eprt", + "\t (FTP) Disable the use of the EPRT and LPRT commands when doing", + "\t active FTP transfers. Curl normally first attempts to use EPRT", + "\t before using PORT, but with this option, it uses PORT right away.", + "\t EPRT is an extension to the original FTP protocol, and does not", + "\t work on all servers, but enables more functionality in a better", + "\t way than the traditional PORT command.", + "\n\t --eprt can be used to explicitly enable EPRT again and --no-eprt", + "\t is an alias for --disable-eprt.", + "\n\t If the server is accessed using IPv6, this option has no effect as", + "\t EPRT is necessary then.", + "\n\t Disabling EPRT only changes the active behavior. If you want to", + "\t switch to passive mode you need to not use --ftp-port or force it", + "\t with --ftp-pasv. Providing --disable-eprt multiple times has no", + "\t extra effect. Disable it again with --no-disable-eprt.", + "\n\t Example:", + "\t curl --disable-eprt ftp://example.com/", + "\n\t See also --disable-epsv and --ftp-port.", + "\n --disable-epsv", + "\t (FTP) Disable the use of the EPSV command when doing passive FTP", + "\t transfers. Curl normally first attempts to use EPSV before PASV,", + "\t but with this option, it does not try EPSV.", + "\n\t --epsv can be used to explicitly enable EPSV again and --no-epsv", + "\t is an alias for --disable-epsv.", + "\n\t If the server is an IPv6 host, this option has no effect as EPSV", + "\t is necessary then.", + "\n\t Disabling EPSV only changes the passive behavior. If you want to", + "\t switch to active mode you need to use --ftp-port. Providing", + "\t --disable-epsv multiple times has no extra effect. Disable it", + "\t again with --no-disable-epsv.", + "\n\t Example:", + "\t curl --disable-epsv ftp://example.com/", + "\n\t See also --disable-eprt and --ftp-port.", + "\n --disallow-username-in-url", + "\t Exit with error if passed a URL containing a username. Probably", + "\t most useful when the URL is being provided at runtime or similar.", + "\t Providing --disallow-username-in-url multiple times has no extra", + "\t effect. Disable it again with --no-disallow-username-in-url.", + "\n\t Example:", + "\t curl --disallow-username-in-url https://example.com", + "\n\t Added in 7.61.0. See also --proto.", + "\n --dns-interface ", + "\t (DNS) Send outgoing DNS requests through the given interface. This", + "\t option is a counterpart to --interface (which does not affect", + "\t DNS). The supplied string must be an interface name (not an", + "\t address). If --dns-interface is provided several times, the last", + "\t set value is used.", + "\n\t Example:", + "\t curl --dns-interface eth0 https://example.com", + "\n\t --dns-interface requires that libcurl is built to support c-ares.", + "\t See also --dns-ipv4-addr and --dns-ipv6-addr.", + "\n --dns-ipv4-addr
", + "\t (DNS) Bind to a specific IP address when making IPv4 DNS requests,", + "\t so that the DNS requests originate from this address. The argument", + "\t should be a single IPv4 address. If --dns-ipv4-addr is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --dns-ipv4-addr 10.1.2.3 https://example.com", + "\n\t --dns-ipv4-addr requires that libcurl is built to support c-ares.", + "\t See also --dns-interface and --dns-ipv6-addr.", + "\n --dns-ipv6-addr
", + "\t (DNS) Bind to a specific IP address when making IPv6 DNS requests,", + "\t so that the DNS requests originate from this address. The argument", + "\t should be a single IPv6 address. If --dns-ipv6-addr is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --dns-ipv6-addr 2a04:4e42::561 https://example.com", + "\n\t --dns-ipv6-addr requires that libcurl is built to support c-ares.", + "\t See also --dns-interface and --dns-ipv4-addr.", + "\n --dns-servers ", + "\t (DNS) Set the list of DNS servers to be used instead of the system", + "\t default. The list of IP addresses should be separated with commas.", + "\t Port numbers may also optionally be given, appended to the IP", + "\t address separated with a colon. If --dns-servers is provided", + "\t several times, the last set value is used.", + "\n\t Examples:", + "\t curl --dns-servers 192.168.0.1,192.168.0.2 https://example.com", + "\t curl --dns-servers 10.0.0.1:53 https://example.com", + "\n\t --dns-servers requires that libcurl is built to support c-ares.", + "\t See also --dns-interface and --dns-ipv4-addr.", + "\n --doh-cert-status", + "\t Same as --cert-status but used for DoH (DNS-over-HTTPS).", + "\n\t Verifies the status of the DoH servers' certificate by using the", + "\t Certificate Status Request (aka. OCSP stapling) TLS extension.", + "\n\t If this option is enabled and the DoH server sends an invalid", + "\t (e.g. expired) response, if the response suggests that the server", + "\t certificate has been revoked, or no response at all is received,", + "\t the verification fails.", + "\n\t This support is currently only implemented in the OpenSSL and", + "\t GnuTLS backends. Providing --doh-cert-status multiple times has no", + "\t extra effect. Disable it again with --no-doh-cert-status.", + "\n\t Example:", + "\t curl --doh-cert-status --doh-url https://doh.example https://example.com", + "\n\t Added in 7.76.0. See also --doh-insecure.", + "\n --doh-insecure", + "\t By default, every connection curl makes to a DoH server is", + "\t verified to be secure before the transfer takes place. This option", + "\t tells curl to skip the verification step and proceed without", + "\t checking.", + "\n\t WARNING: using this option makes the DoH transfer and name", + "\t resolution insecure.", + "\n\t This option is equivalent to --insecure and --proxy-insecure but", + "\t used for DoH (DNS-over-HTTPS) only. Providing --doh-insecure", + "\t multiple times has no extra effect. Disable it again with", + "\t --no-doh-insecure.", + "\n\t Example:", + "\t curl --doh-insecure --doh-url https://doh.example https://example.com", + "\n\t Added in 7.76.0. See also --doh-url, --insecure and", + "\t --proxy-insecure.", + "\n --doh-url ", + "\t Specifies which DNS-over-HTTPS (DoH) server to use to resolve", + "\t hostnames, instead of using the default name resolver mechanism.", + "\t The URL must be HTTPS.", + "\n\t Some SSL options that you set for your transfer also applies to", + "\t DoH since the name lookups take place over SSL. However, the", + "\t certificate verification settings are not inherited but are", + "\t controlled separately via --doh-insecure and --doh-cert-status.", + "\n\t This option is unset if an empty string \"\" is used as the URL.", + "\t (Added in 7.85.0) If --doh-url is provided several times, the last", + "\t set value is used.", + "\n\t Example:", + "\t curl --doh-url https://doh.example https://example.com", + "\n\t Added in 7.62.0. See also --doh-insecure.", + "\n -D, --dump-header ", + "\t (HTTP FTP) Write the received protocol headers to the specified", + "\t file. If no headers are received, the use of this option creates", + "\t an empty file. Specify \"-\" as file name (a single minus) to have", + "\t it written to stdout.", + "\n\t When used in FTP, the FTP server response lines are considered", + "\t being \"headers\" and thus are saved there.", + "\n\t Having multiple transfers in one set of operations (i.e. the URLs", + "\t in one --next clause), appends them to the same file, separated by", + "\t a blank line. If --dump-header is provided several times, the last", + "\t set value is used.", + "\n\t Examples:", + "\t curl --dump-header store.txt https://example.com", + "\t curl --dump-header - https://example.com -o save", + "\n\t See also --output.", + "\n --ech ", + "\t (HTTPS) Specifies how to do ECH (Encrypted Client Hello).", + "\n\t The values allowed for can be:", + "\n\t false", + "\n\t\tDo not attempt ECH", + "\n\t grease", + "\n\t\tSend a GREASE ECH extension", + "\n\t true", + "\n\t\tAttempt ECH if possible, but do not fail if ECH is not", + "\t\tattempted. (The connection fails if ECH is attempted but", + "\t\tfails.)", + "\n\t hard", + "\n\t\tAttempt ECH and fail if that is not possible. ECH only works", + "\t\twith TLS 1.3 and also requires using DoH or providing an", + "\t\tECHConfigList on the command line.", + "\n\t ecl:", + "\n\t\tA base64 encoded ECHConfigList that is used for ECH.", + "\n\t pn:", + "\n\t\tA name to use to over-ride the \"public_name\" field of an", + "\t\tECHConfigList (only available with OpenSSL TLS support)", + "\n\t Errors", + "\n\t\tMost errors cause error CURLE_ECH_REQUIRED (101).", + "\n\t If --ech is provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --ech true https://example.com", + "\n\t Added in 8.8.0. See also --doh-url.", + "\n --egd-file ", + "\t (TLS) Deprecated option (added in 7.84.0). Prior to that it only", + "\t had an effect on curl if built to use old versions of OpenSSL.", + "\n\t Specify the path name to the Entropy Gathering Daemon socket. The", + "\t socket is used to seed the random engine for SSL connections. If", + "\t --egd-file is provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --egd-file /random/here https://example.com", + "\n\t See also --random-file.", + "\n --engine ", + "\t (TLS) Select the OpenSSL crypto engine to use for cipher", + "\t operations. Use --engine list to print a list of build-time", + "\t supported engines. Note that not all (and possibly none) of the", + "\t engines may be available at runtime. If --engine is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --engine flavor https://example.com", + "\n\t See also --ciphers and --curves.", + "\n --etag-compare ", + "\t (HTTP) Make a conditional HTTP request for the specific ETag read", + "\t from the given file by sending a custom If-None-Match header using", + "\t the stored ETag.", + "\n\t For correct results, make sure that the specified file contains", + "\t only a single line with the desired ETag. An empty file is parsed", + "\t as an empty ETag.", + "\n\t Use the option --etag-save to first save the ETag from a response,", + "\t and then use this option to compare against the saved ETag in a", + "\t subsequent request. If --etag-compare is provided several times,", + "\t the last set value is used.", + "\n\t Example:", + "\t curl --etag-compare etag.txt https://example.com", + "\n\t Added in 7.68.0. See also --etag-save and --time-cond.", + "\n --etag-save ", + "\t (HTTP) Save an HTTP ETag to the specified file. An ETag is a", + "\t caching related header, usually returned in a response.", + "\n\t If no ETag is sent by the server, an empty file is created. If", + "\t --etag-save is provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --etag-save storetag.txt https://example.com", + "\n\t Added in 7.68.0. See also --etag-compare.", + "\n --expect100-timeout ", + "\t (HTTP) Maximum time in seconds that you allow curl to wait for a", + "\t 100-continue response when curl emits an Expects: 100-continue", + "\t header in its request. By default curl waits one second. This", + "\t option accepts decimal values. When curl stops waiting, it", + "\t continues as if a response was received.", + "\n\t The decimal value needs to provided using a dot (\".\") as decimal", + "\t separator - not the local version even if it might be using", + "\t another separator. If --expect100-timeout is provided several", + "\t times, the last set value is used.", + "\n\t Example:", + "\t curl --expect100-timeout 2.5 -T file https://example.com", + "\n\t See also --connect-timeout.", + "\n -f, --fail", + "\t (HTTP) Fail with error code 22 and with no response body output at", + "\t all for HTTP transfers returning HTTP response codes at 400 or", + "\t greater.", + "\n\t In normal cases when an HTTP server fails to deliver a document,", + "\t it returns a body of text stating so (which often also describes", + "\t why and more) and a 4xx HTTP response code. This command line", + "\t option prevents curl from outputting that data and instead returns", + "\t error 22 early. By default, curl does not consider HTTP response", + "\t codes to indicate failure.", + "\n\t To get both the error code and also save the content, use", + "\t --fail-with-body instead.", + "\n\t This method is not fail-safe and there are occasions where", + "\t non-successful response codes slip through, especially when", + "\t authentication is involved (response codes 401 and 407). Providing", + "\t --fail multiple times has no extra effect. Disable it again with", + "\t --no-fail.", + "\n\t Example:", + "\t curl --fail https://example.com", + "\n\t This option is mutually exclusive with --fail-with-body. See also", + "\t --fail-with-body and --fail-early.", + "\n --fail-early", + "\t Fail and exit on the first detected transfer error.", + "\n\t When curl is used to do multiple transfers on the command line, it", + "\t attempts to operate on each given URL, one by one. By default, it", + "\t ignores errors if there are more URLs given and the last URL's", + "\t success determines the error code curl returns. Early failures are", + "\t \"hidden\" by subsequent successful transfers.", + "\n\t Using this option, curl instead returns an error on the first", + "\t transfer that fails, independent of the amount of URLs that are", + "\t given on the command line. This way, no transfer failures go", + "\t undetected by scripts and similar.", + "\n\t This option does not imply --fail, which causes transfers to fail", + "\t due to the server's HTTP status code. You can combine the two", + "\t options, however note --fail is not global and is therefore", + "\t contained by --next.", + "\n\t This option is global and does not need to be specified for each", + "\t use of --next. Providing --fail-early multiple times has no extra", + "\t effect. Disable it again with --no-fail-early.", + "\n\t Example:", + "\t curl --fail-early https://example.com https://two.example", + "\n\t See also --fail and --fail-with-body.", + "\n --fail-with-body", + "\t (HTTP) Return an error on server errors where the HTTP response", + "\t code is 400 or greater). In normal cases when an HTTP server fails", + "\t to deliver a document, it returns an HTML document stating so", + "\t (which often also describes why and more). This option allows curl", + "\t to output and save that content but also to return error 22.", + "\n\t This is an alternative option to --fail which makes curl fail for", + "\t the same circumstances but without saving the content. Providing", + "\t --fail-with-body multiple times has no extra effect. Disable it", + "\t again with --no-fail-with-body.", + "\n\t Example:", + "\t curl --fail-with-body https://example.com", + "\n\t This option is mutually exclusive with --fail. Added in 7.76.0.", + "\t See also --fail and --fail-early.", + "\n --false-start", + "\t (TLS) Use false start during the TLS handshake. False start is a", + "\t mode where a TLS client starts sending application data before", + "\t verifying the server's Finished message, thus saving a round trip", + "\t when performing a full handshake.", + "\n\t This functionality is currently only implemented in the Secure", + "\t Transport (on iOS 7.0 or later, or OS X 10.9 or later) backend.", + "\t Providing --false-start multiple times has no extra effect.", + "\t Disable it again with --no-false-start.", + "\n\t Example:", + "\t curl --false-start https://example.com", + "\n\t See also --tcp-fastopen.", + "\n -F, --form ", + "\t (HTTP SMTP IMAP) For the HTTP protocol family, emulate a filled-in", + "\t form in which a user has pressed the submit button. This makes", + "\t curl POST data using the Content-Type multipart/form-data", + "\t according to RFC 2388.", + "\n\t For SMTP and IMAP protocols, this composes a multipart mail", + "\t message to transmit.", + "\n\t This enables uploading of binary files etc. To force the 'content'", + "\t part to be a file, prefix the filename with an @ sign. To just get", + "\t the content part from a file, prefix the filename with the symbol", + "\t <. The difference between @ and < is then that @ makes a file get", + "\t attached in the post as a file upload, while the < makes a text", + "\t field and just get the contents for that text field from a file.", + "\n\t Read content from stdin instead of a file by using a single \"-\" as", + "\t filename. This goes for both @ and < constructs. When stdin is", + "\t used, the contents is buffered in memory first by curl to", + "\t determine its size and allow a possible resend. Defining a part's", + "\t data from a named non-regular file (such as a named pipe or", + "\t similar) is not subject to buffering and is instead read at", + "\t transmission time; since the full size is unknown before the", + "\t transfer starts, such data is sent as chunks by HTTP and rejected", + "\t by IMAP.", + "\n\t Example: send an image to an HTTP server, where 'profile' is the", + "\t name of the form-field to which the file portrait.jpg is the", + "\t input:", + "\n\t\tcurl -F profile=@portrait.jpg https://example.com/upload.cgi", + "\n\t Example: send your name and shoe size in two text fields to the", + "\t server:", + "\n\t\tcurl -F name=John -F shoesize=11 https://example.com/", + "\n\t Example: send your essay in a text field to the server. Send it as", + "\t a plain text field, but get the contents for it from a local file:", + "\n\t\tcurl -F \"story=HTML message;type=text/html' \\", + "\t\t -F '=)' -F '=@textfile.txt' ... smtp://example.com", + "\n\t Data can be encoded for transfer using encoder=. Available", + "\t encodings are binary and 8bit that do nothing else than adding the", + "\t corresponding Content-Transfer-Encoding header, 7bit that only", + "\t rejects 8-bit characters with a transfer error, quoted-printable", + "\t and base64 that encodes data according to the corresponding", + "\t schemes, limiting lines length to 76 characters.", + "\n\t Example: send multipart mail with a quoted-printable text message", + "\t and a base64 attached file:", + "\n\t\tcurl -F '=text message;encoder=quoted-printable' \\", + "\t\t -F '=@localfile;encoder=base64' ... smtp://example.com", + "\n\t See further examples and details in the MANUAL. --form can be used", + "\t several times in a command line", + "\n\t Example:", + "\t curl --form \"name=curl\" --form \"file=@loadthis\" https://example.com", + "\n\t This option is mutually exclusive with --data, --head and", + "\t --upload-file. See also --data, --form-string and --form-escape.", + "\n --form-escape", + "\t (HTTP imap smtp) Pass on names of multipart form fields and files", + "\t using backslash-escaping instead of percent-encoding. If", + "\t --form-escape is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --form-escape -F 'field\\name=curl' -F 'file=@load\"this' https://example.com", + "\n\t Added in 7.81.0. See also --form.", + "\n --form-string ", + "\t (HTTP SMTP IMAP) Similar to --form except that the value string", + "\t for the named parameter is used literally. Leading @ and <", + "\t characters, and the \";type=\" string in the value have no special", + "\t meaning. Use this in preference to --form if there is any", + "\t possibility that the string value may accidentally trigger the @", + "\t or < features of --form. --form-string can be used several times", + "\t in a command line", + "\n\t Example:", + "\t curl --form-string \"name=data\" https://example.com", + "\n\t See also --form.", + "\n --ftp-account ", + "\t (FTP) When an FTP server asks for \"account data\" after username", + "\t and password has been provided, this data is sent off using the", + "\t ACCT command. If --ftp-account is provided several times, the last", + "\t set value is used.", + "\n\t Example:", + "\t curl --ftp-account \"mr.robot\" ftp://example.com/", + "\n\t See also --user.", + "\n --ftp-alternative-to-user ", + "\t (FTP) If authenticating with the USER and PASS commands fails,", + "\t send this command. When connecting to Tumbleweed's Secure", + "\t Transport server over FTPS using a client certificate, using \"SITE", + "\t AUTH\" tells the server to retrieve the username from the", + "\t certificate. If --ftp-alternative-to-user is provided several", + "\t times, the last set value is used.", + "\n\t Example:", + "\t curl --ftp-alternative-to-user \"U53r\" ftp://example.com", + "\n\t See also --ftp-account and --user.", + "\n --ftp-create-dirs", + "\t (FTP SFTP) When an FTP or SFTP URL/operation uses a path that does", + "\t not currently exist on the server, the standard behavior of curl", + "\t is to fail. Using this option, curl instead attempts to create", + "\t missing directories. Providing --ftp-create-dirs multiple times", + "\t has no extra effect. Disable it again with --no-ftp-create-dirs.", + "\n\t Example:", + "\t curl --ftp-create-dirs -T file ftp://example.com/remote/path/file", + "\n\t See also --create-dirs.", + "\n --ftp-method ", + "\t (FTP) Control what method curl should use to reach a file on an", + "\t FTP(S) server. The method argument should be one of the following", + "\t alternatives:", + "\n\t multicwd", + "\n\t\tDo a single CWD operation for each path part in the given URL.", + "\t\tFor deep hierarchies this means many commands. This is how RFC", + "\t\t1738 says it should be done. This is the default but the", + "\t\tslowest behavior.", + "\n\t nocwd", + "\n\t\tDo no CWD at all. curl does SIZE, RETR, STOR etc and gives the", + "\t\tfull path to the server for each of these commands. This is", + "\t\tthe fastest behavior.", + "\n\t singlecwd", + "\n\t\tDo one CWD with the full target directory and then operate on", + "\t\tthe file \"normally\" (like in the multicwd case). This is", + "\t\tsomewhat more standards compliant than \"nocwd\" but without the", + "\t\tfull penalty of \"multicwd\".", + "\n\t If --ftp-method is provided several times, the last set value is", + "\t used.", + "\n\t Examples:", + "\t curl --ftp-method multicwd ftp://example.com/dir1/dir2/file", + "\t curl --ftp-method nocwd ftp://example.com/dir1/dir2/file", + "\t curl --ftp-method singlecwd ftp://example.com/dir1/dir2/file", + "\n\t See also --list-only.", + "\n --ftp-pasv", + "\t (FTP) Use passive mode for the data connection. Passive is the", + "\t internal default behavior, but using this option can be used to", + "\t override a previous --ftp-port option.", + "\n\t Reversing an enforced passive really is not doable but you must", + "\t then instead enforce the correct --ftp-port again.", + "\n\t Passive mode means that curl tries the EPSV command first and then", + "\t PASV, unless --disable-epsv is used. Providing --ftp-pasv multiple", + "\t times has no extra effect. Disable it again with --no-ftp-pasv.", + "\n\t Example:", + "\t curl --ftp-pasv ftp://example.com/", + "\n\t See also --disable-epsv.", + "\n -P, --ftp-port
", + "\t (FTP) Reverses the default initiator/listener roles when", + "\t connecting with FTP. This option makes curl use active mode. curl", + "\t then commands the server to connect back to the client's specified", + "\t address and port, while passive mode asks the server to setup an", + "\t IP address and port for it to connect to.
should be one", + "\t of:", + "\n\t interface", + "\n\t\te.g. eth0 to specify which interface's IP address you want to", + "\t\tuse (Unix only)", + "\n\t IP address", + "\n\t\te.g. 192.168.10.1 to specify the exact IP address", + "\n\t hostname", + "\n\t\te.g. my.host.domain to specify the machine", + "\n\t -", + "\n\t\tmake curl pick the same IP address that is already used for", + "\t\tthe control connection. This is the recommended choice.", + "\t\tDisable the use of PORT with --ftp-pasv. Disable the attempt", + "\t\tto use the EPRT command instead of PORT by using", + "\t\t--disable-eprt. EPRT is really PORT++.", + "\n\t\tYou can also append \":[start]-[end]\" to the right of the", + "\t\taddress, to tell curl what TCP port range to use. That means", + "\t\tyou specify a port range, from a lower to a higher number. A", + "\t\tsingle number works as well, but do note that it increases the", + "\t\trisk of failure since the port may not be available.", + "\n\t If --ftp-port is provided several times, the last set value is", + "\t used.", + "\n\t Examples:", + "\t curl -P - ftp:/example.com", + "\t curl -P eth0 ftp:/example.com", + "\t curl -P 192.168.0.2 ftp:/example.com", + "\n\t See also --ftp-pasv and --disable-eprt.", + "\n --ftp-pret", + "\t (FTP) Send a PRET command before PASV (and EPSV). Certain FTP", + "\t servers, mainly drftpd, require this non-standard command for", + "\t directory listings as well as up and downloads in PASV mode.", + "\t Providing --ftp-pret multiple times has no extra effect. Disable", + "\t it again with --no-ftp-pret.", + "\n\t Example:", + "\t curl --ftp-pret ftp://example.com/", + "\n\t See also --ftp-port and --ftp-pasv.", + "\n --ftp-skip-pasv-ip", + "\t (FTP) Do not use the IP address the server suggests in its", + "\t response to curl's PASV command when curl connects the data", + "\t connection. Instead curl reuses the same IP address it already", + "\t uses for the control connection.", + "\n\t This option is enabled by default (added in 7.74.0).", + "\n\t This option has no effect if PORT, EPRT or EPSV is used instead of", + "\t PASV. Providing --ftp-skip-pasv-ip multiple times has no extra", + "\t effect. Disable it again with --no-ftp-skip-pasv-ip.", + "\n\t Example:", + "\t curl --ftp-skip-pasv-ip ftp://example.com/", + "\n\t See also --ftp-pasv.", + "\n --ftp-ssl-ccc", + "\t (FTP) Use CCC (Clear Command Channel) Shuts down the SSL/TLS layer", + "\t after authenticating. The rest of the control channel", + "\t communication is be unencrypted. This allows NAT routers to follow", + "\t the FTP transaction. The default mode is passive. Providing", + "\t --ftp-ssl-ccc multiple times has no extra effect. Disable it again", + "\t with --no-ftp-ssl-ccc.", + "\n\t Example:", + "\t curl --ftp-ssl-ccc ftps://example.com/", + "\n\t See also --ssl and --ftp-ssl-ccc-mode.", + "\n --ftp-ssl-ccc-mode ", + "\t (FTP) Sets the CCC mode. The passive mode does not initiate the", + "\t shutdown, but instead waits for the server to do it, and does not", + "\t reply to the shutdown from the server. The active mode initiates", + "\t the shutdown and waits for a reply from the server. Providing", + "\t --ftp-ssl-ccc-mode multiple times has no extra effect. Disable it", + "\t again with --no-ftp-ssl-ccc-mode.", + "\n\t Example:", + "\t curl --ftp-ssl-ccc-mode active --ftp-ssl-ccc ftps://example.com/", + "\n\t See also --ftp-ssl-ccc.", + "\n --ftp-ssl-control", + "\t (FTP) Require SSL/TLS for the FTP login, clear for transfer.", + "\t Allows secure authentication, but non-encrypted data transfers for", + "\t efficiency. Fails the transfer if the server does not support", + "\t SSL/TLS. Providing --ftp-ssl-control multiple times has no extra", + "\t effect. Disable it again with --no-ftp-ssl-control.", + "\n\t Example:", + "\t curl --ftp-ssl-control ftp://example.com", + "\n\t See also --ssl.", + "\n -G, --get", + "\t (HTTP) When used, this option makes all data specified with", + "\t --data, --data-binary or --data-urlencode to be used in an HTTP", + "\t GET request instead of the POST request that otherwise would be", + "\t used. curl appends the provided data to the URL as a query string.", + "\n\t If used in combination with --head, the POST data is instead", + "\t appended to the URL with a HEAD request. Providing --get multiple", + "\t times has no extra effect. Disable it again with --no-get.", + "\n\t Examples:", + "\t curl --get https://example.com", + "\t curl --get -d \"tool=curl\" -d \"age=old\" https://example.com", + "\t curl --get -I -d \"tool=curl\" https://example.com", + "\n\t See also --data and --request.", + "\n -g, --globoff", + "\t Switch off the URL globbing function. When you set this option,", + "\t you can specify URLs that contain the letters {}[] without having", + "\t curl itself interpret them. Note that these letters are not normal", + "\t legal URL contents but they should be encoded according to the URI", + "\t standard. Providing --globoff multiple times has no extra effect.", + "\t Disable it again with --no-globoff.", + "\n\t Example:", + "\t curl -g \"https://example.com/{[]}}}}\"", + "\n\t See also --config and --disable.", + "\n --happy-eyeballs-timeout-ms ", + "\t Happy Eyeballs is an algorithm that attempts to connect to both", + "\t IPv4 and IPv6 addresses for dual-stack hosts, giving IPv6 a", + "\t head-start of the specified number of milliseconds. If the IPv6", + "\t address cannot be connected to within that time, then a connection", + "\t attempt is made to the IPv4 address in parallel. The first", + "\t connection to be established is the one that is used.", + "\n\t The range of suggested useful values is limited. Happy Eyeballs", + "\t RFC 6555 says \"It is RECOMMENDED that connection attempts be paced", + "\t 150-250 ms apart to balance human factors against network load.\"", + "\t libcurl currently defaults to 200 ms. Firefox and Chrome currently", + "\t default to 300 ms. If --happy-eyeballs-timeout-ms is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --happy-eyeballs-timeout-ms 500 https://example.com", + "\n\t See also --max-time and --connect-timeout.", + "\n --haproxy-clientip ", + "\t (HTTP) Sets a client IP in HAProxy PROXY protocol v1 header at the", + "\t beginning of the connection.", + "\n\t For valid requests, IPv4 addresses must be indicated as a series", + "\t of exactly 4 integers in the range [0..255] inclusive written in", + "\t decimal representation separated by exactly one dot between each", + "\t other. Heading zeroes are not permitted in front of numbers in", + "\t order to avoid any possible confusion with octal numbers. IPv6", + "\t addresses must be indicated as series of 4 hexadecimal digits", + "\t (upper or lower case) delimited by colons between each other, with", + "\t the acceptance of one double colon sequence to replace the largest", + "\t acceptable range of consecutive zeroes. The total number of", + "\t decoded bits must exactly be 128.", + "\n\t Otherwise, any string can be accepted for the client IP and get", + "\t sent.", + "\n\t It replaces --haproxy-protocol if used, it is not necessary to", + "\t specify both flags. If --haproxy-clientip is provided several", + "\t times, the last set value is used.", + "\n\t Example:", + "\t curl --haproxy-clientip $IP", + "\n\t Added in 8.2.0. See also --proxy.", + "\n --haproxy-protocol", + "\t (HTTP) Send a HAProxy PROXY protocol v1 header at the beginning of", + "\t the connection. This is used by some load balancers and reverse", + "\t proxies to indicate the client's true IP address and port.", + "\n\t This option is primarily useful when sending test requests to a", + "\t service that expects this header. Providing --haproxy-protocol", + "\t multiple times has no extra effect. Disable it again with", + "\t --no-haproxy-protocol.", + "\n\t Example:", + "\t curl --haproxy-protocol https://example.com", + "\n\t Added in 7.60.0. See also --proxy.", + "\n -I, --head", + "\t (HTTP FTP FILE) Fetch the headers only! HTTP-servers feature the", + "\t command HEAD which this uses to get nothing but the header of a", + "\t document. When used on an FTP or FILE file, curl displays the file", + "\t size and last modification time only. Providing --head multiple", + "\t times has no extra effect. Disable it again with --no-head.", + "\n\t Example:", + "\t curl -I https://example.com", + "\n\t See also --get, --verbose and --trace-ascii.", + "\n -H, --header
", + "\t (HTTP IMAP SMTP) Extra header to include in information sent. When", + "\t used within an HTTP request, it is added to the regular request", + "\t headers.", + "\n\t For an IMAP or SMTP MIME uploaded mail built with --form options,", + "\t it is prepended to the resulting MIME document, effectively", + "\t including it at the mail global level. It does not affect raw", + "\t uploaded mails.", + "\n\t You may specify any number of extra headers. Note that if you", + "\t should add a custom header that has the same name as one of the", + "\t internal ones curl would use, your externally set header is used", + "\t instead of the internal one. This allows you to make even trickier", + "\t stuff than curl would normally do. You should not replace", + "\t internally set headers without knowing perfectly well what you are", + "\t doing. Remove an internal header by giving a replacement without", + "\t content on the right side of the colon, as in: -H \"Host:\". If you", + "\t send the custom header with no-value then its header must be", + "\t terminated with a semicolon, such as -H \"X-Custom-Header;\" to send", + "\t \"X-Custom-Header:\".", + "\n\t curl makes sure that each header you add/replace is sent with the", + "\t proper end-of-line marker, you should thus not add that as a part", + "\t of the header content: do not add newlines or carriage returns,", + "\t they only mess things up for you. curl passes on the verbatim", + "\t string you give it without any filter or other safe guards. That", + "\t includes white space and control characters.", + "\n\t This option can take an argument in @filename style, which then", + "\t adds a header for each line in the input file. Using @- makes curl", + "\t read the header file from stdin.", + "\n\t Please note that most anti-spam utilities check the presence and", + "\t value of several MIME mail headers: these are \"From:\", \"To:\",", + "\t \"Date:\" and \"Subject:\" among others and should be added with this", + "\t option.", + "\n\t You need --proxy-header to send custom headers intended for an", + "\t HTTP proxy.", + "\n\t Passing on a \"Transfer-Encoding: chunked\" header when doing an", + "\t HTTP request with a request body, makes curl send the data using", + "\t chunked encoding.", + "\n\t WARNING: headers set with this option are set in all HTTP requests", + "\t - even after redirects are followed, like when told with", + "\t --location. This can lead to the header being sent to other hosts", + "\t than the original host, so sensitive headers should be used with", + "\t caution combined with following redirects. --header can be used", + "\t several times in a command line", + "\n\t Examples:", + "\t curl -H \"X-First-Name: Joe\" https://example.com", + "\t curl -H \"User-Agent: yes-please/2000\" https://example.com", + "\t curl -H \"Host:\" https://example.com", + "\t curl -H @headers.txt https://example.com", + "\n\t See also --user-agent and --referer.", + "\n -h, --help ", + "\t Usage help. List all curl command line options within the given", + "\t category.", + "\n\t If no argument is provided, curl displays the most important", + "\t command line arguments.", + "\n\t For category all, curl displays help for all options.", + "\n\t If category is specified, curl displays all available help", + "\t categories.", + "\n\t Example:", + "\t curl --help all", + "\n\t See also --verbose.", + "\n --hostpubmd5 ", + "\t (SFTP SCP) Pass a string containing 32 hexadecimal digits. The", + "\t string should be the 128 bit MD5 checksum of the remote host's", + "\t public key, curl refuses the connection with the host unless the", + "\t checksums match. If --hostpubmd5 is provided several times, the", + "\t last set value is used.", + "\n\t Example:", + "\t curl --hostpubmd5 e5c1c49020640a5ab0f2034854c321a8 sftp://example.com/", + "\n\t See also --hostpubsha256.", + "\n --hostpubsha256 ", + "\t (SFTP SCP) Pass a string containing a Base64-encoded SHA256 hash", + "\t of the remote host's public key. Curl refuses the connection with", + "\t the host unless the hashes match.", + "\n\t This feature requires libcurl to be built with libssh2 and does", + "\t not work with other SSH backends. If --hostpubsha256 is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --hostpubsha256 NDVkMTQxMGQ1ODdmMjQ3MjczYjAyOTY5MmRkMjVmNDQ= sftp://example.com/", + "\n\t Added in 7.80.0. See also --hostpubmd5.", + "\n --hsts ", + "\t (HTTPS) Enable HSTS for the transfer. If the filename points to an", + "\t existing HSTS cache file, that is used. After a completed", + "\t transfer, the cache is saved to the filename again if it has been", + "\t modified.", + "\n\t If curl is told to use HTTP:// for a transfer involving a hostname", + "\t that exists in the HSTS cache, it upgrades the transfer to use", + "\t HTTPS. Each HSTS cache entry has an individual life time after", + "\t which the upgrade is no longer performed.", + "\n\t Specify a \"\" filename (zero length) to avoid loading/saving and", + "\t make curl just handle HSTS in memory.", + "\n\t If this option is used several times, curl loads contents from all", + "\t the files but the last one is used for saving. --hsts can be used", + "\t several times in a command line", + "\n\t Example:", + "\t curl --hsts cache.txt https://example.com", + "\n\t Added in 7.74.0. See also --proto.", + "\n --http0.9", + "\t (HTTP) Accept an HTTP version 0.9 response.", + "\n\t HTTP/0.9 is a response without headers and therefore you can also", + "\t connect with this to non-HTTP servers and still get a response", + "\t since curl simply transparently downgrades - if allowed.", + "\n\t HTTP/0.9 is disabled by default (added in 7.66.0) Providing", + "\t --http0.9 multiple times has no extra effect. Disable it again", + "\t with --no-http0.9.", + "\n\t Example:", + "\t curl --http0.9 https://example.com", + "\n\t Added in 7.64.0. See also --http1.1, --http2 and --http3.", + "\n -0, --http1.0", + "\t (HTTP) Use HTTP version 1.0 instead of using its internally", + "\t preferred HTTP version. Providing --http1.0 multiple times has no", + "\t extra effect.", + "\n\t Example:", + "\t curl --http1.0 https://example.com", + "\n\t This option is mutually exclusive with --http1.1, --http2,", + "\t --http2-prior-knowledge and --http3. See also --http0.9 and", + "\t --http1.1.", + "\n --http1.1", + "\t (HTTP) Use HTTP version 1.1. This is the default with HTTP://", + "\t URLs. Providing --http1.1 multiple times has no extra effect.", + "\n\t Example:", + "\t curl --http1.1 https://example.com", + "\n\t This option is mutually exclusive with --http1.0, --http2,", + "\t --http2-prior-knowledge and --http3. See also --http1.0 and", + "\t --http0.9.", + "\n --http2", + "\t (HTTP) Use HTTP/2.", + "\n\t For HTTPS, this means curl negotiates HTTP/2 in the TLS handshake.", + "\t curl does this by default.", + "\n\t For HTTP, this means curl attempts to upgrade the request to", + "\t HTTP/2 using the Upgrade: request header.", + "\n\t When curl uses HTTP/2 over HTTPS, it does not itself insist on TLS", + "\t 1.2 or higher even though that is required by the specification. A", + "\t user can add this version requirement with --tlsv1.2. Providing", + "\t --http2 multiple times has no extra effect.", + "\n\t Example:", + "\t curl --http2 https://example.com", + "\n\t --http2 requires that libcurl is built to support HTTP/2. This", + "\t option is mutually exclusive with --http1.1, --http1.0,", + "\t --http2-prior-knowledge and --http3. See also --http1.1, --http3", + "\t and --no-alpn.", + "\n --http2-prior-knowledge", + "\t (HTTP) Issue a non-TLS HTTP requests using HTTP/2 directly without", + "\t HTTP/1.1 Upgrade. It requires prior knowledge that the server", + "\t supports HTTP/2 straight away. HTTPS requests still do HTTP/2 the", + "\t standard way with negotiated protocol version in the TLS", + "\t handshake. Providing --http2-prior-knowledge multiple times has no", + "\t extra effect. Disable it again with --no-http2-prior-knowledge.", + "\n\t Example:", + "\t curl --http2-prior-knowledge https://example.com", + "\n\t --http2-prior-knowledge requires that libcurl is built to support", + "\t HTTP/2. This option is mutually exclusive with --http1.1,", + "\t --http1.0, --http2 and --http3. See also --http2 and --http3.", + "\n --http3", + "\t (HTTP) Attempt HTTP/3 to the host in the URL, but fallback to", + "\t earlier HTTP versions if the HTTP/3 connection establishment", + "\t fails. HTTP/3 is only available for HTTPS and not for HTTP URLs.", + "\n\t This option allows a user to avoid using the Alt-Svc method of", + "\t upgrading to HTTP/3 when you know that the target speaks HTTP/3 on", + "\t the given host and port.", + "\n\t When asked to use HTTP/3, curl issues a separate attempt to use", + "\t older HTTP versions with a slight delay, so if the HTTP/3 transfer", + "\t fails or is slow, curl still tries to proceed with an older HTTP", + "\t version.", + "\n\t Use --http3-only for similar functionality without a fallback.", + "\t Providing --http3 multiple times has no extra effect.", + "\n\t Example:", + "\t curl --http3 https://example.com", + "\n\t --http3 requires that libcurl is built to support HTTP/3. This", + "\t option is mutually exclusive with --http1.1, --http1.0, --http2,", + "\t --http2-prior-knowledge and --http3-only. Added in 7.66.0. See", + "\t also --http1.1 and --http2.", + "\n --http3-only", + "\t (HTTP) Instructs curl to use HTTP/3 to the host in the URL, with", + "\t no fallback to earlier HTTP versions. HTTP/3 can only be used for", + "\t HTTPS and not for HTTP URLs. For HTTP, this option triggers an", + "\t error.", + "\n\t This option allows a user to avoid using the Alt-Svc method of", + "\t upgrading to HTTP/3 when you know that the target speaks HTTP/3 on", + "\t the given host and port.", + "\n\t This option makes curl fail if a QUIC connection cannot be", + "\t established, it does not attempt any other HTTP versions on its", + "\t own. Use --http3 for similar functionality with a fallback.", + "\t Providing --http3-only multiple times has no extra effect.", + "\n\t Example:", + "\t curl --http3-only https://example.com", + "\n\t --http3-only requires that libcurl is built to support HTTP/3.", + "\t This option is mutually exclusive with --http1.1, --http1.0,", + "\t --http2, --http2-prior-knowledge and --http3. Added in 7.88.0. See", + "\t also --http1.1, --http2 and --http3.", + "\n --ignore-content-length", + "\t (FTP HTTP) For HTTP, Ignore the Content-Length header. This is", + "\t particularly useful for servers running Apache 1.x, which reports", + "\t incorrect Content-Length for files larger than 2 gigabytes.", + "\n\t For FTP, this makes curl skip the SIZE command to figure out the", + "\t size before downloading a file.", + "\n\t This option does not work for HTTP if libcurl was built to use", + "\t hyper. Providing --ignore-content-length multiple times has no", + "\t extra effect. Disable it again with --no-ignore-content-length.", + "\n\t Example:", + "\t curl --ignore-content-length https://example.com", + "\n\t See also --ftp-skip-pasv-ip.", + "\n -i, --include", + "\t (HTTP FTP) Include response headers in the output. HTTP response", + "\t headers can include things like server name, cookies, date of the", + "\t document, HTTP version and more... With non-HTTP protocols, the", + "\t \"headers\" are other server communication.", + "\n\t To view the request headers, consider the --verbose option.", + "\t Providing --include multiple times has no extra effect. Disable it", + "\t again with --no-include.", + "\n\t Example:", + "\t curl -i https://example.com", + "\n\t See also --verbose.", + "\n -k, --insecure", + "\t (TLS SFTP SCP) By default, every secure connection curl makes is", + "\t verified to be secure before the transfer takes place. This option", + "\t makes curl skip the verification step and proceed without", + "\t checking.", + "\n\t When this option is not used for protocols using TLS, curl", + "\t verifies the server's TLS certificate before it continues: that", + "\t the certificate contains the right name which matches the hostname", + "\t used in the URL and that the certificate has been signed by a CA", + "\t certificate present in the cert store. See this online resource", + "\t for further details: https://curl.se/docs/sslcerts.html", + "\n\t For SFTP and SCP, this option makes curl skip the known_hosts", + "\t verification. known_hosts is a file normally stored in the user's", + "\t home directory in the \".ssh\" subdirectory, which contains", + "\t hostnames and their public keys.", + "\n\t WARNING: using this option makes the transfer insecure.", + "\n\t When curl uses secure protocols it trusts responses and allows for", + "\t example HSTS and Alt-Svc information to be stored and used", + "\t subsequently. Using --insecure can make curl trust and use such", + "\t information from malicious servers. Providing --insecure multiple", + "\t times has no extra effect. Disable it again with --no-insecure.", + "\n\t Example:", + "\t curl --insecure https://example.com", + "\n\t See also --proxy-insecure, --cacert and --capath.", + "\n --interface ", + "\t Perform the operation using a specified interface. You can enter", + "\t interface name, IP address or hostname. If you prefer to be", + "\t specific, you can use the following special syntax:", + "\n\t if!", + "\n\t\tInterface name. If the provided name does not match an", + "\t\texisting interface, curl returns with error 45.", + "\n\t host!", + "\n\t\tIP address or hostname.", + "\n\t ifhost!!", + "\n\t\tInterface name and IP address or hostname. This syntax", + "\t\trequires libcurl 8.9.0 or later.", + "\n\t\tIf the provided name does not match an existing interface,", + "\t\tcurl returns with error 45. curl does not support using", + "\t\tnetwork interface names for this option on Windows.", + "\n\t\tThat name resolve operation if a hostname is provided does not", + "\t\tuse DNS-over-HTTPS even if --doh-url is set.", + "\n\t\tOn Linux this option can be used to specify a VRF (Virtual", + "\t\tRouting and Forwarding) device, but the binary then needs to", + "\t\teither have the CAP_NET_RAW capability set or to be run as", + "\t\troot.", + "\n\t If --interface is provided several times, the last set value is", + "\t used.", + "\n\t Examples:", + "\t curl --interface eth0 https://example.com", + "\t curl --interface \"host!10.0.0.1\" https://example.com", + "\t curl --interface \"if!enp3s0\" https://example.com", + "\n\t See also --dns-interface.", + "\n --ip-tos ", + "\t (All) Set Type of Service (TOS) for IPv4 or Traffic Class for", + "\t IPv6.", + "\n\t The values allowed for can be a numeric value between 1", + "\t and 255 or one of the following:", + "\n\t CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, AF11, AF12, AF13, AF21,", + "\t AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43, EF, VOICE-ADMIT,", + "\t ECT1, ECT0, CE, LE, LOWCOST, LOWDELAY, THROUGHPUT, RELIABILITY,", + "\t MINCOST If --ip-tos is provided several times, the last set value", + "\t is used.", + "\n\t Example:", + "\t curl --ip-tos CS5 https://example.com", + "\n\t Added in 8.9.0. See also --tcp-nodelay and --vlan-priority.", + "\n --ipfs-gateway ", + "\t (IPFS) Specify which gateway to use for IPFS and IPNS URLs. Not", + "\t specifying this instead makes curl check if the IPFS_GATEWAY", + "\t environment variable is set, or if a \"~/.ipfs/gateway\" file", + "\t holding the gateway URL exists.", + "\n\t If you run a local IPFS node, this gateway is by default available", + "\t under \"http://localhost:8080\". A full example URL would look like:", + "\n\t\tcurl --ipfs-gateway http://localhost:8080 ipfs://bafybeigagd5nmnn2iys2f3doro7ydrevyr2mzarwidgadawmamiteydbzi", + "\n\t There are many public IPFS gateways. See for example:", + "\t https://ipfs.github.io/public-gateway-checker/", + "\n\t If you opt to go for a remote gateway you need to be aware that", + "\t you completely trust the gateway. This might be fine in local", + "\t gateways that you host yourself. With remote gateways there could", + "\t potentially be malicious actors returning you data that does not", + "\t match the request you made, inspect or even interfere with the", + "\t request. You may not notice this when using curl. A mitigation", + "\t could be to go for a \"trustless\" gateway. This means you locally", + "\t verify that the data. Consult the docs page on trusted vs", + "\t trustless:", + "\t https://docs.ipfs.tech/reference/http/gateway/#trusted-vs-trustless", + "\t If --ipfs-gateway is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --ipfs-gateway https://example.com ipfs://", + "\n\t Added in 8.4.0. See also --help and --manual.", + "\n -4, --ipv4", + "\t Use IPv4 addresses only when resolving hostnames, and not for", + "\t example try IPv6. Providing --ipv4 multiple times has no extra", + "\t effect.", + "\n\t Example:", + "\t curl --ipv4 https://example.com", + "\n\t This option is mutually exclusive with --ipv6. See also --http1.1", + "\t and --http2.", + "\n -6, --ipv6", + "\t Use IPv6 addresses only when resolving hostnames, and not for", + "\t example try IPv4.", + "\n\t Your resolver may respond to an IPv6-only resolve request by", + "\t returning IPv6 addresses that contain \"mapped\" IPv4 addresses for", + "\t compatibility purposes. macOS is known to do this. Providing", + "\t --ipv6 multiple times has no extra effect.", + "\n\t Example:", + "\t curl --ipv6 https://example.com", + "\n\t This option is mutually exclusive with --ipv4. See also --http1.1", + "\t and --http2.", + "\n --json ", + "\t (HTTP) Sends the specified JSON data in a POST request to the HTTP", + "\t server. --json works as a shortcut for passing on these three", + "\t options:", + "\n\t\t--data [arg]", + "\t\t--header \"Content-Type: application/json\"", + "\t\t--header \"Accept: application/json\"", + "\n\t There is no verification that the passed in data is actual JSON or", + "\t that the syntax is correct.", + "\n\t If you start the data with the letter @, the rest should be a", + "\t filename to read the data from, or a single dash (-) if you want", + "\t curl to read the data from stdin. Posting data from a file named", + "\t 'foobar' would thus be done with --json @foobar and to instead", + "\t read the data from stdin, use --json @-.", + "\n\t If this option is used more than once on the same command line,", + "\t the additional data pieces are concatenated to the previous before", + "\t sending.", + "\n\t The headers this option sets can be overridden with --header as", + "\t usual. --json can be used several times in a command line", + "\n\t Examples:", + "\t curl --json '{ \"drink\": \"coffe\" }' https://example.com", + "\t curl --json '{ \"drink\":' --json ' \"coffe\" }' https://example.com", + "\t curl --json @prepared https://example.com", + "\t curl --json @- https://example.com < json.txt", + "\n\t This option is mutually exclusive with --form, --head and", + "\t --upload-file. Added in 7.82.0. See also --data-binary and", + "\t --data-raw.", + "\n -j, --junk-session-cookies", + "\t (HTTP) When curl is told to read cookies from a given file, this", + "\t option makes it discard all \"session cookies\". This has the same", + "\t effect as if a new session is started. Typical browsers discard", + "\t session cookies when they are closed down. Providing", + "\t --junk-session-cookies multiple times has no extra effect. Disable", + "\t it again with --no-junk-session-cookies.", + "\n\t Example:", + "\t curl --junk-session-cookies -b cookies.txt https://example.com", + "\n\t See also --cookie and --cookie-jar.", + "\n --keepalive-cnt ", + "\t Set the maximum number of keepalive probes TCP should send but get", + "\t no response before dropping the connection. This option is usually", + "\t used in conjunction with --keepalive-time.", + "\n\t This option is supported on Linux, *BSD/macOS, Windows", + "\t >=10.0.16299, Solaris 11.4, and recent AIX, HP-UX and more. This", + "\t option has no effect if --no-keepalive is used.", + "\n\t If unspecified, the option defaults to 9. If --keepalive-cnt is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --keepalive-cnt 3 https://example.com", + "\n\t Added in 8.9.0. See also --keepalive-time and --no-keepalive.", + "\n --keepalive-time ", + "\t Set the time a connection needs to remain idle before sending", + "\t keepalive probes and the time between individual keepalive probes.", + "\t It is currently effective on operating systems offering the", + "\t \"TCP_KEEPIDLE\" and \"TCP_KEEPINTVL\" socket options (meaning Linux,", + "\t *BSD/macOS, Windows, Solaris, and recent AIX, HP-UX and more).", + "\t Keepalive is used by the TCP stack to detect broken networks on", + "\t idle connections. The number of missed keepalive probes before", + "\t declaring the connection down is OS dependent and is commonly 8", + "\t (*BSD/macOS/AIX), 9 (Linux/AIX) or 5/10 (Windows), and this number", + "\t can be changed by specifying the curl option \"keepalive-cnt\". Note", + "\t that this option has no effect if --no-keepalive is used.", + "\n\t If unspecified, the option defaults to 60 seconds. If", + "\t --keepalive-time is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --keepalive-time 20 https://example.com", + "\n\t See also --no-keepalive, --keepalive-cnt and --max-time.", + "\n --key ", + "\t (TLS SSH) Private key filename. Allows you to provide your private", + "\t key in this separate file. For SSH, if not specified, curl tries", + "\t the following candidates in order: \"~/.ssh/id_rsa\",", + "\t \"~/.ssh/id_dsa\", \"./id_rsa\", \"./id_dsa\".", + "\n\t If curl is built against OpenSSL library, and the engine pkcs11 is", + "\t available, then a PKCS#11 URI (RFC 7512) can be used to specify a", + "\t private key located in a PKCS#11 device. A string beginning with", + "\t \"pkcs11:\" is interpreted as a PKCS#11 URI. If a PKCS#11 URI is", + "\t provided, then the --engine option is set as \"pkcs11\" if none was", + "\t provided and the --key-type option is set as \"ENG\" if none was", + "\t provided.", + "\n\t If curl is built against Secure Transport or Schannel then this", + "\t option is ignored for TLS protocols (HTTPS, etc). Those backends", + "\t expect the private key to be already present in the keychain or", + "\t PKCS#12 file containing the certificate. If --key is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --cert certificate --key here https://example.com", + "\n\t See also --key-type and --cert.", + "\n --key-type ", + "\t (TLS) Private key file type. Specify which type your --key", + "\t provided private key is. DER, PEM, and ENG are supported. If not", + "\t specified, PEM is assumed. If --key-type is provided several", + "\t times, the last set value is used.", + "\n\t Example:", + "\t curl --key-type DER --key here https://example.com", + "\n\t See also --key.", + "\n --krb ", + "\t (FTP) Enable Kerberos authentication and use. The level must be", + "\t entered and should be one of 'clear', 'safe', 'confidential', or", + "\t 'private'. Should you use a level that is not one of these,", + "\t 'private' is used. If --krb is provided several times, the last", + "\t set value is used.", + "\n\t Example:", + "\t curl --krb clear ftp://example.com/", + "\n\t --krb requires that libcurl is built to support Kerberos. See also", + "\t --delegation and --ssl.", + "\n --libcurl ", + "\t Append this option to any ordinary curl command line, and you get", + "\t libcurl-using C source code written to the file that does the", + "\t equivalent of what your command-line operation does!", + "\n\t This option is global and does not need to be specified for each", + "\t use of --next. If --libcurl is provided several times, the last", + "\t set value is used.", + "\n\t Example:", + "\t curl --libcurl client.c https://example.com", + "\n\t See also --verbose.", + "\n --limit-rate ", + "\t Specify the maximum transfer rate you want curl to use - for both", + "\t downloads and uploads. This feature is useful if you have a", + "\t limited pipe and you would like your transfer not to use your", + "\t entire bandwidth. To make it slower than it otherwise would be.", + "\n\t The given speed is measured in bytes/second, unless a suffix is", + "\t appended. Appending 'k' or 'K' counts the number as kilobytes, 'm'", + "\t or 'M' makes it megabytes, while 'g' or 'G' makes it gigabytes.", + "\t The suffixes (k, M, G, T, P) are 1024 based. For example 1k is", + "\t 1024. Examples: 200K, 3m and 1G.", + "\n\t The rate limiting logic works on averaging the transfer speed to", + "\t no more than the set threshold over a period of multiple seconds.", + "\n\t If you also use the --speed-limit option, that option takes", + "\t precedence and might cripple the rate-limiting slightly, to help", + "\t keeping the speed-limit logic working. If --limit-rate is provided", + "\t several times, the last set value is used.", + "\n\t Examples:", + "\t curl --limit-rate 100K https://example.com", + "\t curl --limit-rate 1000 https://example.com", + "\t curl --limit-rate 10M https://example.com", + "\n\t See also --rate, --speed-limit and --speed-time.", + "\n -l, --list-only", + "\t (FTP POP3 SFTP FILE) When listing an FTP directory, force a", + "\t name-only view. Maybe particularly useful if the user wants to", + "\t machine-parse the contents of an FTP directory since the normal", + "\t directory view does not use a standard look or format. When used", + "\t like this, the option causes an NLST command to be sent to the", + "\t server instead of LIST.", + "\n\t Note: Some FTP servers list only files in their response to NLST;", + "\t they do not include sub-directories and symbolic links.", + "\n\t When listing an SFTP directory, this switch forces a name-only", + "\t view, one per line. This is especially useful if the user wants to", + "\t machine-parse the contents of an SFTP directory since the normal", + "\t directory view provides more information than just filenames.", + "\n\t When retrieving a specific email from POP3, this switch forces a", + "\t LIST command to be performed instead of RETR. This is particularly", + "\t useful if the user wants to see if a specific message-id exists on", + "\t the server and what size it is.", + "\n\t For FILE, this option has no effect yet as directories are always", + "\t listed in this mode.", + "\n\t Note: When combined with --request, this option can be used to", + "\t send a UIDL command instead, so the user may use the email's", + "\t unique identifier rather than its message-id to make the request.", + "\t Providing --list-only multiple times has no extra effect. Disable", + "\t it again with --no-list-only.", + "\n\t Example:", + "\t curl --list-only ftp://example.com/dir/", + "\n\t See also --quote and --request.", + "\n --local-port ", + "\t Set a preferred single number or range (FROM-TO) of local port", + "\t numbers to use for the connection(s). Note that port numbers by", + "\t nature are a scarce resource so setting this range to something", + "\t too narrow might cause unnecessary connection setup failures. If", + "\t --local-port is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --local-port 1000-3000 https://example.com", + "\n\t See also --globoff.", + "\n -L, --location", + "\t (HTTP) If the server reports that the requested page has moved to", + "\t a different location (indicated with a Location: header and a 3XX", + "\t response code), this option makes curl redo the request on the new", + "\t place. If used together with --include or --head, headers from all", + "\t requested pages are shown.", + "\n\t When authentication is used, curl only sends its credentials to", + "\t the initial host. If a redirect takes curl to a different host, it", + "\t does not get the user+password pass on. See also", + "\t --location-trusted on how to change this.", + "\n\t Limit the amount of redirects to follow by using the --max-redirs", + "\t option.", + "\n\t When curl follows a redirect and if the request is a POST, it", + "\t sends the following request with a GET if the HTTP response was", + "\t 301, 302, or 303. If the response code was any other 3xx code,", + "\t curl resends the following request using the same unmodified", + "\t method.", + "\n\t You can tell curl to not change POST requests to GET after a 30x", + "\t response by using the dedicated options for that: --post301,", + "\t --post302 and --post303.", + "\n\t The method set with --request overrides the method curl would", + "\t otherwise select to use. Providing --location multiple times has", + "\t no extra effect. Disable it again with --no-location.", + "\n\t Example:", + "\t curl -L https://example.com", + "\n\t See also --resolve and --alt-svc.", + "\n --location-trusted", + "\t (HTTP) Like --location, but allows sending the name + password to", + "\t all hosts that the site may redirect to. This may or may not", + "\t introduce a security breach if the site redirects you to a site to", + "\t which you send your authentication info (which is clear-text in", + "\t the case of HTTP Basic authentication). Providing", + "\t --location-trusted multiple times has no extra effect. Disable it", + "\t again with --no-location-trusted.", + "\n\t Example:", + "\t curl --location-trusted -u user:password https://example.com", + "\n\t See also --user.", + "\n --login-options ", + "\t (IMAP LDAP POP3 SMTP) Specify the login options to use during", + "\t server authentication.", + "\n\t You can use login options to specify protocol specific options", + "\t that may be used during authentication. At present only IMAP, POP3", + "\t and SMTP support login options. For more information about login", + "\t options please see RFC 2384, RFC 5092 and the IETF draft", + "\t https://datatracker.ietf.org/doc/html/draft-earhart-url-smtp-00", + "\n\t Since 8.2.0, IMAP supports the login option \"AUTH=+LOGIN\". With", + "\t this option, curl uses the plain (not SASL) \"LOGIN IMAP\" command", + "\t even if the server advertises SASL authentication. Care should be", + "\t taken in using this option, as it sends your password over the", + "\t network in plain text. This does not work if the IMAP server", + "\t disables the plain \"LOGIN\" (e.g. to prevent password snooping). If", + "\t --login-options is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --login-options 'AUTH=*' imap://example.com", + "\n\t See also --user.", + "\n --mail-auth
", + "\t (SMTP) Specify a single address. This is used to specify the", + "\t authentication address (identity) of a submitted message that is", + "\t being relayed to another server. If --mail-auth is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --mail-auth user@example.com -T mail smtp://example.com/", + "\n\t See also --mail-rcpt and --mail-from.", + "\n --mail-from
", + "\t (SMTP) Specify a single address that the given mail should get", + "\t sent from. If --mail-from is provided several times, the last set", + "\t value is used.", + "\n\t Example:", + "\t curl --mail-from user@example.com -T mail smtp://example.com/", + "\n\t See also --mail-rcpt and --mail-auth.", + "\n --mail-rcpt
", + "\t (SMTP) Specify a single email address, username or mailing list", + "\t name. Repeat this option several times to send to multiple", + "\t recipients.", + "\n\t When performing an address verification (VRFY command), the", + "\t recipient should be specified as the username or username and", + "\t domain (as per Section 3.5 of RFC 5321).", + "\n\t When performing a mailing list expand (EXPN command), the", + "\t recipient should be specified using the mailing list name, such as", + "\t \"Friends\" or \"London-Office\". --mail-rcpt can be used several", + "\t times in a command line", + "\n\t Example:", + "\t curl --mail-rcpt user@example.net smtp://example.com", + "\n\t See also --mail-rcpt-allowfails.", + "\n --mail-rcpt-allowfails", + "\t (SMTP) When sending data to multiple recipients, by default curl", + "\t aborts SMTP conversation if at least one of the recipients causes", + "\t RCPT TO command to return an error.", + "\n\t The default behavior can be changed by passing", + "\t --mail-rcpt-allowfails command-line option which makes curl ignore", + "\t errors and proceed with the remaining valid recipients.", + "\n\t If all recipients trigger RCPT TO failures and this flag is", + "\t specified, curl still aborts the SMTP conversation and returns the", + "\t error received from to the last RCPT TO command. Providing", + "\t --mail-rcpt-allowfails multiple times has no extra effect. Disable", + "\t it again with --no-mail-rcpt-allowfails.", + "\n\t Example:", + "\t curl --mail-rcpt-allowfails --mail-rcpt dest@example.com smtp://example.com", + "\n\t Added in 7.69.0. See also --mail-rcpt.", + "\n -M, --manual", + "\t Manual. Display the huge help text.", + "\n\t Example:", + "\t curl --manual", + "\n\t See also --verbose, --libcurl and --trace.", + "\n --max-filesize ", + "\t (FTP HTTP MQTT) Specify the maximum size (in bytes) of a file to", + "\t download. If the file requested is larger than this value, the", + "\t transfer does not start and curl returns with exit code 63.", + "\n\t A size modifier may be used. For example, Appending 'k' or 'K'", + "\t counts the number as kilobytes, 'm' or 'M' makes it megabytes,", + "\t while 'g' or 'G' makes it gigabytes. Examples: 200K, 3m and 1G.", + "\n\t NOTE: before curl 8.4.0, when the file size is not known prior to", + "\t download, for such files this option has no effect even if the", + "\t file transfer ends up being larger than this given limit.", + "\n\t Starting with curl 8.4.0, this option aborts the transfer if it", + "\t reaches the threshold during transfer. If --max-filesize is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --max-filesize 100K https://example.com", + "\n\t See also --limit-rate.", + "\n --max-redirs ", + "\t (HTTP) Set maximum number of redirections to follow. When", + "\t --location is used, to prevent curl from following too many", + "\t redirects, by default, the limit is set to 50 redirects. Set this", + "\t option to -1 to make it unlimited. If --max-redirs is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --max-redirs 3 --location https://example.com", + "\n\t See also --location.", + "\n -m, --max-time ", + "\t Set maximum time in seconds that you allow each transfer to take.", + "\t Prevents your batch jobs from hanging for hours due to slow", + "\t networks or links going down. This option accepts decimal values.", + "\n\t If you enable retrying the transfer (--retry) then the maximum", + "\t time counter is reset each time the transfer is retried. You can", + "\t use --retry-max-time to limit the retry time.", + "\n\t The decimal value needs to provided using a dot (.) as decimal", + "\t separator - not the local version even if it might be using", + "\t another separator. If --max-time is provided several times, the", + "\t last set value is used.", + "\n\t Examples:", + "\t curl --max-time 10 https://example.com", + "\t curl --max-time 2.92 https://example.com", + "\n\t See also --connect-timeout and --retry-max-time.", + "\n --metalink", + "\t This option was previously used to specify a Metalink resource.", + "\t Metalink support is disabled in curl for security reasons (added", + "\t in 7.78.0). If --metalink is provided several times, the last set", + "\t value is used.", + "\n\t Example:", + "\t curl --metalink file https://example.com", + "\n\t See also --parallel.", + "\n --mptcp", + "\t Enables the use of Multipath TCP (MPTCP) for connections. MPTCP is", + "\t an extension to the standard TCP that allows multiple TCP streams", + "\t over different network paths between the same source and", + "\t destination. This can enhance bandwidth and improve reliability by", + "\t using multiple paths simultaneously.", + "\n\t MPTCP is beneficial in networks where multiple paths exist between", + "\t clients and servers, such as mobile networks where a device may", + "\t switch between WiFi and cellular data or in wired networks with", + "\t multiple Internet Service Providers.", + "\n\t This option is currently only supported on Linux starting from", + "\t kernel 5.6. Only TCP connections are modified, hence this option", + "\t does not effect HTTP/3 (QUIC) or UDP connections.", + "\n\t The server curl connects to must also support MPTCP. If not, the", + "\t connection seamlessly falls back to TCP. Providing --mptcp", + "\t multiple times has no extra effect. Disable it again with", + "\t --no-mptcp.", + "\n\t Example:", + "\t curl --mptcp https://example.com", + "\n\t Added in 8.9.0. See also --tcp-fastopen.", + "\n --negotiate", + "\t (HTTP) Enable Negotiate (SPNEGO) authentication.", + "\n\t This option requires a library built with GSS-API or SSPI support.", + "\t Use --version to see if your curl supports GSS-API/SSPI or SPNEGO.", + "\n\t When using this option, you must also provide a fake --user option", + "\t to activate the authentication code properly. Sending a '-u :' is", + "\t enough as the username and password from the --user option are not", + "\t actually used. Providing --negotiate multiple times has no extra", + "\t effect.", + "\n\t Example:", + "\t curl --negotiate -u : https://example.com", + "\n\t See also --basic, --ntlm, --anyauth and --proxy-negotiate.", + "\n -n, --netrc", + "\t Make curl scan the .netrc file in the user's home directory for", + "\t login name and password. This is typically used for FTP on Unix.", + "\t If used with HTTP, curl enables user authentication. See netrc(5)", + "\t and ftp(1) for details on the file format. Curl does not complain", + "\t if that file does not have the right permissions (it should be", + "\t neither world- nor group-readable). The environment variable", + "\t \"HOME\" is used to find the home directory.", + "\n\t On Windows two filenames in the home directory are checked: .netrc", + "\t and _netrc, preferring the former. Older versions on Windows", + "\t checked for _netrc only.", + "\n\t A quick and simple example of how to setup a .netrc to allow curl", + "\t to FTP to the machine host.domain.com with username 'myself' and", + "\t password 'secret' could look similar to:", + "\n\t\tmachine host.domain.com", + "\t\tlogin myself", + "\t\tpassword secret", + "\n\t Providing --netrc multiple times has no extra effect. Disable it", + "\t again with --no-netrc.", + "\n\t Example:", + "\t curl --netrc https://example.com", + "\n\t This option is mutually exclusive with --netrc-file and", + "\t --netrc-optional. See also --netrc-file, --config and --user.", + "\n --netrc-file ", + "\t Set the netrc file to use. Similar to --netrc, except that you", + "\t also provide the path (absolute or relative).", + "\n\t It abides by --netrc-optional if specified. If --netrc-file is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --netrc-file netrc https://example.com", + "\n\t This option is mutually exclusive with --netrc. See also --netrc,", + "\t --user and --config.", + "\n --netrc-optional", + "\t Similar to --netrc, but this option makes the .netrc usage", + "\t optional and not mandatory as the --netrc option does. Providing", + "\t --netrc-optional multiple times has no extra effect. Disable it", + "\t again with --no-netrc-optional.", + "\n\t Example:", + "\t curl --netrc-optional https://example.com", + "\n\t This option is mutually exclusive with --netrc. See also", + "\t --netrc-file.", + "\n -:, --next", + "\t Use a separate operation for the following URL and associated", + "\t options. This allows you to send several URL requests, each with", + "\t their own specific options, for example, such as different", + "\t usernames or custom requests for each.", + "\n\t --next resets all local options and only global ones have their", + "\t values survive over to the operation following the --next", + "\t instruction. Global options include --verbose, --trace,", + "\t --trace-ascii and --fail-early.", + "\n\t For example, you can do both a GET and a POST in a single command", + "\t line:", + "\n\t\tcurl www1.example.com --next -d postthis www2.example.com", + "\n\t --next can be used several times in a command line", + "\n\t Examples:", + "\t curl https://example.com --next -d postthis www2.example.com", + "\t curl -I https://example.com --next https://example.net/", + "\n\t See also --parallel and --config.", + "\n --no-alpn", + "\t (HTTPS) Disable the ALPN TLS extension. ALPN is enabled by default", + "\t if libcurl was built with an SSL library that supports ALPN. ALPN", + "\t is used by a libcurl that supports HTTP/2 to negotiate HTTP/2", + "\t support with the server during https sessions.", + "\n\t Note that this is the negated option name documented. You can use", + "\t --alpn to enable ALPN. Providing --no-alpn multiple times has no", + "\t extra effect. Disable it again with --alpn.", + "\n\t Example:", + "\t curl --no-alpn https://example.com", + "\n\t --no-alpn requires that libcurl is built to support TLS. See also", + "\t --no-npn and --http2.", + "\n -N, --no-buffer", + "\t Disables the buffering of the output stream. In normal work", + "\t situations, curl uses a standard buffered output stream that has", + "\t the effect that it outputs the data in chunks, not necessarily", + "\t exactly when the data arrives. Using this option disables that", + "\t buffering.", + "\n\t Note that this is the negated option name documented. You can use", + "\t --buffer to enable buffering again. Providing --no-buffer multiple", + "\t times has no extra effect. Disable it again with --buffer.", + "\n\t Example:", + "\t curl --no-buffer https://example.com", + "\n\t See also --progress-bar.", + "\n --no-clobber", + "\t When used in conjunction with the --output, --remote-header-name,", + "\t --remote-name, or --remote-name-all options, curl avoids", + "\t overwriting files that already exist. Instead, a dot and a number", + "\t gets appended to the name of the file that would be created, up to", + "\t filename.100 after which it does not create any file.", + "\n\t Note that this is the negated option name documented. You can thus", + "\t use --clobber to enforce the clobbering, even if", + "\t --remote-header-name is specified. Providing --no-clobber multiple", + "\t times has no extra effect. Disable it again with --clobber.", + "\n\t Example:", + "\t curl --no-clobber --output local/dir/file https://example.com", + "\n\t Added in 7.83.0. See also --output and --remote-name.", + "\n --no-keepalive", + "\t Disables the use of keepalive messages on the TCP connection. curl", + "\t otherwise enables them by default.", + "\n\t Note that this is the negated option name documented. You can thus", + "\t use --keepalive to enforce keepalive. Providing --no-keepalive", + "\t multiple times has no extra effect. Disable it again with", + "\t --keepalive.", + "\n\t Example:", + "\t curl --no-keepalive https://example.com", + "\n\t See also --keepalive-time and --keepalive-cnt.", + "\n --no-npn", + "\t (HTTPS) curl never uses NPN, this option has no effect (added in", + "\t 7.86.0).", + "\n\t Disable the NPN TLS extension. NPN is enabled by default if", + "\t libcurl was built with an SSL library that supports NPN. NPN is", + "\t used by a libcurl that supports HTTP/2 to negotiate HTTP/2 support", + "\t with the server during https sessions. Providing --no-npn multiple", + "\t times has no extra effect. Disable it again with --npn.", + "\n\t Example:", + "\t curl --no-npn https://example.com", + "\n\t --no-npn requires that libcurl is built to support TLS. See also", + "\t --no-alpn and --http2.", + "\n --no-progress-meter", + "\t Option to switch off the progress meter output without muting or", + "\t otherwise affecting warning and informational messages like", + "\t --silent does.", + "\n\t Note that this is the negated option name documented. You can thus", + "\t use --progress-meter to enable the progress meter again. Providing", + "\t --no-progress-meter multiple times has no extra effect. Disable it", + "\t again with --progress-meter.", + "\n\t Example:", + "\t curl --no-progress-meter -o store https://example.com", + "\n\t Added in 7.67.0. See also --verbose and --silent.", + "\n --no-sessionid", + "\t (TLS) Disable curl's use of SSL session-ID caching. By default all", + "\t transfers are done using the cache. Note that while nothing should", + "\t ever get hurt by attempting to reuse SSL session-IDs, there seem", + "\t to be broken SSL implementations in the wild that may require you", + "\t to disable this in order for you to succeed.", + "\n\t Note that this is the negated option name documented. You can thus", + "\t use --sessionid to enforce session-ID caching. Providing", + "\t --no-sessionid multiple times has no extra effect. Disable it", + "\t again with --sessionid.", + "\n\t Example:", + "\t curl --no-sessionid https://example.com", + "\n\t See also --insecure.", + "\n --noproxy ", + "\t Comma-separated list of hosts for which not to use a proxy, if one", + "\t is specified. The only wildcard is a single \"*\" character, which", + "\t matches all hosts, and effectively disables the proxy. Each name", + "\t in this list is matched as either a domain which contains the", + "\t hostname, or the hostname itself. For example, \"local.com\" would", + "\t match \"local.com\", \"local.com:80\", and \"www.local.com\", but not", + "\t \"www.notlocal.com\".", + "\n\t This option overrides the environment variables that disable the", + "\t proxy (\"no_proxy\" and \"NO_PROXY\"). If there is an environment", + "\t variable disabling a proxy, you can set the no proxy list to \"\" to", + "\t override it.", + "\n\t IP addresses specified to this option can be provided using CIDR", + "\t notation (added in 7.86.0): an appended slash and number specifies", + "\t the number of network bits out of the address to use in the", + "\t comparison. For example \"192.168.0.0/16\" would match all addresses", + "\t starting with \"192.168\". If --noproxy is provided several times,", + "\t the last set value is used.", + "\n\t Example:", + "\t curl --noproxy \"www.example\" https://example.com", + "\n\t See also --proxy.", + "\n --ntlm", + "\t (HTTP) Use NTLM authentication. The NTLM authentication method was", + "\t designed by Microsoft and is used by IIS web servers. It is a", + "\t proprietary protocol, reverse-engineered by clever people and", + "\t implemented in curl based on their efforts. This kind of behavior", + "\t should not be endorsed, you should encourage everyone who uses", + "\t NTLM to switch to a public and documented authentication method", + "\t instead, such as Digest.", + "\n\t If you want to enable NTLM for your proxy authentication, then use", + "\t --proxy-ntlm. Providing --ntlm multiple times has no extra effect.", + "\n\t Example:", + "\t curl --ntlm -u user:password https://example.com", + "\n\t --ntlm requires that libcurl is built to support TLS. This option", + "\t is mutually exclusive with --basic, --negotiate, --digest and", + "\t --anyauth. See also --proxy-ntlm.", + "\n --ntlm-wb", + "\t (HTTP) Deprecated option (added in 8.8.0).", + "\n\t Enabled NTLM much in the style --ntlm does, but handed over the", + "\t authentication to a separate executable that was executed when", + "\t needed. Providing --ntlm-wb multiple times has no extra effect.", + "\n\t Example:", + "\t curl --ntlm-wb -u user:password https://example.com", + "\n\t See also --ntlm and --proxy-ntlm.", + "\n --oauth2-bearer ", + "\t (IMAP LDAP POP3 SMTP HTTP) Specify the Bearer Token for OAUTH 2.0", + "\t server authentication. The Bearer Token is used in conjunction", + "\t with the username which can be specified as part of the --url or", + "\t --user options.", + "\n\t The Bearer Token and username are formatted according to RFC 6750.", + "\t If --oauth2-bearer is provided several times, the last set value", + "\t is used.", + "\n\t Example:", + "\t curl --oauth2-bearer \"mF_9.B5f-4.1JqM\" https://example.com", + "\n\t See also --basic, --ntlm and --digest.", + "\n -o, --output ", + "\t Write output to the given file instead of stdout. If you are using", + "\t globbing to fetch multiple documents, you should quote the URL and", + "\t you can use \"#\" followed by a number in the filename. That", + "\t variable is then replaced with the current string for the URL", + "\t being fetched. Like in:", + "\n\t\tcurl \"http://{one,two}.example.com\" -o \"file_#1.txt\"", + "\n\t or use several variables like:", + "\n\t\tcurl \"http://{site,host}.host[1-5].example\" -o \"#1_#2\"", + "\n\t You may use this option as many times as the number of URLs you", + "\t have. For example, if you specify two URLs on the same command", + "\t line, you can use it like this:", + "\n\t\tcurl -o aa example.com -o bb example.net", + "\n\t and the order of the -o options and the URLs does not matter, just", + "\t that the first -o is for the first URL and so on, so the above", + "\t command line can also be written as", + "\n\t\tcurl example.com example.net -o aa -o bb", + "\n\t See also the --create-dirs option to create the local directories", + "\t dynamically. Specifying the output as '-' (a single dash) passes", + "\t the output to stdout.", + "\n\t To suppress response bodies, you can redirect output to /dev/null:", + "\n\t\tcurl example.com -o /dev/null", + "\n\t Or for Windows:", + "\n\t\tcurl example.com -o nul", + "\n\t Specify the filename as single minus to force the output to", + "\t stdout, to override curl's internal binary output in terminal", + "\t prevention:", + "\n\t\tcurl https://example.com/jpeg -o -", + "\n\t --output is associated with a single URL. Use it once per URL when", + "\t you use several URLs in a command line.", + "\n\t Examples:", + "\t curl -o file https://example.com", + "\t curl \"http://{one,two}.example.com\" -o \"file_#1.txt\"", + "\t curl \"http://{site,host}.host[1-5].example\" -o \"#1_#2\"", + "\t curl -o file https://example.com -o file2 https://example.net", + "\n\t See also --remote-name, --remote-name-all and", + "\t --remote-header-name.", + "\n --output-dir ", + "\t Specify the directory in which files should be stored, when", + "\t --remote-name or --output are used.", + "\n\t The given output directory is used for all URLs and output options", + "\t on the command line, up until the first --next.", + "\n\t If the specified target directory does not exist, the operation", + "\t fails unless --create-dirs is also used. If --output-dir is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --output-dir \"tmp\" -O https://example.com", + "\n\t Added in 7.73.0. See also --remote-name and --remote-header-name.", + "\n -Z, --parallel", + "\t Makes curl perform all transfers in parallel as compared to the", + "\t regular serial manner. Parallel transfer means that curl runs up", + "\t to N concurrent transfers simultaneously and if there are more", + "\t than N transfers to handle, it starts new ones when earlier", + "\t transfers finish.", + "\n\t With parallel transfers, the progress meter output is different", + "\t than when doing serial transfers, as it then displays the transfer", + "\t status for multiple transfers in a single line.", + "\n\t The maximum amount of concurrent transfers is set with", + "\t --parallel-max and it defaults to 50.", + "\n\t This option is global and does not need to be specified for each", + "\t use of --next. Providing --parallel multiple times has no extra", + "\t effect. Disable it again with --no-parallel.", + "\n\t Example:", + "\t curl --parallel https://example.com -o file1 https://example.com -o file2", + "\n\t Added in 7.66.0. See also --next, --verbose, --parallel-max and", + "\t --parallel-immediate.", + "\n --parallel-immediate", + "\t When doing parallel transfers, this option instructs curl to", + "\t prefer opening up more connections in parallel at once rather than", + "\t waiting to see if new transfers can be added as multiplexed", + "\t streams on another connection.", + "\n\t By default, without this option set, curl prefers to wait a little", + "\t and multiplex new transfers over existing connections. It keeps", + "\t the number of connections low at the expense of risking a slightly", + "\t slower transfer startup.", + "\n\t This option is global and does not need to be specified for each", + "\t use of --next. Providing --parallel-immediate multiple times has", + "\t no extra effect. Disable it again with --no-parallel-immediate.", + "\n\t Example:", + "\t curl --parallel-immediate -Z https://example.com -o file1 https://example.com -o file2", + "\n\t Added in 7.68.0. See also --parallel and --parallel-max.", + "\n --parallel-max ", + "\t When asked to do parallel transfers, using --parallel, this option", + "\t controls the maximum amount of transfers to do simultaneously.", + "\n\t The default is 50. 300 is the largest supported value.", + "\n\t This option is global and does not need to be specified for each", + "\t use of --next. If --parallel-max is provided several times, the", + "\t last set value is used.", + "\n\t Example:", + "\t curl --parallel-max 100 -Z https://example.com ftp://example.com/", + "\n\t Added in 7.66.0. See also --parallel.", + "\n --pass ", + "\t (SSH TLS) Passphrase for the private key. If --pass is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --pass secret --key file https://example.com", + "\n\t See also --key and --user.", + "\n --path-as-is", + "\t Do not handle sequences of /../ or /./ in the given URL path.", + "\t Normally curl squashes or merges them according to standards but", + "\t with this option set you tell it not to do that. Providing", + "\t --path-as-is multiple times has no extra effect. Disable it again", + "\t with --no-path-as-is.", + "\n\t Example:", + "\t curl --path-as-is https://example.com/../../etc/passwd", + "\n\t See also --request-target.", + "\n --pinnedpubkey ", + "\t (TLS) Use the specified public key file (or hashes) to verify the", + "\t peer. This can be a path to a file which contains a single public", + "\t key in PEM or DER format, or any number of base64 encoded sha256", + "\t hashes preceded by 'sha256//' and separated by ';'.", + "\n\t When negotiating a TLS or SSL connection, the server sends a", + "\t certificate indicating its identity. A public key is extracted", + "\t from this certificate and if it does not exactly match the public", + "\t key provided to this option, curl aborts the connection before", + "\t sending or receiving any data.", + "\n\t This option is independent of option --insecure. If you use both", + "\t options together then the peer is still verified by public key.", + "\n\t PEM/DER support:", + "\n\t OpenSSL and GnuTLS, wolfSSL, mbedTLS , Secure Transport macOS", + "\t 10.7+/iOS 10+, Schannel", + "\n\t sha256 support:", + "\n\t OpenSSL, GnuTLS and wolfSSL, mbedTLS, Secure Transport macOS", + "\t 10.7+/iOS 10+, Schannel", + "\n\t Other SSL backends not supported. If --pinnedpubkey is provided", + "\t several times, the last set value is used.", + "\n\t Examples:", + "\t curl --pinnedpubkey keyfile https://example.com", + "\t curl --pinnedpubkey 'sha256//ce118b51897f4452dc' https://example.com", + "\n\t See also --hostpubsha256.", + "\n --post301", + "\t (HTTP) Respect RFC 7231/6.4.2 and do not convert POST requests", + "\t into GET requests when following a 301 redirect. The non-RFC", + "\t behavior is ubiquitous in web browsers, so curl does the", + "\t conversion by default to maintain consistency. However, a server", + "\t may require a POST to remain a POST after such a redirection. This", + "\t option is meaningful only when using --location. Providing", + "\t --post301 multiple times has no extra effect. Disable it again", + "\t with --no-post301.", + "\n\t Example:", + "\t curl --post301 --location -d \"data\" https://example.com", + "\n\t See also --post302, --post303 and --location.", + "\n --post302", + "\t (HTTP) Respect RFC 7231/6.4.3 and do not convert POST requests", + "\t into GET requests when following a 302 redirect. The non-RFC", + "\t behavior is ubiquitous in web browsers, so curl does the", + "\t conversion by default to maintain consistency. However, a server", + "\t may require a POST to remain a POST after such a redirection. This", + "\t option is meaningful only when using --location. Providing", + "\t --post302 multiple times has no extra effect. Disable it again", + "\t with --no-post302.", + "\n\t Example:", + "\t curl --post302 --location -d \"data\" https://example.com", + "\n\t See also --post301, --post303 and --location.", + "\n --post303", + "\t (HTTP) Violate RFC 7231/6.4.4 and do not convert POST requests", + "\t into GET requests when following 303 redirect. A server may", + "\t require a POST to remain a POST after a 303 redirection. This", + "\t option is meaningful only when using --location. Providing", + "\t --post303 multiple times has no extra effect. Disable it again", + "\t with --no-post303.", + "\n\t Example:", + "\t curl --post303 --location -d \"data\" https://example.com", + "\n\t See also --post302, --post301 and --location.", + "\n --preproxy [protocol://]host[:port]", + "\t Use the specified SOCKS proxy before connecting to an HTTP or", + "\t HTTPS --proxy. In such a case curl first connects to the SOCKS", + "\t proxy and then connects (through SOCKS) to the HTTP or HTTPS", + "\t proxy. Hence pre proxy.", + "\n\t The pre proxy string should be specified with a protocol:// prefix", + "\t to specify alternative proxy protocols. Use socks4://, socks4a://,", + "\t socks5:// or socks5h:// to request the specific SOCKS version to", + "\t be used. No protocol specified makes curl default to SOCKS4.", + "\n\t If the port number is not specified in the proxy string, it is", + "\t assumed to be 1080.", + "\n\t User and password that might be provided in the proxy string are", + "\t URL decoded by curl. This allows you to pass in special characters", + "\t such as @ by using %40 or pass in a colon with %3a. If --preproxy", + "\t is provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --preproxy socks5://proxy.example -x http://http.example https://example.com", + "\n\t See also --proxy and --socks5.", + "\n -#, --progress-bar", + "\t Make curl display transfer progress as a simple progress bar", + "\t instead of the standard, more informational, meter.", + "\n\t This progress bar draws a single line of '#' characters across the", + "\t screen and shows a percentage if the transfer size is known. For", + "\t transfers without a known size, there is a space ship (-=o=-) that", + "\t moves back and forth but only while data is being transferred,", + "\t with a set of flying hash sign symbols on top.", + "\n\t This option is global and does not need to be specified for each", + "\t use of --next. Providing --progress-bar multiple times has no", + "\t extra effect. Disable it again with --no-progress-bar.", + "\n\t Example:", + "\t curl -# -O https://example.com", + "\n\t See also --styled-output.", + "\n --proto ", + "\t Limit what protocols to allow for transfers. Protocols are", + "\t evaluated left to right, are comma separated, and are each a", + "\t protocol name or 'all', optionally prefixed by zero or more", + "\t modifiers. Available modifiers are:", + "\n\t +", + "\n\t\tPermit this protocol in addition to protocols already", + "\t\tpermitted (this is the default if no modifier is used).", + "\n\t -", + "\n\t\tDeny this protocol, removing it from the list of protocols", + "\t\talready permitted.", + "\n\t =", + "\n\t\tPermit only this protocol (ignoring the list already", + "\t\tpermitted), though subject to later modification by subsequent", + "\t\tentries in the comma separated list. For example: --proto", + "\t\t-ftps uses the default protocols, but disables ftps", + "\n\t\t--proto -all,https,+http only enables http and https", + "\n\t\t--proto =http,https also only enables http and https", + "\n\t\tUnknown and disabled protocols produce a warning. This allows", + "\t\tscripts to safely rely on being able to disable potentially", + "\t\tdangerous protocols, without relying upon support for that", + "\t\tprotocol being built into curl to avoid an error.", + "\n\t\tThis option can be used multiple times, in which case the", + "\t\teffect is the same as concatenating the protocols into one", + "\t\tinstance of the option.", + "\n\t If --proto is provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --proto =http,https,sftp https://example.com", + "\n\t See also --proto-redir and --proto-default.", + "\n --proto-default ", + "\t Use protocol for any provided URL missing a scheme.", + "\n\t An unknown or unsupported protocol causes error", + "\t CURLE_UNSUPPORTED_PROTOCOL.", + "\n\t This option does not change the default proxy protocol (http).", + "\n\t Without this option set, curl guesses protocol based on the", + "\t hostname, see --url for details. If --proto-default is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --proto-default https ftp.example.com", + "\n\t See also --proto and --proto-redir.", + "\n --proto-redir ", + "\t Limit what protocols to allow on redirects. Protocols denied by", + "\t --proto are not overridden by this option. See --proto for how", + "\t protocols are represented.", + "\n\t Example, allow only HTTP and HTTPS on redirect:", + "\n\t\tcurl --proto-redir -all,http,https http://example.com", + "\n\t By default curl only allows HTTP, HTTPS, FTP and FTPS on redirects", + "\t (added in 7.65.2). Specifying all or +all enables all protocols on", + "\t redirects, which is not good for security. If --proto-redir is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --proto-redir =http,https https://example.com", + "\n\t See also --proto.", + "\n -x, --proxy [protocol://]host[:port]", + "\t Use the specified proxy.", + "\n\t The proxy string can be specified with a protocol:// prefix. No", + "\t protocol specified or http:// it is treated as an HTTP proxy. Use", + "\t socks4://, socks4a://, socks5:// or socks5h:// to request a", + "\t specific SOCKS version to be used.", + "\n\t Unix domain sockets are supported for socks proxy. Set localhost", + "\t for the host part. e.g. socks5h://localhost/path/to/socket.sock", + "\n\t HTTPS proxy support works set with the https:// protocol prefix", + "\t for OpenSSL and GnuTLS. It also works for BearSSL, mbedTLS,", + "\t rustls, Schannel, Secure Transport and wolfSSL (added in 7.87.0).", + "\n\t Unrecognized and unsupported proxy protocols cause an error.", + "\t Ancient curl versions ignored unknown schemes and used http://", + "\t instead.", + "\n\t If the port number is not specified in the proxy string, it is", + "\t assumed to be 1080.", + "\n\t This option overrides existing environment variables that set the", + "\t proxy to use. If there is an environment variable setting a proxy,", + "\t you can set proxy to \"\" to override it.", + "\n\t All operations that are performed over an HTTP proxy are", + "\t transparently converted to HTTP. It means that certain protocol", + "\t specific operations might not be available. This is not the case", + "\t if you can tunnel through the proxy, as one with the --proxytunnel", + "\t option.", + "\n\t User and password that might be provided in the proxy string are", + "\t URL decoded by curl. This allows you to pass in special characters", + "\t such as @ by using %40 or pass in a colon with %3a.", + "\n\t The proxy host can be specified the same way as the proxy", + "\t environment variables, including the protocol prefix (http://) and", + "\t the embedded user + password.", + "\n\t When a proxy is used, the active FTP mode as set with --ftp-port,", + "\t cannot be used. If --proxy is provided several times, the last set", + "\t value is used.", + "\n\t Example:", + "\t curl --proxy http://proxy.example https://example.com", + "\n\t See also --socks5 and --proxy-basic.", + "\n --proxy-anyauth", + "\t Automatically pick a suitable authentication method when", + "\t communicating with the given HTTP proxy. This might cause an extra", + "\t request/response round-trip. Providing --proxy-anyauth multiple", + "\t times has no extra effect.", + "\n\t Example:", + "\t curl --proxy-anyauth --proxy-user user:passwd -x proxy https://example.com", + "\n\t See also --proxy, --proxy-basic and --proxy-digest.", + "\n --proxy-basic", + "\t Use HTTP Basic authentication when communicating with the given", + "\t proxy. Use --basic for enabling HTTP Basic with a remote host.", + "\t Basic is the default authentication method curl uses with proxies.", + "\t Providing --proxy-basic multiple times has no extra effect.", + "\n\t Example:", + "\t curl --proxy-basic --proxy-user user:passwd -x proxy https://example.com", + "\n\t See also --proxy, --proxy-anyauth and --proxy-digest.", + "\n --proxy-ca-native", + "\t (TLS) Use the CA store from the native operating system to verify", + "\t the HTTPS proxy. By default, curl uses a CA store provided in a", + "\t single file or directory, but when using this option it interfaces", + "\t the operating system's own vault.", + "\n\t This option works for curl on Windows when built to use OpenSSL,", + "\t wolfSSL (added in 8.3.0) or GnuTLS (added in 8.5.0). When curl on", + "\t Windows is built to use Schannel, this feature is implied and curl", + "\t then only uses the native CA store. Providing --proxy-ca-native", + "\t multiple times has no extra effect. Disable it again with", + "\t --no-proxy-ca-native.", + "\n\t Example:", + "\t curl --proxy-ca-native https://example.com", + "\n\t Added in 8.2.0. See also --cacert, --capath and --insecure.", + "\n --proxy-cacert ", + "\t Use the specified certificate file to verify the HTTPS proxy. The", + "\t file may contain multiple CA certificates. The certificate(s) must", + "\t be in PEM format.", + "\n\t This allows you to use a different trust for the proxy compared to", + "\t the remote server connected to via the proxy.", + "\n\t Equivalent to --cacert but used in HTTPS proxy context. If", + "\t --proxy-cacert is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --proxy-cacert CA-file.txt -x https://proxy https://example.com", + "\n\t See also --proxy-capath, --cacert, --capath and --proxy.", + "\n --proxy-capath ", + "\t Same as --capath but used in HTTPS proxy context.", + "\n\t Use the specified certificate directory to verify the proxy.", + "\t Multiple paths can be provided by separated with colon (\":\") (e.g.", + "\t \"path1:path2:path3\"). The certificates must be in PEM format, and", + "\t if curl is built against OpenSSL, the directory must have been", + "\t processed using the c_rehash utility supplied with OpenSSL. Using", + "\t --proxy-capath can allow OpenSSL-powered curl to make", + "\t SSL-connections much more efficiently than using --proxy-cacert if", + "\t the --proxy-cacert file contains many CA certificates.", + "\n\t If this option is set, the default capath value is ignored. If", + "\t --proxy-capath is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --proxy-capath /local/directory -x https://proxy https://example.com", + "\n\t See also --proxy-cacert, --proxy and --capath.", + "\n --proxy-cert ", + "\t Use the specified client certificate file when communicating with", + "\t an HTTPS proxy. The certificate must be in PKCS#12 format if using", + "\t Secure Transport, or PEM format if using any other engine. If the", + "\t optional password is not specified, it is queried for on the", + "\t terminal. Use --proxy-key to provide the private key.", + "\n\t This option is the equivalent to --cert but used in HTTPS proxy", + "\t context. If --proxy-cert is provided several times, the last set", + "\t value is used.", + "\n\t Example:", + "\t curl --proxy-cert file -x https://proxy https://example.com", + "\n\t See also --proxy, --proxy-key and --proxy-cert-type.", + "\n --proxy-cert-type ", + "\t Set type of the provided client certificate when using HTTPS", + "\t proxy. PEM, DER, ENG and P12 are recognized types.", + "\n\t The default type depends on the TLS backend and is usually PEM,", + "\t however for Secure Transport and Schannel it is P12. If", + "\t --proxy-cert is a pkcs11: URI then ENG is the default type.", + "\n\t Equivalent to --cert-type but used in HTTPS proxy context. If", + "\t --proxy-cert-type is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --proxy-cert-type PEM --proxy-cert file -x https://proxy https://example.com", + "\n\t See also --proxy-cert and --proxy-key.", + "\n --proxy-ciphers ", + "\t Same as --ciphers but used in HTTPS proxy context.", + "\n\t Specifies which ciphers to use in the connection to the HTTPS", + "\t proxy. The list of ciphers must specify valid ciphers. Read up on", + "\t SSL cipher list details on this URL:", + "\n\t https://curl.se/docs/ssl-ciphers.html If --proxy-ciphers is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --proxy-ciphers ECDHE-ECDSA-AES256-CCM8 -x https://proxy https://example.com", + "\n\t See also --ciphers, --curves and --proxy.", + "\n --proxy-crlfile ", + "\t Provide filename for a PEM formatted file with a Certificate", + "\t Revocation List that specifies peer certificates that are", + "\t considered revoked when communicating with an HTTPS proxy.", + "\n\t Equivalent to --crlfile but only used in HTTPS proxy context. If", + "\t --proxy-crlfile is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --proxy-crlfile rejects.txt -x https://proxy https://example.com", + "\n\t See also --crlfile and --proxy.", + "\n --proxy-digest", + "\t Use HTTP Digest authentication when communicating with the given", + "\t proxy. Use --digest for enabling HTTP Digest with a remote host.", + "\t Providing --proxy-digest multiple times has no extra effect.", + "\n\t Example:", + "\t curl --proxy-digest --proxy-user user:passwd -x proxy https://example.com", + "\n\t See also --proxy, --proxy-anyauth and --proxy-basic.", + "\n --proxy-header
", + "\t (HTTP) Extra header to include in the request when sending HTTP to", + "\t a proxy. You may specify any number of extra headers. This is the", + "\t equivalent option to --header but is for proxy communication only", + "\t like in CONNECT requests when you want a separate header sent to", + "\t the proxy to what is sent to the actual remote host.", + "\n\t curl makes sure that each header you add/replace is sent with the", + "\t proper end-of-line marker, you should thus not add that as a part", + "\t of the header content: do not add newlines or carriage returns,", + "\t they only mess things up for you.", + "\n\t Headers specified with this option are not included in requests", + "\t that curl knows are not be sent to a proxy.", + "\n\t This option can take an argument in @filename style, which then", + "\t adds a header for each line in the input file. Using @- makes curl", + "\t read the headers from stdin.", + "\n\t This option can be used multiple times to add/replace/remove", + "\t multiple headers. --proxy-header can be used several times in a", + "\t command line", + "\n\t Examples:", + "\t curl --proxy-header \"X-First-Name: Joe\" -x http://proxy https://example.com", + "\t curl --proxy-header \"User-Agent: surprise\" -x http://proxy https://example.com", + "\t curl --proxy-header \"Host:\" -x http://proxy https://example.com", + "\n\t See also --proxy.", + "\n --proxy-http2", + "\t (HTTP) Negotiate HTTP/2 with an HTTPS proxy. The proxy might still", + "\t only offer HTTP/1 and then curl sticks to using that version.", + "\n\t This has no effect for any other kinds of proxies. Providing", + "\t --proxy-http2 multiple times has no extra effect. Disable it again", + "\t with --no-proxy-http2.", + "\n\t Example:", + "\t curl --proxy-http2 -x proxy https://example.com", + "\n\t --proxy-http2 requires that libcurl is built to support HTTP/2.", + "\t Added in 8.1.0. See also --proxy.", + "\n --proxy-insecure", + "\t Same as --insecure but used in HTTPS proxy context.", + "\n\t Every secure connection curl makes is verified to be secure before", + "\t the transfer takes place. This option makes curl skip the", + "\t verification step with a proxy and proceed without checking.", + "\n\t When this option is not used for a proxy using HTTPS, curl", + "\t verifies the proxy's TLS certificate before it continues: that the", + "\t certificate contains the right name which matches the hostname and", + "\t that the certificate has been signed by a CA certificate present", + "\t in the cert store. See this online resource for further details:", + "\t https://curl.se/docs/sslcerts.html", + "\n\t WARNING: using this option makes the transfer to the proxy", + "\t insecure. Providing --proxy-insecure multiple times has no extra", + "\t effect. Disable it again with --no-proxy-insecure.", + "\n\t Example:", + "\t curl --proxy-insecure -x https://proxy https://example.com", + "\n\t See also --proxy and --insecure.", + "\n --proxy-key ", + "\t Specify the filename for your private key when using client", + "\t certificates with your HTTPS proxy. This option is the equivalent", + "\t to --key but used in HTTPS proxy context. If --proxy-key is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --proxy-key here -x https://proxy https://example.com", + "\n\t See also --proxy-key-type and --proxy.", + "\n --proxy-key-type ", + "\t Specify the private key file type your --proxy-key provided", + "\t private key uses. DER, PEM, and ENG are supported. If not", + "\t specified, PEM is assumed.", + "\n\t Equivalent to --key-type but used in HTTPS proxy context. If", + "\t --proxy-key-type is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --proxy-key-type DER --proxy-key here -x https://proxy https://example.com", + "\n\t See also --proxy-key and --proxy.", + "\n --proxy-negotiate", + "\t Use HTTP Negotiate (SPNEGO) authentication when communicating with", + "\t the given proxy. Use --negotiate for enabling HTTP Negotiate", + "\t (SPNEGO) with a remote host. Providing --proxy-negotiate multiple", + "\t times has no extra effect.", + "\n\t Example:", + "\t curl --proxy-negotiate --proxy-user user:passwd -x proxy https://example.com", + "\n\t See also --proxy-anyauth, --proxy-basic and --proxy-service-name.", + "\n --proxy-ntlm", + "\t Use HTTP NTLM authentication when communicating with the given", + "\t proxy. Use --ntlm for enabling NTLM with a remote host. Providing", + "\t --proxy-ntlm multiple times has no extra effect.", + "\n\t Example:", + "\t curl --proxy-ntlm --proxy-user user:passwd -x http://proxy https://example.com", + "\n\t See also --proxy-negotiate, --proxy-anyauth and --proxy-user.", + "\n --proxy-pass ", + "\t Passphrase for the private key for HTTPS proxy client certificate.", + "\n\t Equivalent to --pass but used in HTTPS proxy context. If", + "\t --proxy-pass is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --proxy-pass secret --proxy-key here -x https://proxy https://example.com", + "\n\t See also --proxy and --proxy-key.", + "\n --proxy-pinnedpubkey ", + "\t (TLS) Use the specified public key file (or hashes) to verify the", + "\t proxy. This can be a path to a file which contains a single public", + "\t key in PEM or DER format, or any number of base64 encoded sha256", + "\t hashes preceded by 'sha256//' and separated by ';'.", + "\n\t When negotiating a TLS or SSL connection, the server sends a", + "\t certificate indicating its identity. A public key is extracted", + "\t from this certificate and if it does not exactly match the public", + "\t key provided to this option, curl aborts the connection before", + "\t sending or receiving any data. If --proxy-pinnedpubkey is provided", + "\t several times, the last set value is used.", + "\n\t Examples:", + "\t curl --proxy-pinnedpubkey keyfile https://example.com", + "\t curl --proxy-pinnedpubkey 'sha256//ce118b51897f4452dc' https://example.com", + "\n\t See also --pinnedpubkey and --proxy.", + "\n --proxy-service-name ", + "\t Set the service name for SPNEGO when doing proxy authentication.", + "\t If --proxy-service-name is provided several times, the last set", + "\t value is used.", + "\n\t Example:", + "\t curl --proxy-service-name \"shrubbery\" -x proxy https://example.com", + "\n\t See also --service-name, --proxy and --proxy-negotiate.", + "\n --proxy-ssl-allow-beast", + "\t Do not work around a security flaw in the TLS1.0 protocol known as", + "\t BEAST when communicating to an HTTPS proxy. If this option is not", + "\t used, the TLS layer may use workarounds known to cause", + "\t interoperability problems with some older server implementations.", + "\n\t This option only changes how curl does TLS 1.0 with an HTTPS proxy", + "\t and has no effect on later TLS versions.", + "\n\t WARNING: this option loosens the TLS security, and by using this", + "\t flag you ask for exactly that.", + "\n\t Equivalent to --ssl-allow-beast but used in HTTPS proxy context.", + "\t Providing --proxy-ssl-allow-beast multiple times has no extra", + "\t effect. Disable it again with --no-proxy-ssl-allow-beast.", + "\n\t Example:", + "\t curl --proxy-ssl-allow-beast -x https://proxy https://example.com", + "\n\t See also --ssl-allow-beast and --proxy.", + "\n --proxy-ssl-auto-client-cert", + "\t Same as --ssl-auto-client-cert but used in HTTPS proxy context.", + "\n\t This is only supported by Schannel. Providing", + "\t --proxy-ssl-auto-client-cert multiple times has no extra effect.", + "\t Disable it again with --no-proxy-ssl-auto-client-cert.", + "\n\t Example:", + "\t curl --proxy-ssl-auto-client-cert -x https://proxy https://example.com", + "\n\t Added in 7.77.0. See also --ssl-auto-client-cert and --proxy.", + "\n --proxy-tls13-ciphers ", + "\t (TLS) Specify which cipher suites to use in the connection to your", + "\t HTTPS proxy when it negotiates TLS 1.3. The list of ciphers suites", + "\t must specify valid ciphers. Read up on TLS 1.3 cipher suite", + "\t details on this URL:", + "\n\t https://curl.se/docs/ssl-ciphers.html", + "\n\t This option is currently used only when curl is built to use", + "\t OpenSSL 1.1.1 or later. If you are using a different SSL backend", + "\t you can try setting TLS 1.3 cipher suites by using the", + "\t --proxy-ciphers option. If --proxy-tls13-ciphers is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --proxy-tls13-ciphers TLS_AES_128_GCM_SHA256 -x proxy https://example.com", + "\n\t Added in 7.61.0. See also --tls13-ciphers, --curves and", + "\t --proxy-ciphers.", + "\n --proxy-tlsauthtype ", + "\t Set TLS authentication type with HTTPS proxy. The only supported", + "\t option is \"SRP\", for TLS-SRP (RFC 5054). This option works only if", + "\t the underlying libcurl is built with TLS-SRP support.", + "\n\t Equivalent to --tlsauthtype but used in HTTPS proxy context. If", + "\t --proxy-tlsauthtype is provided several times, the last set value", + "\t is used.", + "\n\t Example:", + "\t curl --proxy-tlsauthtype SRP -x https://proxy https://example.com", + "\n\t See also --proxy, --proxy-tlsuser and --proxy-tlspassword.", + "\n --proxy-tlspassword ", + "\t Set password to use with the TLS authentication method specified", + "\t with --proxy-tlsauthtype when using HTTPS proxy. Requires that", + "\t --proxy-tlsuser is set.", + "\n\t This option does not work with TLS 1.3.", + "\n\t Equivalent to --tlspassword but used in HTTPS proxy context. If", + "\t --proxy-tlspassword is provided several times, the last set value", + "\t is used.", + "\n\t Example:", + "\t curl --proxy-tlspassword passwd -x https://proxy https://example.com", + "\n\t See also --proxy and --proxy-tlsuser.", + "\n --proxy-tlsuser ", + "\t Set username for use for HTTPS proxy with the TLS authentication", + "\t method specified with --proxy-tlsauthtype. Requires that", + "\t --proxy-tlspassword also is set.", + "\n\t This option does not work with TLS 1.3. If --proxy-tlsuser is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --proxy-tlsuser smith -x https://proxy https://example.com", + "\n\t See also --proxy and --proxy-tlspassword.", + "\n --proxy-tlsv1", + "\t Use at least TLS version 1.x when negotiating with an HTTPS proxy.", + "\t That means TLS version 1.0 or higher", + "\n\t Equivalent to --tlsv1 but for an HTTPS proxy context. Providing", + "\t --proxy-tlsv1 multiple times has no extra effect.", + "\n\t Example:", + "\t curl --proxy-tlsv1 -x https://proxy https://example.com", + "\n\t See also --proxy.", + "\n -U, --proxy-user ", + "\t Specify the username and password to use for proxy authentication.", + "\n\t If you use a Windows SSPI-enabled curl binary and do either", + "\t Negotiate or NTLM authentication then you can tell curl to select", + "\t the username and password from your environment by specifying a", + "\t single colon with this option: \"-U :\".", + "\n\t On systems where it works, curl hides the given option argument", + "\t from process listings. This is not enough to protect credentials", + "\t from possibly getting seen by other users on the same system as", + "\t they still are visible for a moment before cleared. Such sensitive", + "\t data should be retrieved from a file instead or similar and never", + "\t used in clear text in a command line. If --proxy-user is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --proxy-user smith:secret -x proxy https://example.com", + "\n\t See also --proxy-pass.", + "\n --proxy1.0 ", + "\t Use the specified HTTP 1.0 proxy. If the port number is not", + "\t specified, it is assumed at port 1080.", + "\n\t The only difference between this and the HTTP proxy option", + "\t --proxy, is that attempts to use CONNECT through the proxy", + "\t specifies an HTTP 1.0 protocol instead of the default HTTP 1.1.", + "\t Providing --proxy1.0 multiple times has no extra effect.", + "\n\t Example:", + "\t curl --proxy1.0 http://proxy https://example.com", + "\n\t See also --proxy, --socks5 and --preproxy.", + "\n -p, --proxytunnel", + "\t When an HTTP proxy is used --proxy, this option makes curl tunnel", + "\t the traffic through the proxy. The tunnel approach is made with", + "\t the HTTP proxy CONNECT request and requires that the proxy allows", + "\t direct connect to the remote port number curl wants to tunnel", + "\t through to.", + "\n\t To suppress proxy CONNECT response headers when curl is set to", + "\t output headers use --suppress-connect-headers. Providing", + "\t --proxytunnel multiple times has no extra effect. Disable it again", + "\t with --no-proxytunnel.", + "\n\t Example:", + "\t curl --proxytunnel -x http://proxy https://example.com", + "\n\t See also --proxy.", + "\n --pubkey ", + "\t (SFTP SCP) Public key filename. Allows you to provide your public", + "\t key in this separate file.", + "\n\t curl attempts to automatically extract the public key from the", + "\t private key file, so passing this option is generally not", + "\t required. Note that this public key extraction requires libcurl to", + "\t be linked against a copy of libssh2 1.2.8 or higher that is itself", + "\t linked against OpenSSL. If --pubkey is provided several times, the", + "\t last set value is used.", + "\n\t Example:", + "\t curl --pubkey file.pub sftp://example.com/", + "\n\t See also --pass.", + "\n -Q, --quote ", + "\t (FTP SFTP) Send an arbitrary command to the remote FTP or SFTP", + "\t server. Quote commands are sent BEFORE the transfer takes place", + "\t (just after the initial PWD command in an FTP transfer, to be", + "\t exact). To make commands take place after a successful transfer,", + "\t prefix them with a dash '-'.", + "\n\t (FTP only) To make commands be sent after curl has changed the", + "\t working directory, just before the file transfer command(s),", + "\t prefix the command with a '+'. This is not performed when a", + "\t directory listing is performed.", + "\n\t You may specify any number of commands.", + "\n\t By default curl stops at first failure. To make curl continue even", + "\t if the command fails, prefix the command with an asterisk (*).", + "\t Otherwise, if the server returns failure for one of the commands,", + "\t the entire operation is aborted.", + "\n\t You must send syntactically correct FTP commands as RFC 959", + "\t defines to FTP servers, or one of the commands listed below to", + "\t SFTP servers.", + "\n\t SFTP is a binary protocol. Unlike for FTP, curl interprets SFTP", + "\t quote commands itself before sending them to the server. Filenames", + "\t may be quoted shell-style to embed spaces or special characters.", + "\t Following is the list of all supported SFTP quote commands:", + "\n\t atime date file", + "\n\t\tThe atime command sets the last access time of the file named", + "\t\tby the file operand. The date expression can be all sorts of", + "\t\tdate strings, see the curl_getdate(3) man page for date", + "\t\texpression details. (Added in 7.73.0)", + "\n\t chgrp group file", + "\n\t\tThe chgrp command sets the group ID of the file named by the", + "\t\tfile operand to the group ID specified by the group operand.", + "\t\tThe group operand is a decimal integer group ID.", + "\n\t chmod mode file", + "\n\t\tThe chmod command modifies the file mode bits of the specified", + "\t\tfile. The mode operand is an octal integer mode number.", + "\n\t chown user file", + "\n\t\tThe chown command sets the owner of the file named by the file", + "\t\toperand to the user ID specified by the user operand. The user", + "\t\toperand is a decimal integer user ID.", + "\n\t ln source_file target_file", + "\n\t\tThe ln and symlink commands create a symbolic link at the", + "\t\ttarget_file location pointing to the source_file location.", + "\n\t mkdir directory_name", + "\n\t\tThe mkdir command creates the directory named by the", + "\t\tdirectory_name operand.", + "\n\t mtime date file", + "\n\t\tThe mtime command sets the last modification time of the file", + "\t\tnamed by the file operand. The date expression can be all", + "\t\tsorts of date strings, see the curl_getdate(3) man page for", + "\t\tdate expression details. (Added in 7.73.0)", + "\n\t pwd", + "\n\t\tThe pwd command returns the absolute path name of the current", + "\t\tworking directory.", + "\n\t rename source target", + "\n\t\tThe rename command renames the file or directory named by the", + "\t\tsource operand to the destination path named by the target", + "\t\toperand.", + "\n\t rm file", + "\n\t\tThe rm command removes the file specified by the file operand.", + "\n\t rmdir directory", + "\n\t\tThe rmdir command removes the directory entry specified by the", + "\t\tdirectory operand, provided it is empty.", + "\n\t symlink source_file target_file", + "\n\t\tSee ln.", + "\n\t --quote can be used several times in a command line", + "\n\t Example:", + "\t curl --quote \"DELE file\" ftp://example.com/foo", + "\n\t See also --request.", + "\n --random-file ", + "\t Deprecated option. This option is ignored (added in 7.84.0). Prior", + "\t to that it only had an effect on curl if built to use old versions", + "\t of OpenSSL.", + "\n\t Specify the path name to file containing random data. The data may", + "\t be used to seed the random engine for SSL connections. If", + "\t --random-file is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --random-file rubbish https://example.com", + "\n\t See also --egd-file.", + "\n -r, --range ", + "\t (HTTP FTP SFTP FILE) Retrieve a byte range (i.e. a partial", + "\t document) from an HTTP/1.1, FTP or SFTP server or a local FILE.", + "\t Ranges can be specified in a number of ways.", + "\n\t 0-499", + "\n\t\tspecifies the first 500 bytes", + "\n\t 500-999", + "\n\t\tspecifies the second 500 bytes", + "\n\t -500", + "\n\t\tspecifies the last 500 bytes", + "\n\t 9500-", + "\n\t\tspecifies the bytes from offset 9500 and forward", + "\n\t 0-0,-1", + "\n\t\tspecifies the first and last byte only(*)(HTTP)", + "\n\t 100-199,500-599", + "\n\t\tspecifies two separate 100-byte ranges(*) (HTTP) (*) = NOTE", + "\t\tthat these make the server reply with a multipart response,", + "\t\twhich is returned as-is by curl! Parsing or otherwise", + "\t\ttransforming this response is the responsibility of the", + "\t\tcaller.", + "\n\t\tOnly digit characters (0-9) are valid in the 'start' and", + "\t\t'stop' fields of the 'start-stop' range syntax. If a non-digit", + "\t\tcharacter is given in the range, the server's response is", + "\t\tunspecified, depending on the server's configuration.", + "\n\t\tMany HTTP/1.1 servers do not have this feature enabled, so", + "\t\tthat when you attempt to get a range, curl instead gets the", + "\t\twhole document.", + "\n\t\tFTP and SFTP range downloads only support the simple", + "\t\t'start-stop' syntax (optionally with one of the numbers", + "\t\tomitted). FTP use depends on the extended FTP command SIZE.", + "\n\t If --range is provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --range 22-44 https://example.com", + "\n\t See also --continue-at and --append.", + "\n --rate ", + "\t Specify the maximum transfer frequency you allow curl to use - in", + "\t number of transfer starts per time unit (sometimes called request", + "\t rate). Without this option, curl starts the next transfer as fast", + "\t as possible.", + "\n\t If given several URLs and a transfer completes faster than the", + "\t allowed rate, curl waits until the next transfer is started to", + "\t maintain the requested rate. This option has no effect when", + "\t --parallel is used.", + "\n\t The request rate is provided as \"N/U\" where N is an integer number", + "\t and U is a time unit. Supported units are 's' (second), 'm'", + "\t (minute), 'h' (hour) and 'd' /(day, as in a 24 hour unit). The", + "\t default time unit, if no \"/U\" is provided, is number of transfers", + "\t per hour.", + "\n\t If curl is told to allow 10 requests per minute, it does not start", + "\t the next request until 6 seconds have elapsed since the previous", + "\t transfer was started.", + "\n\t This function uses millisecond resolution. If the allowed", + "\t frequency is set more than 1000 per second, it instead runs", + "\t unrestricted.", + "\n\t When retrying transfers, enabled with --retry, the separate retry", + "\t delay logic is used and not this setting.", + "\n\t This option is global and does not need to be specified for each", + "\t use of --next. If --rate is provided several times, the last set", + "\t value is used.", + "\n\t Examples:", + "\t curl --rate 2/s https://example.com ...", + "\t curl --rate 3/h https://example.com ...", + "\t curl --rate 14/m https://example.com ...", + "\n\t Added in 7.84.0. See also --limit-rate and --retry-delay.", + "\n --raw", + "\t (HTTP) When used, it disables all internal HTTP decoding of", + "\t content or transfer encodings and instead makes them passed on", + "\t unaltered, raw. Providing --raw multiple times has no extra", + "\t effect. Disable it again with --no-raw.", + "\n\t Example:", + "\t curl --raw https://example.com", + "\n\t See also --tr-encoding.", + "\n -e, --referer ", + "\t (HTTP) Set the referrer URL in the HTTP request. This can also be", + "\t set with the --header flag of course. When used with --location", + "\t you can append \";auto\"\" to the --referer URL to make curl", + "\t automatically set the previous URL when it follows a Location:", + "\t header. The \";auto\" string can be used alone, even if you do not", + "\t set an initial --referer. If --referer is provided several times,", + "\t the last set value is used.", + "\n\t Examples:", + "\t curl --referer \"https://fake.example\" https://example.com", + "\t curl --referer \"https://fake.example;auto\" -L https://example.com", + "\t curl --referer \";auto\" -L https://example.com", + "\n\t See also --user-agent and --header.", + "\n -J, --remote-header-name", + "\t (HTTP) Tell the --remote-name option to use the server-specified", + "\t Content-Disposition filename instead of extracting a filename from", + "\t the URL. If the server-provided filename contains a path, that is", + "\t stripped off before the filename is used.", + "\n\t The file is saved in the current directory, or in the directory", + "\t specified with --output-dir.", + "\n\t If the server specifies a filename and a file with that name", + "\t already exists in the destination directory, it is not overwritten", + "\t and an error occurs - unless you allow it by using the --clobber", + "\t option. If the server does not specify a filename then this option", + "\t has no effect.", + "\n\t There is no attempt to decode %-sequences (yet) in the provided", + "\t filename, so this option may provide you with rather unexpected", + "\t filenames.", + "\n\t This feature uses the name from the \"filename\" field, it does not", + "\t yet support the \"filename*\" field (filenames with explicit", + "\t character sets).", + "\n\t WARNING: Exercise judicious use of this option, especially on", + "\t Windows. A rogue server could send you the name of a DLL or other", + "\t file that could be loaded automatically by Windows or some third", + "\t party software. Providing --remote-header-name multiple times has", + "\t no extra effect. Disable it again with --no-remote-header-name.", + "\n\t Example:", + "\t curl -OJ https://example.com/file", + "\n\t See also --remote-name.", + "\n -O, --remote-name", + "\t Write output to a local file named like the remote file we get.", + "\t (Only the file part of the remote file is used, the path is cut", + "\t off.)", + "\n\t The file is saved in the current working directory. If you want", + "\t the file saved in a different directory, make sure you change the", + "\t current working directory before invoking curl with this option or", + "\t use --output-dir.", + "\n\t The remote filename to use for saving is extracted from the given", + "\t URL, nothing else, and if it already exists it is overwritten. If", + "\t you want the server to be able to choose the filename refer to", + "\t --remote-header-name which can be used in addition to this option.", + "\t If the server chooses a filename and that name already exists it", + "\t is not overwritten.", + "\n\t There is no URL decoding done on the filename. If it has %20 or", + "\t other URL encoded parts of the name, they end up as-is as", + "\t filename.", + "\n\t You may use this option as many times as the number of URLs you", + "\t have. --remote-name is associated with a single URL. Use it once", + "\t per URL when you use several URLs in a command line.", + "\n\t Examples:", + "\t curl -O https://example.com/filename", + "\t curl -O https://example.com/filename -O https://example.com/file2", + "\n\t See also --remote-name-all, --output-dir and --remote-header-name.", + "\n --remote-name-all", + "\t Change the default action for all given URLs to be dealt with as", + "\t if --remote-name were used for each one. If you want to disable", + "\t that for a specific URL after --remote-name-all has been used, you", + "\t must use \"-o -\" or --no-remote-name. Providing --remote-name-all", + "\t multiple times has no extra effect. Disable it again with", + "\t --no-remote-name-all.", + "\n\t Example:", + "\t curl --remote-name-all ftp://example.com/file1 ftp://example.com/file2", + "\n\t See also --remote-name.", + "\n -R, --remote-time", + "\t Makes curl attempt to figure out the timestamp of the remote file", + "\t that is getting downloaded, and if that is available make the", + "\t local file get that same timestamp. Providing --remote-time", + "\t multiple times has no extra effect. Disable it again with", + "\t --no-remote-time.", + "\n\t Example:", + "\t curl --remote-time -o foo https://example.com", + "\n\t See also --remote-name and --time-cond.", + "\n --remove-on-error", + "\t Remove output file if an error occurs. If curl returns an error", + "\t when told to save output in a local file. This prevents curl from", + "\t leaving a partial file in the case of an error during transfer.", + "\n\t If the output is not a regular file, this option has no effect.", + "\t Providing --remove-on-error multiple times has no extra effect.", + "\t Disable it again with --no-remove-on-error.", + "\n\t Example:", + "\t curl --remove-on-error -o output https://example.com", + "\n\t Added in 7.83.0. See also --fail.", + "\n -X, --request ", + "\t Change the method to use when starting the transfer.", + "\n\t curl passes on the verbatim string you give it in the request", + "\t without any filter or other safe guards. That includes white space", + "\t and control characters.", + "\n\t HTTP", + "\n\t\tSpecifies a custom request method to use when communicating", + "\t\twith the HTTP server. The specified request method is used", + "\t\tinstead of the method otherwise used (which defaults to GET).", + "\t\tRead the HTTP 1.1 specification for details and explanations.", + "\t\tCommon additional HTTP requests include PUT and DELETE, while", + "\t\trelated technologies like WebDAV offers PROPFIND, COPY, MOVE", + "\t\tand more.", + "\n\t\tNormally you do not need this option. All sorts of GET, HEAD,", + "\t\tPOST and PUT requests are rather invoked by using dedicated", + "\t\tcommand line options.", + "\n\t\tThis option only changes the actual word used in the HTTP", + "\t\trequest, it does not alter the way curl behaves. For example", + "\t\tif you want to make a proper HEAD request, using -X HEAD does", + "\t\tnot suffice. You need to use the --head option.", + "\n\t\tThe method string you set with --request is used for all", + "\t\trequests, which if you for example use --location may cause", + "\t\tunintended side-effects when curl does not change request", + "\t\tmethod according to the HTTP 30x response codes - and similar.", + "\n\t FTP", + "\n\t\tSpecifies a custom FTP command to use instead of LIST when", + "\t\tdoing file lists with FTP.", + "\n\t POP3", + "\n\t\tSpecifies a custom POP3 command to use instead of LIST or", + "\t\tRETR.", + "\n\t IMAP", + "\n\t\tSpecifies a custom IMAP command to use instead of LIST.", + "\n\t SMTP", + "\n\t\tSpecifies a custom SMTP command to use instead of HELP or", + "\t\tVRFY.", + "\n\t If --request is provided several times, the last set value is", + "\t used.", + "\n\t Examples:", + "\t curl -X \"DELETE\" https://example.com", + "\t curl -X NLST ftp://example.com/", + "\n\t See also --request-target.", + "\n --request-target ", + "\t (HTTP) Use an alternative target (path) instead of using the path", + "\t as provided in the URL. Particularly useful when wanting to issue", + "\t HTTP requests without leading slash or other data that does not", + "\t follow the regular URL pattern, like \"OPTIONS *\".", + "\n\t curl passes on the verbatim string you give it its the request", + "\t without any filter or other safe guards. That includes white space", + "\t and control characters. If --request-target is provided several", + "\t times, the last set value is used.", + "\n\t Example:", + "\t curl --request-target \"*\" -X OPTIONS https://example.com", + "\n\t See also --request.", + "\n --resolve <[+]host:port:addr[,addr]...>", + "\t Provide a custom address for a specific host and port pair. Using", + "\t this, you can make the curl requests(s) use a specified address", + "\t and prevent the otherwise normally resolved address to be used.", + "\t Consider it a sort of /etc/hosts alternative provided on the", + "\t command line. The port number should be the number used for the", + "\t specific protocol the host is used for. It means you need several", + "\t entries if you want to provide address for the same host but", + "\t different ports.", + "\n\t By specifying \"*\" as host you can tell curl to resolve any host", + "\t and specific port pair to the specified address. Wildcard is", + "\t resolved last so any --resolve with a specific host and port is", + "\t used first.", + "\n\t The provided address set by this option is used even if --ipv4 or", + "\t --ipv6 is set to make curl use another IP version.", + "\n\t By prefixing the host with a '+' you can make the entry time out", + "\t after curl's default timeout (1 minute). Note that this only makes", + "\t sense for long running parallel transfers with a lot of files. In", + "\t such cases, if this option is used curl tries to resolve the host", + "\t as it normally would once the timeout has expired.", + "\n\t To redirect connects from a specific hostname or any hostname,", + "\t independently of port number, consider the --connect-to option.", + "\n\t Support for resolving with wildcard was added in 7.64.0.", + "\n\t Support for the '+' prefix was added in 7.75.0. --resolve can be", + "\t used several times in a command line", + "\n\t Example:", + "\t curl --resolve example.com:443:127.0.0.1 https://example.com", + "\n\t See also --connect-to and --alt-svc.", + "\n --retry ", + "\t If a transient error is returned when curl tries to perform a", + "\t transfer, it retries this number of times before giving up.", + "\t Setting the number to 0 makes curl do no retries (which is the", + "\t default). Transient error means either: a timeout, an FTP 4xx", + "\t response code or an HTTP 408, 429, 500, 502, 503 or 504 response", + "\t code.", + "\n\t When curl is about to retry a transfer, it first waits one second", + "\t and then for all forthcoming retries it doubles the waiting time", + "\t until it reaches 10 minutes which then remains delay between the", + "\t rest of the retries. By using --retry-delay you disable this", + "\t exponential backoff algorithm. See also --retry-max-time to limit", + "\t the total time allowed for retries.", + "\n\t curl complies with the Retry-After: response header if one was", + "\t present to know when to issue the next retry (added in 7.66.0). If", + "\t --retry is provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --retry 7 https://example.com", + "\n\t See also --retry-max-time.", + "\n --retry-all-errors", + "\t Retry on any error. This option is used together with --retry.", + "\n\t This option is the \"sledgehammer\" of retrying. Do not use this", + "\t option by default (for example in your curlrc), there may be", + "\t unintended consequences such as sending or receiving duplicate", + "\t data. Do not use with redirected input or output. You might be", + "\t better off handling your unique problems in a shell script. Please", + "\t read the example below.", + "\n\t WARNING: For server compatibility curl attempts to retry failed", + "\t flaky transfers as close as possible to how they were started, but", + "\t this is not possible with redirected input or output. For example,", + "\t before retrying it removes output data from a failed partial", + "\t transfer that was written to an output file. However this is not", + "\t true of data redirected to a | pipe or > file, which are not", + "\t reset. We strongly suggest you do not parse or record output via", + "\t redirect in combination with this option, since you may receive", + "\t duplicate data.", + "\n\t By default curl does not return error for transfers with an HTTP", + "\t response code that indicates an HTTP error, if the transfer was", + "\t successful. For example, if a server replies 404 Not Found and the", + "\t reply is fully received then that is not an error. When --retry is", + "\t used then curl retries on some HTTP response codes that indicate", + "\t transient HTTP errors, but that does not include most 4xx response", + "\t codes such as 404. If you want to retry on all response codes that", + "\t indicate HTTP errors (4xx and 5xx) then combine with --fail.", + "\t Providing --retry-all-errors multiple times has no extra effect.", + "\t Disable it again with --no-retry-all-errors.", + "\n\t Example:", + "\t curl --retry 5 --retry-all-errors https://example.com", + "\n\t Added in 7.71.0. See also --retry.", + "\n --retry-connrefused", + "\t In addition to the other conditions, consider ECONNREFUSED as a", + "\t transient error too for --retry. This option is used together with", + "\t --retry. Providing --retry-connrefused multiple times has no extra", + "\t effect. Disable it again with --no-retry-connrefused.", + "\n\t Example:", + "\t curl --retry-connrefused --retry 7 https://example.com", + "\n\t See also --retry and --retry-all-errors.", + "\n --retry-delay ", + "\t Make curl sleep this amount of time before each retry when a", + "\t transfer has failed with a transient error (it changes the default", + "\t backoff time algorithm between retries). This option is only", + "\t interesting if --retry is also used. Setting this delay to zero", + "\t makes curl use the default backoff time. If --retry-delay is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --retry-delay 5 --retry 7 https://example.com", + "\n\t See also --retry.", + "\n --retry-max-time ", + "\t The retry timer is reset before the first transfer attempt.", + "\t Retries are done as usual (see --retry) as long as the timer has", + "\t not reached this given limit. Notice that if the timer has not", + "\t reached the limit, the request is made and while performing, it", + "\t may take longer than this given time period. To limit a single", + "\t request's maximum time, use --max-time. Set this option to zero to", + "\t not timeout retries. If --retry-max-time is provided several", + "\t times, the last set value is used.", + "\n\t Example:", + "\t curl --retry-max-time 30 --retry 10 https://example.com", + "\n\t See also --retry.", + "\n --sasl-authzid ", + "\t Use this authorization identity (authzid), during SASL PLAIN", + "\t authentication, in addition to the authentication identity", + "\t (authcid) as specified by --user.", + "\n\t If the option is not specified, the server derives the authzid", + "\t from the authcid, but if specified, and depending on the server", + "\t implementation, it may be used to access another user's inbox,", + "\t that the user has been granted access to, or a shared mailbox for", + "\t example. If --sasl-authzid is provided several times, the last set", + "\t value is used.", + "\n\t Example:", + "\t curl --sasl-authzid zid imap://example.com/", + "\n\t Added in 7.66.0. See also --login-options.", + "\n --sasl-ir", + "\t Enable initial response in SASL authentication. Providing", + "\t --sasl-ir multiple times has no extra effect. Disable it again", + "\t with --no-sasl-ir.", + "\n\t Example:", + "\t curl --sasl-ir imap://example.com/", + "\n\t See also --sasl-authzid.", + "\n --service-name ", + "\t Set the service name for SPNEGO. If --service-name is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --service-name sockd/server https://example.com", + "\n\t See also --negotiate and --proxy-service-name.", + "\n -S, --show-error", + "\t When used with --silent, it makes curl show an error message if it", + "\t fails.", + "\n\t This option is global and does not need to be specified for each", + "\t use of --next. Providing --show-error multiple times has no extra", + "\t effect. Disable it again with --no-show-error.", + "\n\t Example:", + "\t curl --show-error --silent https://example.com", + "\n\t See also --no-progress-meter.", + "\n -s, --silent", + "\t Silent or quiet mode. Do not show progress meter or error", + "\t messages. Makes Curl mute. It still outputs the data you ask for,", + "\t potentially even to the terminal/stdout unless you redirect it.", + "\n\t Use --show-error in addition to this option to disable progress", + "\t meter but still show error messages. Providing --silent multiple", + "\t times has no extra effect. Disable it again with --no-silent.", + "\n\t Example:", + "\t curl -s https://example.com", + "\n\t See also --verbose, --stderr and --no-progress-meter.", + "\n --socks4 ", + "\t Use the specified SOCKS4 proxy. If the port number is not", + "\t specified, it is assumed at port 1080. Using this socket type make", + "\t curl resolve the hostname and passing the address on to the proxy.", + "\n\t To specify proxy on a unix domain socket, use localhost for host,", + "\t e.g. \"socks4://localhost/path/to/socket.sock\"", + "\n\t This option overrides any previous use of --proxy, as they are", + "\t mutually exclusive.", + "\n\t This option is superfluous since you can specify a socks4 proxy", + "\t with --proxy using a socks4:// protocol prefix.", + "\n\t --preproxy can be used to specify a SOCKS proxy at the same time", + "\t proxy is used with an HTTP/HTTPS proxy. In such a case, curl first", + "\t connects to the SOCKS proxy and then connects (through SOCKS) to", + "\t the HTTP or HTTPS proxy. If --socks4 is provided several times,", + "\t the last set value is used.", + "\n\t Example:", + "\t curl --socks4 hostname:4096 https://example.com", + "\n\t See also --socks4a, --socks5 and --socks5-hostname.", + "\n --socks4a ", + "\t Use the specified SOCKS4a proxy. If the port number is not", + "\t specified, it is assumed at port 1080. This asks the proxy to", + "\t resolve the hostname.", + "\n\t To specify proxy on a unix domain socket, use localhost for host,", + "\t e.g. \"socks4a://localhost/path/to/socket.sock\"", + "\n\t This option overrides any previous use of --proxy, as they are", + "\t mutually exclusive.", + "\n\t This option is superfluous since you can specify a socks4a proxy", + "\t with --proxy using a socks4a:// protocol prefix.", + "\n\t --preproxy can be used to specify a SOCKS proxy at the same time", + "\t --proxy is used with an HTTP/HTTPS proxy. In such a case, curl", + "\t first connects to the SOCKS proxy and then connects (through", + "\t SOCKS) to the HTTP or HTTPS proxy. If --socks4a is provided", + "\t several times, the last set value is used.", + "\n\t Example:", + "\t curl --socks4a hostname:4096 https://example.com", + "\n\t See also --socks4, --socks5 and --socks5-hostname.", + "\n --socks5 ", + "\t Use the specified SOCKS5 proxy - but resolve the hostname locally.", + "\t If the port number is not specified, it is assumed at port 1080.", + "\n\t To specify proxy on a unix domain socket, use localhost for host,", + "\t e.g. \"socks5://localhost/path/to/socket.sock\"", + "\n\t This option overrides any previous use of --proxy, as they are", + "\t mutually exclusive.", + "\n\t This option is superfluous since you can specify a socks5 proxy", + "\t with --proxy using a socks5:// protocol prefix.", + "\n\t --preproxy can be used to specify a SOCKS proxy at the same time", + "\t --proxy is used with an HTTP/HTTPS proxy. In such a case, curl", + "\t first connects to the SOCKS proxy and then connects (through", + "\t SOCKS) to the HTTP or HTTPS proxy.", + "\n\t This option does not work with FTPS or LDAP. If --socks5 is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --socks5 proxy.example:7000 https://example.com", + "\n\t See also --socks5-hostname and --socks4a.", + "\n --socks5-basic", + "\t Use username/password authentication when connecting to a SOCKS5", + "\t proxy. The username/password authentication is enabled by default.", + "\t Use --socks5-gssapi to force GSS-API authentication to SOCKS5", + "\t proxies. Providing --socks5-basic multiple times has no extra", + "\t effect.", + "\n\t Example:", + "\t curl --socks5-basic --socks5 hostname:4096 https://example.com", + "\n\t See also --socks5.", + "\n --socks5-gssapi", + "\t Use GSS-API authentication when connecting to a SOCKS5 proxy. The", + "\t GSS-API authentication is enabled by default (if curl is compiled", + "\t with GSS-API support). Use --socks5-basic to force", + "\t username/password authentication to SOCKS5 proxies. Providing", + "\t --socks5-gssapi multiple times has no extra effect. Disable it", + "\t again with --no-socks5-gssapi.", + "\n\t Example:", + "\t curl --socks5-gssapi --socks5 hostname:4096 https://example.com", + "\n\t See also --socks5.", + "\n --socks5-gssapi-nec", + "\t As part of the GSS-API negotiation a protection mode is", + "\t negotiated. RFC 1961 says in section 4.3/4.4 it should be", + "\t protected, but the NEC reference implementation does not. The", + "\t option --socks5-gssapi-nec allows the unprotected exchange of the", + "\t protection mode negotiation. Providing --socks5-gssapi-nec", + "\t multiple times has no extra effect. Disable it again with", + "\t --no-socks5-gssapi-nec.", + "\n\t Example:", + "\t curl --socks5-gssapi-nec --socks5 hostname:4096 https://example.com", + "\n\t See also --socks5.", + "\n --socks5-gssapi-service ", + "\t Set the service name for a socks server. Default is", + "\t rcmd/server-fqdn. If --socks5-gssapi-service is provided several", + "\t times, the last set value is used.", + "\n\t Example:", + "\t curl --socks5-gssapi-service sockd --socks5 hostname:4096 https://example.com", + "\n\t See also --socks5.", + "\n --socks5-hostname ", + "\t Use the specified SOCKS5 proxy (and let the proxy resolve the", + "\t hostname). If the port number is not specified, it is assumed at", + "\t port 1080.", + "\n\t To specify proxy on a unix domain socket, use localhost for host,", + "\t e.g. \"socks5h://localhost/path/to/socket.sock\"", + "\n\t This option overrides any previous use of --proxy, as they are", + "\t mutually exclusive.", + "\n\t This option is superfluous since you can specify a socks5 hostname", + "\t proxy with --proxy using a socks5h:// protocol prefix.", + "\n\t --preproxy can be used to specify a SOCKS proxy at the same time", + "\t --proxy is used with an HTTP/HTTPS proxy. In such a case, curl", + "\t first connects to the SOCKS proxy and then connects (through", + "\t SOCKS) to the HTTP or HTTPS proxy. If --socks5-hostname is", + "\t provided several times, the last set value is used.", + "\n\t Example:", + "\t curl --socks5-hostname proxy.example:7000 https://example.com", + "\n\t See also --socks5 and --socks4a.", + "\n -Y, --speed-limit ", + "\t If a transfer is slower than this set speed (in bytes per second)", + "\t for a given number of seconds, it gets aborted. The time period is", + "\t set with --speed-time and is 30 seconds by default. If", + "\t --speed-limit is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --speed-limit 300 --speed-time 10 https://example.com", + "\n\t See also --speed-time, --limit-rate and --max-time.", + "\n -y, --speed-time ", + "\t If a transfer runs slower than speed-limit bytes per second during", + "\t a speed-time period, the transfer is aborted. If speed-time is", + "\t used, the default speed-limit is 1 unless set with --speed-limit.", + "\n\t This option controls transfers (in both directions) but does not", + "\t affect slow connects etc. If this is a concern for you, try the", + "\t --connect-timeout option. If --speed-time is provided several", + "\t times, the last set value is used.", + "\n\t Example:", + "\t curl --speed-limit 300 --speed-time 10 https://example.com", + "\n\t See also --speed-limit and --limit-rate.", + "\n --ssl", + "\t (FTP IMAP POP3 SMTP LDAP) Warning: this is considered an insecure", + "\t option. Consider using --ssl-reqd instead to be sure curl upgrades", + "\t to a secure connection.", + "\n\t Try to use SSL/TLS for the connection - often referred to as", + "\t STARTTLS or STLS because of the involved commands. Reverts to a", + "\t non-secure connection if the server does not support SSL/TLS. See", + "\t also --ftp-ssl-control and --ssl-reqd for different levels of", + "\t encryption required.", + "\n\t This option is handled in LDAP (added in 7.81.0). It is fully", + "\t supported by the OpenLDAP backend and ignored by the generic ldap", + "\t backend.", + "\n\t Please note that a server may close the connection if the", + "\t negotiation does not succeed.", + "\n\t This option was formerly known as --ftp-ssl. That option name can", + "\t still be used but might be removed in a future version. Providing", + "\t --ssl multiple times has no extra effect. Disable it again with", + "\t --no-ssl.", + "\n\t Example:", + "\t curl --ssl pop3://example.com/", + "\n\t See also --ssl-reqd, --insecure and --ciphers.", + "\n --ssl-allow-beast", + "\t (TLS) Do not work around a security flaw in the TLS1.0 protocol", + "\t known as BEAST. If this option is not used, the TLS layer may use", + "\t workarounds known to cause interoperability problems with some", + "\t older server implementations.", + "\n\t This option only changes how curl does TLS 1.0 and has no effect", + "\t on later TLS versions.", + "\n\t WARNING: this option loosens the TLS security, and by using this", + "\t flag you ask for exactly that. Providing --ssl-allow-beast", + "\t multiple times has no extra effect. Disable it again with", + "\t --no-ssl-allow-beast.", + "\n\t Example:", + "\t curl --ssl-allow-beast https://example.com", + "\n\t See also --proxy-ssl-allow-beast and --insecure.", + "\n --ssl-auto-client-cert", + "\t (TLS) (Schannel) Automatically locate and use a client certificate", + "\t for authentication, when requested by the server. Since the server", + "\t can request any certificate that supports client authentication in", + "\t the OS certificate store it could be a privacy violation and", + "\t unexpected. Providing --ssl-auto-client-cert multiple times has no", + "\t extra effect. Disable it again with --no-ssl-auto-client-cert.", + "\n\t Example:", + "\t curl --ssl-auto-client-cert https://example.com", + "\n\t Added in 7.77.0. See also --proxy-ssl-auto-client-cert.", + "\n --ssl-no-revoke", + "\t (TLS) (Schannel) Disable certificate revocation checks. WARNING:", + "\t this option loosens the SSL security, and by using this flag you", + "\t ask for exactly that. Providing --ssl-no-revoke multiple times has", + "\t no extra effect. Disable it again with --no-ssl-no-revoke.", + "\n\t Example:", + "\t curl --ssl-no-revoke https://example.com", + "\n\t See also --crlfile.", + "\n --ssl-reqd", + "\t (FTP IMAP POP3 SMTP LDAP) Require SSL/TLS for the connection -", + "\t often referred to as STARTTLS or STLS because of the involved", + "\t commands. Terminates the connection if the transfer cannot be", + "\t upgraded to use SSL/TLS.", + "\n\t This option is handled in LDAP (added in 7.81.0). It is fully", + "\t supported by the OpenLDAP backend and rejected by the generic ldap", + "\t backend if explicit TLS is required.", + "\n\t This option is unnecessary if you use a URL scheme that in itself", + "\t implies immediate and implicit use of TLS, like for FTPS, IMAPS,", + "\t POP3S, SMTPS and LDAPS. Such a transfer always fails if the TLS", + "\t handshake does not work.", + "\n\t This option was formerly known as --ftp-ssl-reqd. Providing", + "\t --ssl-reqd multiple times has no extra effect. Disable it again", + "\t with --no-ssl-reqd.", + "\n\t Example:", + "\t curl --ssl-reqd ftp://example.com", + "\n\t See also --ssl and --insecure.", + "\n --ssl-revoke-best-effort", + "\t (TLS) (Schannel) Ignore certificate revocation checks when they", + "\t failed due to missing/offline distribution points for the", + "\t revocation check lists. Providing --ssl-revoke-best-effort", + "\t multiple times has no extra effect. Disable it again with", + "\t --no-ssl-revoke-best-effort.", + "\n\t Example:", + "\t curl --ssl-revoke-best-effort https://example.com", + "\n\t Added in 7.70.0. See also --crlfile and --insecure.", + "\n -2, --sslv2", + "\t (SSL) This option previously asked curl to use SSLv2, but is now", + "\t ignored (added in 7.77.0). SSLv2 is widely considered insecure", + "\t (see RFC 6176). Providing --sslv2 multiple times has no extra", + "\t effect.", + "\n\t Example:", + "\t curl --sslv2 https://example.com", + "\n\t --sslv2 requires that libcurl is built to support TLS. This option", + "\t is mutually exclusive with --sslv3, --tlsv1, --tlsv1.1 and", + "\t --tlsv1.2. See also --http1.1 and --http2.", + "\n -3, --sslv3", + "\t (SSL) This option previously asked curl to use SSLv3, but is now", + "\t ignored (added in 7.77.0). SSLv3 is widely considered insecure", + "\t (see RFC 7568). Providing --sslv3 multiple times has no extra", + "\t effect.", + "\n\t Example:", + "\t curl --sslv3 https://example.com", + "\n\t --sslv3 requires that libcurl is built to support TLS. This option", + "\t is mutually exclusive with --sslv2, --tlsv1, --tlsv1.1 and", + "\t --tlsv1.2. See also --http1.1 and --http2.", + "\n --stderr ", + "\t Redirect all writes to stderr to the specified file instead. If", + "\t the filename is a plain '-', it is instead written to stdout.", + "\n\t This option is global and does not need to be specified for each", + "\t use of --next. If --stderr is provided several times, the last set", + "\t value is used.", + "\n\t Example:", + "\t curl --stderr output.txt https://example.com", + "\n\t See also --verbose and --silent.", + "\n --styled-output", + "\t Enable automatic use of bold font styles when writing HTTP headers", + "\t to the terminal. Use --no-styled-output to switch them off.", + "\n\t Styled output requires a terminal that supports bold fonts. This", + "\t feature is not present on curl for Windows due to lack of this", + "\t capability.", + "\n\t This option is global and does not need to be specified for each", + "\t use of --next. Providing --styled-output multiple times has no", + "\t extra effect. Disable it again with --no-styled-output.", + "\n\t Example:", + "\t curl --styled-output -I https://example.com", + "\n\t Added in 7.61.0. See also --head and --verbose.", + "\n --suppress-connect-headers", + "\t When --proxytunnel is used and a CONNECT request is made do not", + "\t output proxy CONNECT response headers. This option is meant to be", + "\t used with --dump-header or --include which are used to show", + "\t protocol headers in the output. It has no effect on debug options", + "\t such as --verbose or --trace, or any statistics. Providing", + "\t --suppress-connect-headers multiple times has no extra effect.", + "\t Disable it again with --no-suppress-connect-headers.", + "\n\t Example:", + "\t curl --suppress-connect-headers --include -x proxy https://example.com", + "\n\t See also --dump-header, --include and --proxytunnel.", + "\n --tcp-fastopen", + "\t Enable use of TCP Fast Open (RFC 7413). TCP Fast Open is a TCP", + "\t extension that allows data to get sent earlier over the connection", + "\t (before the final handshake ACK) if the client and server have", + "\t been connected previously. Providing --tcp-fastopen multiple times", + "\t has no extra effect. Disable it again with --no-tcp-fastopen.", + "\n\t Example:", + "\t curl --tcp-fastopen https://example.com", + "\n\t See also --false-start.", + "\n --tcp-nodelay", + "\t Turn on the TCP_NODELAY option. See the curl_easy_setopt(3) man", + "\t page for details about this option.", + "\n\t curl sets this option by default and you need to explicitly switch", + "\t it off if you do not want it on. Providing --tcp-nodelay multiple", + "\t times has no extra effect. Disable it again with --no-tcp-nodelay.", + "\n\t Example:", + "\t curl --tcp-nodelay https://example.com", + "\n\t See also --no-buffer.", + "\n -t, --telnet-option ", + "\t Pass options to the telnet protocol. Supported options are:", + "\n\t TTYPE=", + "\n\t\tSets the terminal type.", + "\n\t XDISPLOC=", + "\n\t\tSets the X display location.", + "\n\t NEW_ENV=", + "\n\t\tSets an environment variable.", + "\n\t --telnet-option can be used several times in a command line", + "\n\t Example:", + "\t curl -t TTYPE=vt100 telnet://example.com/", + "\n\t See also --config.", + "\n --tftp-blksize ", + "\t (TFTP) Set the TFTP BLKSIZE option (must be 512 or larger). This", + "\t is the block size that curl tries to use when transferring data to", + "\t or from a TFTP server. By default 512 bytes are used. If", + "\t --tftp-blksize is provided several times, the last set value is", + "\t used.", + "\n\t Example:", + "\t curl --tftp-blksize 1024 tftp://example.com/file", + "\n\t See also --tftp-no-options.", + "\n --tftp-no-options", + "\t (TFTP) Do not to send TFTP options requests. This improves interop", + "\t with some legacy servers that do not acknowledge or properly", + "\t implement TFTP options. When this option is used --tftp-blksize is", + "\t ignored. Providing --tftp-no-options multiple times has no extra", + "\t effect. Disable it again with --no-tftp-no-options.", + "\n\t Example:", + "\t curl --tftp-no-options tftp://192.168.0.1/", + "\n\t See also --tftp-blksize.", + "\n -z, --time-cond